Wednesday, February 23, 2011

Umbraco 4.7 Razor Feature Walkthrough – Part 1

Introduction

The new razor support for umbraco is great, though the initial release bundled with 4.6.1 was really just a preview. We've barely scratched the surface of what can be achieved by combining the power of umbraco with the simplicity of the Razor template syntax.

This post, which will be one of a series of posts over the next few days, introduces myself as well as a few of the new MacroEngine features in 4.7's implementation of Razor.

These posts will be slightly technical, if there's anything not in DynamicNode that you find yourself really needing, Drop a codeplex request up and i'll try and keep up!

About Me & Sniper Systems Ltd

My name is Gareth Evans, and I own a small business called Sniper Systems in Auckland, New Zealand.
We've been working with Umbraco in a user capacity for about 4 years having built about 50-60 sites.
We run our own managed hosting where we host umbraco, as well as custom developed ASP.Net webform applications.

I'm part of the Australasian team mentioned in the previous blog post that has worked on the Razor updates in 4.7.
That said, due to the well documented rivalry between Australia and New Zealand, I will be quick to mention that I'm from New Zealand which is not part of, or in any way (apart from both loving umbraco) associated with Australia.

I'm quite fluent with XSLT but I found the razor syntax to be much more readable, and my employees with no prior XSLT experience struggled to learn how to write xpath and use XSLT to transform documents.
With the new release of Razor, we tried to build an entire site using it, avoiding XSLT entirely and ran into some problems.
I made the decision to contribute back to Umbraco and implement the features in Razor that I wanted.

We're available for hire to work on umbraco sites, our website can be found at http://snipersystems.co.nz or I can be reached on twitter under the username @agrath

So with that out of the way, on with the rest of the fun!

Why the new functionality?

For a recent website built by Sniper, we made the decision to try and build it entirely using Razor and ran up against a few problems.
While the code read nicely for simple iterations and property retrieval, we quickly ran up against some issues with types while trying to manipulate sets.

The core of the model used for the Umbraco razor engine is based around a .net 4 class called DynamicObject.
DynamicObject allows you to define getters (and setters) that take the place of real properties on an object.
When the runtime goes to fetch a property referenced by the .Syntax, it instead invokes the correct method on the type which returns the property or invokes the method.
Unfortunately, when it does this, the return type is the base type object - this means that you can't chain method calls without typecasting.

An example of this is calling @Model.Children - this returned (in 4.6.1) an IEnumerable<DynamicNode> but this was boxed in an object.
In order to then call .Count on that instance (to get the number of Children), you had to cast the object back to IEnumerable<DynamicNode>.
This proved to be more trouble than it's worth and completely ruined the readability of the Razor template:
Due to a compilation issue, you can't add a @using for umbraco.MacroEngines, so this is what you were left with to get the Count of Children

@(((IEnumerable<umbraco.MacroEngines.DynamicNode>)(@Model.Children)).Count()); 


Any other syntax results in not being able to find the Count() method on object

IEnumerable handling, we can do better

In order to solve this, we created a new DynamicObject type called DynamicNodeList - this type is used whenever anything in the Razor engine for umbraco returns a list of DynamicNodes
This includes .Children and the filtered version .nodeTypeAlias (e.g. .Pictures if you have children of nodeTypeAlias Picture)

In 4.7 you can do this:

@Model.Children.Count()
//or
@Model.Pictures.Count() //assuming you have children of type Picture

Because DynamicNodeList inherits from DynamicObject, the Razor engine knows that Count() needs to be invoked via the Dynamic methods and the resulting value is returned

Simplistic IEnumerable method support

Taking this new DynamicNodelist a bit further, we now have support for all of the standard IEnumerable methods. They haven't been reimplemented, they're proxied by DynamicNodeList onto the original (inner) IEnumerable<DynamicNode>.

By the way, if you have a DynamicNodeList and for some reason you want to get the internal list, it's public so you can do this:

var list = @Model.Children; list.Items // => IEnumerable<DynamicNode> 

This syntax is intended to be used when you're working with DynamicNodeList from inside your own methods, but more on that later.

Here's a few examples of what you can now do with a DynamicNodeList from a @Model method:

//.Take (get the first X items of the list) @Model.Children.Take(2) //=> the first two items from the list //.Skip @Model.Children.Skip(2) //=> the entire list excluding the first two items from the list //.ElementAt @Model.Children.ElementAt(1) //=> The second element in the children list. Will return null if there aren't that many children //.Count @Model.Children.Count() //=> The number of children //Chaining //You can chain these methods: @Model.Children.Skip(2).Take(2) //=> the 3rd and 4th children @Model.Children.Skip(2).Take(2).Count() //=> 2 

Okay, so that concludes part 1, I'll post another part continuing on from this introductory post in a day or two.

Follow me at @agrath on twitter, and here's a few links:
The new Razor forum on our.umbraco
Github for any feature requests or bugs

Read more from the Umbraco Razor walkthrough series

Umbraco Razor Feature Walkthrough - Part 1
Umbraco Razor Feature Walkthrough - Part 2
Umbraco Razor Feature Walkthrough - Part 3
Umbraco Razor Feature Walkthrough - Part 4
Umbraco Razor Feature Walkthrough - Part 5
Umbraco Razor Feature Walkthrough - Part 6
Umbraco Razor Feature Walkthrough - Part 7
Umbraco Razor Feature Walkthrough - Part 8

Want to be updated on everything Umbraco?

Sign up for the Umbraco newsletter and get the latest news and special offers send directly to your inbox