Saturday, February 8, 2014

Inline Razor Templates

Haven't heard of the System.Web.WebPages.HelperResult? Maybe you have, but you really don't know what it is. Don't worry, you're not alone.

So what is it? 

Simply put it is the key to processing inline Razor templates. What that means is you can put parsed and fully intellisensed blocks of Razor (html) markup directly in your HtmlHelper extension methods, view model constructors, and even view model properties. These can then be read, processed, and rendered seamlessly by your code without any tedious processing or setup.

Look at this sample HtmlHelper extension method which utilizes an inline Razor template:

  1. <div id="my-content">
  2.     @Html.DoSomething(
  3.         @<div>
  4.             Here is some <em>inline</em> html content.
  5.         </div>)    
  6. </div>
The #my-content content shown here is actually passed to the DoSomething method which can then be processed and rendered to an MvcHtmlString and then rendered to your final web page.



So WHY would I want that?
Let's look at a very common approach to Razor view development in the wild today:

  1. <div>
  2.     La la la<br />
  3.     I'm a block of happy HTML...<br />
  4.     But wait...<br />
  5.     What's that inside me???<br />
  6.     
  7.     @Html.MethodOfUnknownHtmlGeneration("guess-what-i-am"new Dictionary<stringobject> {
  8.         {"and-what-i-am""or-what-i-do"},
  9.         {"I-could-mean-anything""or-render-anything"}
  10.     })
  11.     
  12.     <strong>OMG!!!! HALP!!!</strong>
  13. </div>
MAYBE if I were the person who wrote this HtmlHelper extension method I would know what kind of chaos this would put on my final web page when it is rendered. OR maybe I work on a team and one of my co-workers wrote this. What then? I have to try to find it used in the application somewhere in order to know how it should be wired up. Or I might even have to find the code for this method to try and figure out what it does and what kind of parameter inputs it expects.

We could even think a year ahead when I have to come back to this application and troubleshoot something that isn't working on the page. Maybe that something is a piece of javascript functionality that interfaces with this mysteriously generated HTML markup. I run a search on my code to find where that HTML is at, but it doesn't show up anywhere!

What if I'm not even a developer on the team - what if I'm a designer-css guy? Or what if I am the javascript guy that doesn't know or work with .NET?



**The Situation**

Let's say I have a common formatting for popover content that will "hover" over its surrounding content. Basically everywhere I want a floating notation or tooltip I need to reproduce this structure and leave it to the CSS and javascript to do the rest.

Assuming my team are Bootstrap fans my markup might look something like this:

  1. <div class="popover right">
  2.     <div class="arrow"></div>
  3.     <h3 class="popover-title">My Popover Title</h3>
  4.     <div class="popover-content">
  5.         <strong>This is my popover content:</strong><br />
  6.         <p>
  7.             I need to be able to format this content
  8.             and somehow get it appropriately wrapped
  9.             with all this Bootstrap goodness...
  10.         </p>
  11.     </div>
  12. </div>

**The Problem**

As it turns out I have to reproduce this structure a few hundred times in the application. What I don't want to do is have to remember this structure or continually reproduce this part of the formatting:

  1. <div class="popover right">
  2.     <div class="arrow"></div>
  3.     <h3 class="popover-title"></h3>
  4.     <div class="popover-content">
  5.     </div>
  6. </div>
