Monday, February 28, 2011

Umbraco Razor Feature Walkthrough – Part 3

Part 3 of the feature walkthrough continues with features available in the TAFKA 4.6.2 release.
Today, I'm covering locating ancestors, the XML helpers and Extension Methods and the XPath helper

RC1 is just around the corner and there's a bunch more features coming - i'll try and cover those too

AncestorOrSelf

The 4.6.1 version of Razor had an implementation of AncestorOrSelf that took a Func<DynamicNode,bool> as a predicate to decide when to stop ascending the node tree.
In 4.7, there's now a parameterless implementation that will climb to the top of the node tree and return the top most grandparent.

Here's an example:

var Node = @Model.NodeById(1640); var Home = Node.AncestorOrSelf(); @Home.Name; 

Ancestors

Sometimes you want to get a list of the parents walking up the tree.
For this, you can use .Ancestors on the node:

var Node = @Model.NodeById(1640); @foreach(var level in Node.Ancestors) { <li><a href="@level.Url">@level.Name</a></li> } 

Ancestors and AncestorOrSelf have been improved in RC1 - more on that in a later part!

Type Safety

In 4.6.1, when a property was returned by the @ syntax, the type was always string.
This meant that sometimes when you really wanted a boolean or integer, you had to do typecasting within your razor template.

Assuming the properties I'm using below exist on your Document types,
In 4.7, we've improved this so that you can simply go:

if(@Model.shouldBeVisible){ //Do something } //or if(@Model.catCount > 1) { //Do something } 

Warning: this change may break a few of your existing templates but was needed to support some more of the advanced features.
If you have if statements or similar that are checking against strings, they'll need to be refactored slightly.

//instead of: if(@Model.shouldBeVisible == "1") //use: if(@model.shouldBeVisible) 

Supported types are currently:
string, int, decimal, bool, xml (see below for xml)

XML Properties

If you have a field which contains valid XML, and wasn't generated from an RTE, DynamicNode will detect this and allow you to
access it with . notation
If there's more than one node with the same name, you'll need to use [] indexing to access the element you want

Here's an example:
@Model.xmlProperty.Catalog.Books[1].Genre

Warning: the property access is case sensitive. Make sure you check your case.
The root node is removed, so omit that, and remember to check for indexing if you have multiple nodes that might be returned.

If the XML function is kicking in, your root node may need to be excluded - check umbracoSettings.config for an override of document element types that shouldn't be converted

Custom Extension Methods

When working with DynamicNodeList, sometimes you need some functionality that we haven't provided.
We've added support for calling your own Mix-Ins against the DynamicNodeList type.
Here's an example that we've been using for testing:
This random method has been included in the core distribution for 4.7 but we've included it below so that you can see how to write one

using System; using System.Collections.Generic; using System.Linq; using System.Text; using umbraco.MacroEngines; namespace SniperSystems.Umbraco.Razor.Extensions { public static class RazorExtensions { public static DynamicNodeList Random(this DynamicNodeList all, int Min, int Max) { //get a random number generator Random r = new Random(); //choose the number of elements to be returned between Min and Max int Number = r.Next(Min, Max); //Call the other method return Random(all, Number); } public static DynamicNodeList Random(this DynamicNodeList all, int Max) { //Randomly order the items in the set by a Guid, Take the correct number, and return this wrapped in a new DynamicNodeList return new DynamicNodeList(all.Items.OrderBy(x => Guid.NewGuid()).Take(Max)); } } } 

Put this in a new class library and compile it.
Drop the DLL in your bin folder, and now you can do this:

@ForEach(@Model.Children.Random(8)) { //Working with 8 randomly selected Children } 

Unlike the Alliance's way of implementing Random, there's no chance of a single node coming back more than once in your random selection - Unique nodes only!

There are 3 overloads to Random in the 4.7 distribution:

Random() will return a single randomly selected node from the collection
Random(int number) will return N randomly selected nodes from the collection
Random(int min, int max) will return between min and max randomly selected nodes from the collection

XPath helper

Sometimes .Where isn't enough to select the node you want - it only works on the current node, and you can't easily check
Parents/Children
We've added a .XPath helper which lets you use XPath to select the nodes.
The XPath you use is used to select nodes from the original XML which are then upgraded to NodeFactory.Node and then
DynamicNode

@foreach(var item in @Model.XPath("//ChildItem[catCount > 4 and count(.//catPictures) > 0]").Random(4)) { @item.Name<br/> } 

The .XPath helper also works on a DynamicXml item (from an XML property) but won't do the upgrade to DynamicNode/DynamicNodeList like when you call it directly on @Model

We're really very sorry we had to provide something that was similar to XSLT, but it does add a lot of functionality :)

I'm Gareth Evans, Follow me at @agrath on twitter, and here's a few links:
The new Razor forum on our.umbraco: https://our.umbraco.com/forum/developers/razor
Github for any feature requests or bugs: https://github.com/umbraco/Umbraco-CMS

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

Are you sure, that's your real e-mail?