Working with ASP.NET MVC 3 Razor Helpers and Templates
Shape up your views using template-based HTML helpers in Razor
July 12, 2011
ASP.NET MVC3 includes Razor, a new markup language to define the template of views. Razor stands side by side with the old-fashioned ASPX markup language, which was at the foundation of the ASP.NET MVC view engine in earlier versions of ASP.NET MVC.
You may not like or use Razor, and you may not agree that the resulting syntax is terser and cleaner. However, Razor is just one more option that developers can use, and a pretty nice one, at that. There are lots of places where you can learn more about the syntax of Razor. A good place to start is in the ASP.NET Razor Pages section on the ASP.NET website. This site also contains a link for Visual Basic developers. To read about Razor in a broader perspective, visit the DevProConnections website and search for articles published earlier this year.
More often than not, ASP.NET pages are made up of multiple views such as user controls, server controls, and plain HTML templates. How would you create these kinds of reusable markup chunks in an ASP.NET MVC application? Server controls are no longer a viable option, and HTML helpers never captured developers' hearts because of their code-only approach. Razor supports an alternative model for creating HTML helpers by using a mix of code and markup, which combines the power of programmability with the flexibility of templates. Although some (justifiably) criticize Razor's readability and terse syntax, this alternative model is a strong argument in favor of shifting to Razor.
Code-Only HTML Helpers
In ASP.NET MVC, server controls are deprecated and are usually impossible to use. Although you can use some server controls in some simple scenarios, they are not a practical solution. It is better to use HTML helpers.
As the name suggests, an HTML helper is a plain HTML factory. It comes in the form of an extension method for the HtmlHelper class or the AjaxHelper class. ASP.NET MVC has a stock of predefined helpers, and it lets you easily write your own helpers. An HTML helper is just a productivity tool that makes it easier to manage the generation of data-driven HTML.
An HTML helper allows full programmability, but because it is a plain method, it is inherently code-only. Everything that you want to happen must be expressly written in code—no templates and no HTML markup. This approach is not much different from that of classic server controls, but it lacks the design-time capabilities of advanced server controls and the large-scale marketplace of components.
You can also use ASCX user controls to compose ASP.NET MVC views. User controls are reusable pieces of markup that are also programmable. However, to make them programmable, you must create a code-behind class or incorporate a server-side script block. This is doable, but it's not totally aligned with the ASP.NET MVC programming model. For this reason, developers don't favor ASCX user controls.
Razor HTML Helpers
In Razor, you can easily reference any existing (and code-only) HTML helpers. You can also create declarative HTML helpers that result from a powerful combination of code and markup. In this way, you achieve the same flexibility of user controls that have been provided for years by ASP.NET Web Forms. Let's see what it takes to create such Razor HTML helpers.
You start by creating a .cshtml (or .vbhtml) file in the App_Code folder of the project. The project wizard doesn't create the App_Code folder automatically, so you have to do it yourself. Curiously, the folder is not listed as an ASP.NET folder, but it is promptly recognized as a special folder when you create it. It's important to note that your helpers are detected only if you put them in the App_Code folder.
The name you choose for the .cshtml (or .vbhtml) file is important because you'll be using this name when you make calls to the helper. The file is a kind of repository for your HTML helpers. The name of the file is not the name of the helper. A classic name for this file is MyHelpers.cshtml. Figure 1 shows a sample repository that has two HTML helpers.
The @helper keyword is a specific Razor keyword that begins the HTML helper declaration. The keyword is followed by the signature and implementation of the method. The body of the helper is just an embedded fragment of a Razor template, and it contains markup and code nuggets, which are a special type of code block. To invoke a declarative Razor helper, use the following code:
@MyHelpers.CustomerGrid(Model)
Figure 2 shows a complete example of an HTML helper written with and for Razor. The helper contains a bit of logic aimed at processing input values, and it expresses the layout by using direct HTML markup instead of resorting to string or tag builders. This markup can be intertwined with Razor code nuggets of any complexity.
Razor HTML helpers provide a flexible and simple way to define reusable components in ASP.NET MVC applications where the graphical layout can be built with markup instead of code alone. Just use them and enjoy.
Introducing Templated Delegates
A templated delegate is a Razor @ expression that includes code and markup. Implemented as a delegate, such an expression can be passed around as an argument. Suppose you have a HTML helper that displays some data it receives from callers, as represented in the following code:
@helper MessageBox(String message){
@message
}
Now suppose that the message parameter contains some HTML formatting. The following code produces the content that you see in Figure 3:
@{ var message = "DevProConnections is cool"; var formattedMessage = "" + message + "";}@MyHelpers.MessageBox(formattedMessage)
Figure 3: Automatic HTML encoding of Razor @ expressions
Because any markup resulting from an @ expression is automatically HTML-encoded, it comes as no surprise that the following markup is sent to the browser:
DevProConnections is cool
There are two ways to resolve this issue. If your sole concern is outputting formatted text, you can resort to Html.Raw and rewrite the helper as follows:
@helper MessageBox(String message){
@Html.Raw(message)
}
Internally, Html.Raw just wraps any text you provide in a newly created instance of the HtmlString class. ASP.NET recognizes this object as having already been encoded, and it doesn't further encode it. However, if you run the preceding code, you receive a null exception. This is because Html is always null in a Razor declarative @helper method. This is a known issue in ASP.NET MVC 3 and Razor, which, unfortunately, cannot be easily fixed. It is more of an architectural problem than an implementation issue. However, you can introduce two small changes to make it work correctly. First, make the following code available to your project:
public static class MvcIntrinsics{ public static System.Web.Mvc.HtmlHelper Html { get {return ((System.Web.Mvc.WebViewPage)WebPageContext.Current.Page).Html;} }}
Then, replace the original code with the following:
@helper MessageBox(String message){
@MvcIntrinsics.Html.Raw(message)
}
The other way to display formatted text is by using templated delegates. Templated delegates go beyond formatting text, they allow code wrapping and markup in a single object that you can pass around your classes as a native type.
Templated Delegates in Action
A templated delegate is just a delegate, and it is defined as follows:
Func
TResult must be the HelperResult type or any derived type. HelperResult is the wrapper type Razor uses to render any @ expressions. However, Type T can be anything you want, including Object and dynamic. To rewrite the HTML helper to make it accept templates, use the following sample code:
@helper ShowTemplatedMessage(Func funcTemplate, String message){
@funcTemplate(message)
}
And to arrange the call to such a template from a Razor view, use the following sample code:
@MyHelpers.ShowTemplatedMessage(@@item, @message)
To define a templated delegate, use the @markup syntax to add any markup you want after the initial @ symbol. This markup can contain embedded @ expressions that refer to variables and functions.
You should note that @item from the previous code example has a special meaning. It is used to refer to the only parameter that will be passed to the template. That is, the @item refers to the type T value that you're passing to the template.
Templated Delegates and Optional Sections
You can use templated delegates to more elegantly give default content to optional layout sections. In Razor, the term "layout" indicates a master view. You define the layout as a Razor template that is placed in the Shared folder. By default, an ASP.NET MVC project includes a _ViewStart template that sets the common master to _Layout.cshtml, which is located in the Views/Shared folder. If you want to give your views a specific layout, set the Layout property programmatically to point to a given file in your space, as follows:
@{ Layout = "...";}
In a layout, you can define a variety of sections. A section is similar to a content placeholder in ASPX master pages. Unlike a placeholder, a section can't have default content. When you write the layout, you can specify whether the section is required or optional, but you can't easily indicate a default markup. In a layout, you could use the following code:
@if(IsSectionDefined("Copyright")) { @RenderSection("copyright") } else { Rights reserved for a better use. }
However, templated delegates allow you to write this code in a more compact and elegant way. You can write an extension method for WebViewPage that adds another overload to RenderSection. The new overload takes a templated delegate as an argument, as shown in Figure 4.
The code for the extension method can go to an external library, to the App_Code folder, or to wherever you store helper classes in your project. This method gets the section name and a templated delegate, and it renders out the templated delegate if the section is not defined. You use the method as follows:
@this.RenderSection("Copyright", @Rights reserved for a better use.)
It is very important to note that you need to use the this keyword in the call to the RenderSection extension method. This is because extension methods are a compiler trick for adding custom methods to specific instances of a class.
The Case for Razor
Razor uses a terser syntax for expressing views than other markup languages. A Razor view consists of an HTML template that includes code nuggets and HTML helpers. Razor supports all existing HTML helpers. However, existing HTML helpers that are written as extension methods of the HtmlHelper class have limited flexibility for expressing the graphical layout. Fortunately, in Razor, you have an alternative approach—declarative helpers—that combines the power of programmability with the flexibility of templates. This hybrid model makes a compelling argument in favor of shifting to Razor.
About the Author
You May Also Like