So I need a solution that will somehow produce this structure for me, yet allow me to insert my content into the structure appropriately.
As a typical .NET developer my first thought might be something like this...

  1.         public static MvcHtmlString Popover(this HtmlHelper helper, string title, string content)
  2.         {
  3.             var markup = string.Format(@"
  4.                 <div class=""popover-content"">
  5.                     <div class=""arrow""></div>
  6.                     <h3 class=""popover-title"">{0}</h3>
  7.                     <div class=""popover-content"">{1}</div>
  8.                 </div>", title, content);
  9.             return new MvcHtmlString(markup);
  10.         }
Which would look like this in my markup:

  1. @Html.Popover("My Popover Title""Here is some popover content")
But for reasons previously stated I don't want this markup hidden in a formatted string somewhere. Fortunately, as a typical MVC developer, I have learned that I can simply just make a partial which takes a view model. This can either be rendered using the existing .Partial() HtmlHelper extension method or called from a custom method.

The ViewModel:

  1.     public class PopoverViewModel
  2.     {
  3.         public string Title { getset; }
  4.         public string Content { getset; }
  5.     }
The View:

  1. @model MyApplication.Models.PopoverViewModel
  2. <div class="popover-content">
  3.     <div class="arrow"></div>
  4.     <h3 class="popover-title">
  5.         @Model.Title
  6.     </h3>
  7.     <div class="popover-content">
  8.         @Model.Content
  9.     </div>
  10. </div>
The Usage:

  1. @Html.Partial("_Popover"new PopoverViewModel {
  2.     Title = "This is my title",
  3.     Content = "This is my content"
  4. })
But as shown in my original markup, the content needs to be formatted. I need to, without some ridiculous string concatenation, get HTML formatting in there. This is difficult to do when only being able to pass a set of plain ol' strings into a partial.

**The Solution**

This is where inline Razor templates shine. First let's change the view model a bit:

  1.     public class PopoverViewModel
  2.     {
  3.         public string Title { getset; }
  4.         public Func<objectHelperResult> Content { getset; }
  5.         public MvcHtmlString RenderContent()
  6.         {
  7.             // Render the template
  8.             var rawHtml = Content(null).ToHtmlString();
  9.             // Send it to the view
  10.             return new MvcHtmlString(rawHtml);
  11.         }
  12.     }
Next let's update the partial view:

  1. @model MyApplication.Models.PopoverViewModel
  2. <div class="popover-content">
  3.     <div class="arrow"></div>
  4.     <h3 class="popover-title">
  5.         @Model.Title
  6.     </h3>
  7.     <div class="popover-content">
  8.         @Model.RenderContent()
  9.     </div>
  10. </div>
Now we can update our usage:

  1. @Html.Partial("_Popover"new PopoverViewModel {
  2.     Title = "This is my title",
  3.     Content = @<div>
  4.                    <strong>This is my popover content:</strong><br />
  5.                    <p>
  6.                        I need to be able to format this content
  7.                        and somehow get it appropriately wrapped
  8.                        with all this Bootstrap goodness...
  9.                    </p>
  10.                </div>
  11. })
The div with its content will be passed to the Content property of the view model. The signature Razor will give you when it sees an inline template is a Func<object, HelperResult>. To render the template into a plain string containing the HTML you only have to call the delegate to obtain the HelperResult and then invoke the .ToHtmlString() method off of the result.

There are dozens of situations this pattern can be applied to: auto-localization of view content and java interaction wireups to name a couple, but the inline template still has a couple more secrets to reveal...



So the first thing you'll probably notice about Razor inline templates is that horrible Func<object, HelperResult> signature. The template is not actually a Func, it's just a delegate which happens to be mappable to a Func. It should go without saying that this is not a very friendly signature and so reduces maintainability as it is not immediately evident what exactly a Func<object, HelperResult> is supposed to be. A year after writing some code with this signature you might have to do some searching to remember what is expected here.

The cool thing about this being a delegate is that it can be mapped to any defined delegate with a matching signature. This means I can make this look however I want with virtually a single line of code. Try adding the following as a .cs file to your project:

  1. namespace System.Web.WebPages
  2. {
  3.     public delegate HelperResult RazorTemplate(object item);
  4.     public delegate HelperResult RazorTemplate<T>(T item);
  5. }
Instead of a Func<object, HelperResult> signature I can use one of these delegate signatures.
Now my intellisense will look something like this:








Did you notice the "item" parameter in the signature? Like the "Model" reference in your regular Razor views, you will have access to the parameter passed to the template using "@item".

  1. @Html.MyHelperMethod(@<div>
  2.                           Here is the value of item: "@item"
  3.                       </div>)
This will be most useful when building a helper method or template to support the display of an item collection. Let's say I want to make a custom combo-box control with templated select-items:

The ViewModel:

  1.     public class ComboBoxViewModel
  2.     {
  3.         public string Name { getset; }
  4.         public List<object> Items { getset; }
  5.         public RazorTemplate ItemTemplate { getset; }
  6.         public MvcHtmlString RenderItem(object item)
  7.         {
  8.             return new MvcHtmlString(ItemTemplate(item).ToHtmlString());
  9.         }
  10.     }
The View:

  1. @model MyApplication.Models.ComboBoxViewModel
  2. <div class="combo-box">
  3.     <input type="hidden" name="@Model.Name"/>
  4.     <div class="selected-item"></div>
  5.     <ul class="item-menu">
  6.         @foreach (var item in Model.Items) {
  7.             <li>
  8.                 @Model.RenderItem(item)
  9.             </li>
  10.         }
  11.     </ul>
  12. </div>
The Usage:

  1. @Html.Partial("_ComboBox"new ComboBoxViewModel {
  2.     Name = "SelectedValue",
  3.     Items = new List<object>{"Item 1""Item 2""Item 3"},
  4.     ItemTemplate = @<strong><em>@item</em></strong>
  5. })
NOTE: By default the parameter passed into an inline Razor template will always be named item. Changing the name of the parameter in the signature will have no effect on the value's name in the template. It should also be noted that because the parameter in the template is inline to your regular view, any variable generated in your view with the name item may cause warnings or errors and conflict with your template.


It should also probably be mentioned here that an inline Razor template can not support additional inline templates. For example if I were to use the Popover template from earlier in this post and try to render a custom combo-box control inside it, I would get a compile error because the "ItemTemplate" property could not be set inside the Popover template.

There is, however, a reasonable workaround for this limitation: Razor helper methods can be written outside of the inline template and then called from inside it. For example:

  1. @Html.Partial("_Popover"new PopoverViewModel {
  2.     Content = @<div>
  3.                    Popover content with a templated
  4.                    ComboBox inside of it!
  5.                    @RenderComboBox("value"new List<object>{"Item 1""Item 2"})
  6.                </div>
  7. })
  8. @helper RenderComboBox(string name, List<object> items) {
  9.     @Html.Partial("_ComboBox"new ComboBoxViewModel {
  10.         Name = name,
  11.         Items = items,
  12.         ItemTemplate = @<strong>@item</strong>
  13.     })
  14. }





Did you like this post?
Let me know: http://markonthenet.com/contact.htm