Thursday, September 15, 2011

Umbraco Razor Feature Walkthrough–Part 6

Part 6 of the feature walkthrough is the first post for the features available in umbraco 4.7.1 but continues from the previous series.
Today, I'm covering the first set of changes added after the 4.7 release.
I originally wrote these blog posts on my way back from CodeGarden (you can do a lot of blogging with 26 hours of flights) however due to the release getting delayed.. a little.. I'm only just posting them now.

CodeGarden 2011
It was great to meet some of the Razor users at CodeGarden 2011, and hopefully I managed to convert at least 1 member of the XSLT rebel alliance to try Razor.
From talking to a few of the existing razor users, I realised that not everyone may be aware of the work done on DynamicNodeList to enable simple filtering of the list.

I often saw code that looked like this:

@using umbraco.MacroEngines; <ul> @{ DynamicNode n = @Model; List<DynamicNode> nl = @Model.Children.Items; foreach(dynamic item in nl.Where(x => x.GetProperty("umbracoNaviHide").Value != "1")) { <li>@item.Name</li> } } </ul> 

While this code is not wrong, and in fact is a supported way of interacting with your object model from Razor, it's probably not the easiest.
I covered this in an earlier blog post but I thought I'd just reiterate it here.
For simple filtering and sorting operations, you don't need to access the inner .Items [List<DynamicNode>] of the DynamicNode list, DynamicNodeList has ordering and filtering built into it that WILL work with Dynamic objects, no more dynamically dispatched or cannot use a lambda with a dynamic object errors.

You only need to do this if you really really want to use standard lambda syntax, or perhaps if you wanted to do a multi-line predicate or dispatch it to a Business Object model.

This code will work in place:

<ul> @foreach(var item in @Model.Children.Where("!umbracoNaviHide")) { <li>@item.Name</li> } </ul> 

DynamicXml fixes and changes
As mentioned in part 3 DynamicXml provides dot notation access to XML documents stored in a node property.
Following up on some feedback on both the forums and codeplex, some intended behaviour wasn't working quite right. DynamicXml was meant to reduce a single node (that is, if you selected down the chain and reached a bottom-most node) to a string.
Unfortunately, attributes weren't considered so this blocked access to those nodes.
In 4.7.1, the deepest nodes are considered to be nodes that do not have attributes or children. These will automatically convert to strings.

Another change to DynamicXml is that when dealing with xml files that may have variable amounts of children, the behaviour of the dot syntax was inconsistent. Sometimes you needed to use [] indexing, and other times not. We've improved this to make it more logical.

The final fix/change to DynamicXml is to fix a oversight with XML nodes containing - in attribute and element names.
The razor parser treats - as a subtraction within the template parsing. To resolve this, if your XML contains - in an element name or attribute name, it is stripped out.

<!--This--> <somerandomxml> <node attribute-name="adkda" nondashedattribute="akgdj-324kad"> </node> <node-with-dashes attribute-with-dashes="dash-ed"> <othernode>for-example<othernode> </node-with-dashes> </somerandomxml> <!--Will become this:--> <somerandomxml> <node attributename="adkda" nondashedattribute="akgdj-324kad"> </node> <nodewithdashes attributewithdashes="dash-ed"> <othernode>for-example<othernode> </nodewithdashes> </somerandomxml> 

As an aside, did you know that you can use . notation to access an attribute if you have that node?

//Given the following XML contained in a Books document type property /* <Books> <Book genre="action">Exploring Razor</Book> <Book genre="drama">The death of XSLT</Book> </Books> */ @Model.Books.Book[0].genre => "action" 

GetProperty & GetPropertyRecursive

One of the biggest complaints on the forums was accessing recursive data in Razor was difficult. I thought that could be improved, so we've added some new methods to Model [DynamicNode]
These methods deal with accessing string properties in a non dynamic way. That is to say, the property values will not be up-cast to their underlying type.
The advantage to these methods over the dot notation access is that we can add recursion and fallback as well.

There are a few overloads available:

//The most simplistic example, will retrieve a property by alias and return it as the interface IProperty which contains a Name, Value and Version public IProperty GetProperty(string alias); //The same as the previous example, except will recurse up the tree until it finds the first occurance of the alias you asked for public IProperty GetProperty(string alias, bool recursive); //The same as the first example, except only returns the value as a string public string GetPropertyValue(string alias); //The same as the previous example, but if the alias was not found, use the fallback string instead public string GetPropertyValue(string alias, string fallback); //Find the property value with the specified alias. If not found on the current node, check the parent. Rinse. Repeat. public string GetPropertyValue(string alias, bool recursive); //The same as the previous, but if after checking all parents, and not found, use the fallback string. public string GetPropertyValue(string alias, bool recursive, string fallback); 
GetPropertyRecursive has a special shorthand helper: 
Access your property prefixed with _ [this was the only available legal c# character I could use:
Model._pageTitle will fetch the page title of the current page walking recursively up the tree.

New methods on DynamicNode

HasProperty
This method is used like this and is fairly self explanatory: @Model.HasProperty("picture") => true|false

Index (or Position)
When a node is part of a list, returns the node's position within that list. If the node is not part of a list, return's it's position within it's parent's children [it's siblings]

<ul> @foreach(var item in @Model.Children) { <li><em>@item.Index()</em>&nbsp;@item.Name</li> } </ul> 


IsNull and HasValue

Will check a given property name to see if it IsNull (==null) or has no Value (Whitespace or Empty or Null)

@Model.HasValue("property") => true|false @Model.IsNull("property") => true|false 

Pluck | Select

This new method (Pluck, aliased as Select) is available on a DynamicNodeList and will allow you to extract a value from a set and return it as a list.
The use case for this was alluded to in a previous blog post, but the idea is that since you can pass a set of values into a .Where, you may need a way to get that set.

Pluck works like this:

@Model.Children.Pluck("colour") => new List<string>() //red //blue //green //pink //orange... 


Conclusion

I've got quite a few more blog posts planned for the coming few days, introducing all the new features that are in umbraco 4.7.1

I'm Gareth Evans, 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