Advanced Razor

Chapter 3 highlighted the main Razor features you'll be likely to use in day-to-day work. Razor supports some additional features which, while a little more complex, are really powerful. We think they're worth the effort.

Templated Razor Delegates

In our Razor Layout discussion, we looked at one approach to providing default content for optional layout sections that required a bit of boilerplate code. We mentioned that we could create a better approach using a feature of Razor called Templated Razor Delegates.

Razor has the ability to convert an inline Razor template into a delegate. The following code sample shows an example of this:

@{  Func<dynamic, object> template = @<strong>@item</strong>;}

The delegate that's generated when using a Razor template is of type Func<T, HelperResult>. In the preceding example the type T is dynamic. The @item parameter within the template is a special magic parameter. These delegates are allowed only one such parameter, but the template can reference that parameter as many times as it needs.

With this in place, we can now use this delegate anywhere within our Razor view:

<div>      @template("This is bolded.")</div>

The result of this is that we can write a method that accepts a Razor template as an argument value simply by making that argument be a Func<T, HelperResult>.

Going back to the RenderSection example presented in the Layouts example in Chapter 3, let's do just that:

public static class RazorLayoutHelpers {  public static HelperResult RenderSection(this WebPageBase webPage, string name,     Func<dynamic, HelperResult> defaultContents) {    if (webPage.IsSectionDefined(name)) {      return webPage.RenderSection(name);    }    return defaultContents(null);  }}

The method we wrote takes in a section name as well as a Func<dynamic, HelperResult>. Therefore, it can be called within a Razor view like so:

<footer>    @this.RenderSection("Footer", @<span>This is the default.</span>)</footer>

Notice that we passed in the default content as an argument to this method using a snippet of Razor. Also note that the code uses the this argument to call the RenderSection extension method.

When using an extension method of a type from within that type (or a derived type of that type), the this parameter is required to call that extension method. When writing a view, it's not readily apparent that we're writing code within a class, but we are. The next section explains this and provides an example that allows us to clean up our usage of RenderSection even more.

View Compilation

Unlike many templating engines or interpreted view engines, Razor views are dynamically compiled at runtime into classes and then executed. The compilation happens the first time the view is requested, which incurs a slight one-time performance cost. The benefit is that the next time the view is used, it's running fully compiled code. If the content of the view changes, ASP.NET will automatically recompile the view.

The class that a views is compiled into derives from WebViewPage, which itself derives from WebPageBase, which you saw in the section “Templated Razor Delegates.” For long-time ASP.NET users, this shouldn't come as a surprise because this is similar to how ASP.NET Web Forms pages work as well.

It is possible to change the base type for Razor views to a custom class, which makes it possible for you to add your own methods and properties to views.

The base type for Razor views are defined within the Web.config file in the Views directory. The following section of Web.config contains the Razor configuration:

<system.web.webPages.razor>    <host factoryType="System.Web.Mvc.MvcWebRazorHostFactory,     System.Web.Mvc, Version=3.0.0.0,     Culture=neutral, PublicKeyToken=31BF3856AD364E35" />  <pages pageBaseType="System.Web.Mvc.WebViewPage">    <namespaces>      <add namespace="System.Web.Mvc" />      <add namespace="System.Web.Mvc.Ajax" />      <add namespace="System.Web.Mvc.Html" />      <add namespace="System.Web.Routing" />    </namespaces>  </pages></system.web.webPages.razor>

The thing to notice is the <pages> element that has the pageBaseType attribute. The value of that attribute specifies the base page type for all Razor views in your application. But you can change that value by replacing it with your custom base class. To do so, simply write a class that derives from WebViewPage.

Let's do just that — adding a RenderSection method overload to our CustomWebViewPage class:

using System;using System.Web.Mvc;using System.Web.WebPages;public abstract class CustomWebViewPage<T> : WebViewPage<T> {    public HelperResult RenderSection(string name, Func<dynamic, HelperResult>          defaultContents) {        if (IsSectionDefined(name)) {            return RenderSection(name);        }        return defaultContents(null);    }}

Note that the class is a generic class. This is important in order to support strongly typed views. It turns out that all views are generically typed. When no type is specified, that type is dynamic.

After writing this class, we need to change the base page type in Web.config:

<pages pageBaseType="CustomWebViewPage">

After making this change, all the Razor views in the application will derive from CustomWebViewPage<T> and will have the new RenderSection overload, allowing you to define an optional layout section with default content without requiring the this keyword:

<footer>    @RenderSection("Footer", @<span>This is the default.</span>)</footer>
note
UnFigure

To see this code as well as Layouts in action, use NuGet to install the Wrox.ProMvc3.Views.BasePageType package into a default ASP.NET MVC 3 project like so:

Install-Package Wrox.ProMvc3.Views.BasePageType

After installing this package, you'll need to change the base page type within the Web.config file in the Views directory to CustomWebViewPage.

The example folder in the Views directory contains an example of a Layout using the method we just implemented. Hit Ctrl+F5 and visit the following two URLs to see the code in action:

  • /example/layoutsample
  • /example/layoutsamplemissingfooter
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.226.88.110