Thursday, December 15, 2011

Razor View Engine

Asp.Net MVC view engine is a pluggable module, that gives more choices to choose or even create one for the better. Over time, the original ASPX view engine is giving way to Razor, not mentioning Spark, NHaml and other view engines. Microsoft Razor becomes a popular choice.
The first noticeable change from aspx view engine to Razor is @ replacing <%%>. The new parser is so smart that it infers in most cases rather than requiring strict literacy. E.g., it can infer the end of @{…} without requiring an @} to end the block. Also, it can even understand a combination of cs code and html tags inside the @{} block. This really simplifies authoring of views, and makes view code much more concise and less stubborn. The “multi-token” statement denoted with @(…) allows expressions which has "multi-tokens", instead of just a variable, to render. E.g., @(Usr.FirstName+" "+Usr.LastName).

Layout and Content Pages
Razor has all the view files with file name extension .cshtml. It makes better sense than .aspx etc. since it tells that the files are mix of cs code and html. At the meantime, Razor still supports the “master page” idea to re-use view parts and create identical look-and-feel UI experience for a site. Only gives different names: “layout file” and “content file”. @RenderBody () is to define a placeholder in layout files, while in content files @{LayoutPage = “whatever_layoutfile.cshtml”;} to specify the “outer content” file - the layout file. Notice, LayoutPage is a property of the view class and it refers to the layout file. Alternatively, this property can be set in controllers or in configuration file to make the whole site use a layout file by default.
In addition to "render body", @RenderSection(name, optional) in a layout file is to define “sections” inside the layout file that a content file can define content to fill. In a content file @Section the-sec-name{} is to define content for those sections. “Optional” being true indicates a section placeholder doesn’t mandate section content has to be defined. This mechanism offers enough flexibility to decompose and share view parts.

Strong-typed Views
Originally Razor uses @inherits System.Web.Mvc.WebViewPage<T> on the top of a view file to specify the view model type, it is now replaced with a more concise way, @model T. Also, @using namespace is for importing a namespace into a view. For data passing into views, an interesting feature is called "magic properties". ViewData is replaced with ViewBag which implemented the magic property. E.g., instead of ViewData["Message"] = "text", now it is ViewBag.Message= "text". Under the hood, ViewBag data is stored in ViewData as key-value pairs, but decorated with C# 4 new Dynamic feature. C# 4 introduced a new type "dynamic", which declares an instance whose properties can be dynamically added, and so even for it's methods through delegate type properties. Basically dynamic tells the compiler not to validate syntax and leave it until the run-time. To define a dynamic custom object just need the class to inherit from DynamicObject and override the TrySetMember, TryGetMember and TryInvokeMember methods, or by implementing IDynamicMetaObjectProvider interface.

Inline Template
In Razor, @<tag>…</tag> defines a “inline template”, an interesting feature for reusing view parts. Semantically a template is a view part that defines certain layout characteristics and has data place-holders, and is reusable. Razor makes it so handy, as comparing to Html helpers or partial views, that we can just define and use it on the go. E.g., @{Func<T, HelperResult> x = @<div>…item...</div>} defines a inline template and assigns it to a variable. Note, an "inline template" is precompiled into a delegate. The "item" variable denotes the instance of the T. in other words, T can be infered from the "item". "inline template" can also be used as a parameter for a function to apply a layout, e.g., Grid.Column("Price", format: @<span>$@item.Price</span>);

Unit Test Just on Views
Razor makes it handy to isolate views from controllers for tests. After precompiled, the views become classes, and we can write tests on them. E.g.,
[testclass]
Public HomeVieVTest{
var v = new HomeView(){
v.ViewBag.Message = "whatever";
HtmlDocuement d = v.RenderAsHtml();
HtmlNode n = d.DocumentNode.Element("h2");
Assert.AreEqual("whatever", n.InnerHtml.Trim());
}
}


References:

ScottGu's blog
David Ebbo's blog

No comments: