Using helpers in Tridion Razor templating
Today, for the first time, I used a helper in a Razor Tridion template. I'd made a fairly standard 'generic link' embedded schema, so that I could combine the possibility of a component link and an external link in a link list, and allow for custom link text. (Nothing to see here, move along now please.) However, when I came to template the output, I wanted to have a function that would process an individual link. A feature of Razor templating is that you can define a @helper, which is a bit like a function, except that instead of a return value, the body is an exemplar of the required output. There is also support for functions, so to lift Alex Klock's own example:
@functions {
public string HelloWorld(string name) {
return "Hello " + name;
}
}
and
@helper HelloWorld(string name) {
<div>Hello <em>@name</em>!</div>
}
will serve fairly similar purposes.
What I wanted to do today, however was slightly different; I didn't want to pass in a string, but a reference to my embedded field. All the examples on the web so far are about strings, and getting the types right proved interesting. I started out with some code like this:
@foreach(var link in @Fields.links){
@RenderLink(link);
}
So I needed a helper called RenderLink (OK - this might be a very trivial use-case, but a real problem all the same.). But what was the type of the argument? In theory, "links" is an EmbeddedSchemaField (or to give it it's full Sunday name: Tridion.ContentManager.ContentManagement.Fields.EmbeddedSchemaField) but what you get in practice is an object of type "Tridion.Extensions.Mediators.Razor.Models.DynamicItemFields". I'd already guessed this by poking around in the Razor Mediator sources, but after a few of my first experiments went astray, I ended up confirming that with @link.GetType().FullName
Well I tried writing a helper like this:
@using Tridion.Extensions.Mediators.Razor.Models
@helper RenderLink(DynamicItemFields link){
... implementation
}
but that didn't work, because when you try to call the methods on 'link' they don't exist.
And then, just for fun, of course, I tried
@using Tridion.ContentManager.ContentManagement.Fields
@helper RenderLink(EmbeddedSchemaField link){
... implementation
}
but that was just going off in an even worse direction. Yeah, sure, that type would have had the methods, but what I actually had hold of was a DynamicItemFields. Eventually, I remembered some hints in the mediator's documentation and tried using the 'dynamic' keyword. This, it turns out, is what you need. The 'dynamic' type lets you invoke methods at run-time without the compiler needing to know about them. (At last, I was starting to understand some of the details of the mediator's implementation!)
@helper RenderLink(dynamic link){
... implementation
}
This may be obvious with hindsight (as the old engineers' joke has it ... for some value of 'obvious') . For now, I'm writing another blog post tagged #babysteps and #notetoself, and enjoying my tendency to take the road less travelled.
| TWO roads diverged in a yellow wood, | |
| And sorry I could not travel both | |
| And be one traveler, long I stood | |
|
And looked down one as far as I could |
|
| To where it bent in the undergrowth; | |
| Then took the other, as just as fair, | |
| And having perhaps the better claim, | |
|
Because it was grassy and wanted wear; |
|
| Though as for that the passing there | |
| Had worn them really about the same, | |
| And both that morning equally lay | |
|
In leaves no step had trodden black. |
|
| Oh, I kept the first for another day! | |
| Yet knowing how way leads on to way, | |
| I doubted if I should ever come back. | |
|
I shall be telling this with a sigh |
|
| Somewhere ages and ages hence: | |
| Two roads diverged in a wood, and I— | |
| I took the one less traveled by, | |
| And that has made all the difference. |
-- Robert Frost