IronShay

Ironing code, geek t-shirts and even presentations!

NAVIGATION - SEARCH

The Big View Engine Comparison – Razor vs. Spark vs. NHaml vs. Web Forms View Engine

One of the cool things about ASP.NET MVC (and other MVC web frameworks) is its capability to change the default view engine with a Microsoft or a 3rd-party one. ASP.NET MVC currently has a few options for view engine alternatives:

  • The web forms view engine – this view engine, with the ASP-like syntax, is the default one for ASP.NET MVC 1 and 2 applications.
  • Razor – the new view engine which will be the default one for ASP.NET MVC 3 applications.
  • Spark – an open-source view engine which aims to seamlessly integrate code and HTML.
  • NHaml – a port of the Ruby on Rails successful view engine named Haml. This open-source view engine aims to replace HTML tags with an easier to read and better organized syntax.

Note: Spark and NHaml are currently available for ASP.NET MVC 2. NHaml also doesn’t support .NET 4 at the moment. I believe that they will become available for version 3 as soon as it is out (moreover, they are open-source so you can help getting there!). Razor, on the other end, is not available on ASP.NET MVC 2 and below.

In this post I’ll go through the basic operations we do with our views and show you how to get them done with the different view engines mentioned above.

Displaying Variable Content

Here I’m comparing the way to present the content of ViewData["Message"] as an html-encoded string.

The Web Forms View Engine

<%: ViewData["Message"] %>

Razor

@ViewData["Message"]

Spark

${ViewData["Message"]}

 

NHaml

&= ViewData["Message"]

 

Conditions

Here I’m comparing the way of writing an if condition to display “<p>Party</p>” between 6AM and 9PM and “<p>It’s bed time</p>” between 9PM and 6AM.

The Web Forms View Engine

<% if (DateTime.Now.Hour > 20 || DateTime.Now.Hour < 6)
   { %>
  <p>It's bed time!</p>
<% } else { %>
  <p>Party!</p>
<% } %>

 

Razor

@if (DateTime.Now.Hour > 20 || DateTime.Now.Hour < 6) {   
  <p>It's bed time!</p>
} else {
  <p>Party!</p>
}

 

Spark

<if condition='DateTime.Now.Hour > 20 || DateTime.Now.Hour < 6'>
  <p>It's bed time!</p>
</if>
<else>
  <p>Party!</p>
</else>

Another way of doing that in Spark (writing the condition inside the <p> tag):

<p if='DateTime.Now.Hour > 20 || DateTime.Now.Hour < 6'>
  It's bed time!
</p>
<else>
  <p>Party!</p>
</else>

 

NHaml

- if (DateTime.Now.Hour > 20 || DateTime.Now.Hour < 6)
  %p= "It's bed time!"
- else
  %p= "Party!"

 

Loops

Here I’m comparing the way to declare a list in the view and create an <ul>-<li> HTML list out of it.

The Web Forms View Engine

<% var list = new List<string>() { "WebForms", "Razor", "Spark", "NHaml" }; %>
<ul>
<% foreach (var item in list) { %>
  <li><%: item %></li>
<% } %>
</ul>

 

Razor

@{ var list = new List<string>() { "WebForms", "Razor", "Spark", "NHaml" }; }
<ul>
@foreach (var item in list) {
  <li>@item</li>
}
</ul>

 

Spark

<var list='new List<String>() { "WebForms", "Razor", "Spark", "NHaml" }'/>
<ul>
  <li each='var item in list'>
    ${item}
  </li>
</ul>

 

NHaml

- var list = new List<String>() { "WebForms", "Razor", "Spark", "NHaml" }
%ul
  - foreach (var item in list)
    %li= item

 

Displaying a Form

Here I’m comparing the way to write a login form. I’m using the login form from the default ASP.NET MVC 2 template (located at Views/Account/LogOn.aspx). Notice that it is based on a model of type Demo.Models.LogOnModel (located inside Models/AccountModels.cs).

The Web Forms View Engine

<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage<Demo.Models.LogOnModel>" %>

<% using (Html.BeginForm()) { %>
    <%: Html.ValidationSummary(true, "Login was unsuccessful.") %>
    <div>
        <fieldset>
            <legend>Account Information</legend>
                
            <div class="editor-label">
                <%: Html.LabelFor(m => m.UserName) %>
            </div>
            <div class="editor-field">
                <%: Html.TextBoxFor(m => m.UserName) %>
                <%: Html.ValidationMessageFor(m => m.UserName) %>
            </div>
                
            <div class="editor-label">
                <%: Html.LabelFor(m => m.Password) %>
            </div>
            <div class="editor-field">
                <%: Html.PasswordFor(m => m.Password) %>
                <%: Html.ValidationMessageFor(m => m.Password) %>
            </div>
                
            <div class="editor-label">
                <%: Html.CheckBoxFor(m => m.RememberMe) %>
                <%: Html.LabelFor(m => m.RememberMe) %>
            </div>
                
            <p>
                <input type="submit" value="Log On" />
            </p>
        </fieldset>
    </div>
<% } %>

 

Razor

@model Demo.Models.LogOnModel

@using (Html.BeginForm()) {
    @Html.ValidationSummary(true, "Login was unsuccessful.")
    <div>
        <fieldset>
            <legend>Account Information</legend>
                
            <div class="editor-label">
                @Html.LabelFor(m => m.UserName)
            </div>
            <div class="editor-field">
                @Html.TextBoxFor(m => m.UserName)
                @Html.ValidationMessageFor(m => m.UserName)
            </div>
                
            <div class="editor-label">
                @Html.LabelFor(m => m.Password)
            </div>
            <div class="editor-field">
                @Html.PasswordFor(m => m.Password)
                @Html.ValidationMessageFor(m => m.Password)
            </div>
                
            <div class="editor-label">
                @Html.CheckBoxFor(m => m.RememberMe)
                @Html.LabelFor(m => m.RememberMe)
            </div>
                
            <p>
                <input type="submit" value="Log On" />
            </p>
        </fieldset>
    </div>
}

 

Spark

<viewdata model="MvcApplication1.Models.LogOnModel"/>

# using (Html.BeginForm()) {
    ${Html.ValidationSummary(true, "Login was unsuccessful.")}
    <div>
        <fieldset>
            <legend>Account Information</legend>
                
            <div class="editor-label">
                ${Html.LabelFor(m => m.UserName)}
            </div>
            <div class="editor-field">
                ${Html.TextBoxFor(m => m.UserName)}
                ${Html.ValidationMessageFor(m => m.UserName)}
            </div>
                
            <div class="editor-label">
                ${Html.LabelFor(m => m.Password)}
            </div>
            <div class="editor-field">
                ${Html.PasswordFor(m => m.Password)}
                ${Html.ValidationMessageFor(m => m.Password)}
            </div>
                
            <div class="editor-label">
                ${Html.CheckBoxFor(m => m.RememberMe)}
                ${Html.LabelFor(m => m.RememberMe)}
            </div>
                
            <p>
                <input type="submit" value="Log On" />
            </p>
        </fieldset>
    </div>
# }

 

NHaml

- using (Html.BeginForm())
  =Html.ValidationSummary(true, "Login was unsuccessful.")
    %div
      %fieldset
        %legend Account Information
        %div.editor-label
          =Html.Label("UserName")
        %div.editor-field
          =Html.TextBox("UserName")
          =Html.ValidationMessage("UserName")
        %div.editor-label
          =Html.Label("Password")
        %div.editor-field
          =Html.Password("Password")
          =Html.ValidationMessage("Password")
        %div.editor-label
          =Html.CheckBox("RememberMe")
          =Html.Label("RememberMe")
        %p
          %input{type="submit" value="Log On"}

 

Conclusion

This was a quick comparison of the most basic features of the web forms view engine, Razor, Spark and NHaml. As you could see, they all support the same things but each takes it to its own direction.

Which one did you like the most?

All the best,
Shay.

kick it on DotNetKicks.com Shout it



Comments (34) -

Denmark dotnetgeekdk

Razor - no doubt!

Reply

I'm warming to Razor as a view engine, though for me, I don't think any of them have really sold me over the existing WebForms view engine.

<% %> is butt ugly for sure, but in a view it's immediately obvious to me where my variables are, where my looping is, etc.

I'm a big fan of keeping views simple, of re-use through partials, and generally just keeping the code to an absolute minimum (Views are dumb right ;)) so with that in mind, the WebForms view engine really just helps me *see* what's going on.

I'd have to see Razor with nice syntax highlighting etc. to be sure, but that's the one I'm warming to most.

Reply

Nice post Shay, it's good to see how the syntax of the 4 alternatives differ.

Just a minor point regarding the Spark example for "Displaying Variable Content". Typically, the ${} expression automatically encodes its output, while !{} doesn't (though this is based on the web.config setting). As such, the Spark example would normally just be ${ViewData["Message"]}.

It's probably also worth mentioning that Spark supports the awesome feature of bindings (see blog.robertgreyling.com/.../...ired-of-eating.html and http://sparkviewengine.com/documentation/bindings) which enables us to remove almost all of the non-html code from the form example. They're worth checking out if anyone is interested in keeping their Spark views HTML-centric.

It'll be no surprise, then, that my answer to your question is Spark Smile

Reply

Hi Shay - nice comparison. As Martin already pointed out above, your Html.Econde example is now double encoded give that Spark dollar syntax auto-encodes by default. More-so than that, I agree that the Bindings feature of Spark takes it a level above the others in terms of keeping your view simple/dumb, easy to outsource and easy to maintain. The exact same login form you have is done with Bindings in my blog post Martin mentions and takes you back to using Html the way it was meant to be used - as markup.

While this is a cursory look at the basic features side by side, the view engines only start coming in to their own when you start looking at the more advanced features. Of course they all need to support the basics, and there's very few ways of doing that without them looking the same. I'd like to see you dig a little deeper here and start comparing things like Razor Helper templates with things like Spark Macros and Bindings etc. That's when things really start to get interesting in my opinion...

Spark is obviously the winner in my eyes ;) - but I don't mind being called biased, I guess I am Tong

Great post,
All the best,
RobertTheGrey

Reply

Since you mention NHaml doesn't support .Net v4, it is probably worth mentioning that Spark does not support .Net v4 either.  

Also, Spark has an assembly loading race condition bug that *needs* to be worked around, as well as marginal Visual Studio editor support, there is a project to implement syntax highlighting and Intellisense, but it has not worked for us.

As much as we like Spark, these issues have kept us from using it and instead waiting for Razor to be officially released.

Reply

Sorry Quentin, but not much of what you said is actually true.

.Net 4 support works just fine in Spark now.

The race condition you're talking about isn't a "race" condition at all, it's simply a pipeline execution timing issue that isn't even a bug, and furthermore it's not a workaround for it, there's a *solution* to the problem mentioned here: (stackoverflow.com/.../spark-views-work-initially-but-then-get-a-dynamic-view-compilation-failed-erro) - all of which you know about already. Spark cannot control the AppPool execution Pipeline itself, it must be wired into the application bootstrapping, just like anything else.

I don't know of any other project except for NDjango that has full IntelliSense - Not even Razor has full support yet and they actually pay people to work on it. I'm willing to bet the Spark implementation will be done before Razor RTW's

I'm not sure why any of these should prevent you using it, I've had 8 projects in production using Spark, 3 of which have been live for over two years when there really were still issues to speak of. I don't see http://agilezen.com/ struggling to stay up.

I'm sorry, but I'm going to have to call FUD on this one.

Let me know if you're having trouble using SparkSense, we have a dev team of 18 strong all using it without any issues other than the fact that it's not quite finished yet...

Reply

I really like spark, but really the most expressive and concise view engine has to be NHAML.
I love the way NHAML takes care of outputting XHTML and it all results in less lines of code, all of which are needed. It is the clear winner in my eyes, just count the LOC ;)

Reply

There is also a pretty big comparison that I put together at: blogs.msdn.com/.../10070953.aspx

I included a lot of samples but they are not next to each other like your comparison - so you might want to check it out.  The NerdDinner.CodePlex.com project also has the views in Spark, NHaml, aspx, and MVC 3 Razor Preview (Not beta syntax).  

Reply

In this (as well as the comparison mentioned by Jason) I am missing a word about refactoring support. Do existing tools like VS, R# and CodeRush support any of these?

cheers,
Erich

Reply

Australia Chris Rogers

I like the look of Razor - and starting looking at it .... until I realised that ReSharper just cannot handle this new syntax.  Would be too painful to use until Resharper add support.

Reply

Australia John Simons

NHaml is the sexiest one!

Reply

Hi, thanks for writing this up. I posted a follow-up with some of the examples people mentioned about Spark.

whereslou.com/.../notes-on-the-big-view-engine-comparison

Reply

Strong opinion coming Smile

I really don't get how the HAML model isn't adopted more. It's wonderful - the whole "lack of enclosure" and proximity to CSS is the point of it all, as well as absolutely WONDERFUL markup generation. I like the writeup Shay but I think something that would communicate HAML much more clearly would be:

#content
  #title This is my title
  #article
    This is my article
   - foreach(var comment in comments)
     .comment-author=comment.author
    .comment-body
      =comment.body

That is so wonderfully readable, completely without the "noise" of HTML - yet with adhering strongly to proper markup and class identification. I don't want to write HTML in the same way I don't want to write XML or SQL - I suck at it and I forget to properly indent. I also forget closing tags and blow up my page when refactoring.

Anyway - my opinion Smile. Good writeup Smile.

Reply

Some indenting typos above - it was freehanded... hopefully conveys my point Smile.

Reply

Is Haml not strongly typed? All the other exiles are, but the final Haml usesstring based properties

Reply

We are still using webform markup for all of our MVC projects, primarily because Resharper's refactoring support doesn't extend to anything else yet, and you'll be prizing Resharper away from my cold dead fingers...

Reply

I like razor the best here!

Reply

Just picking up on Rob's point about NHAML. Whilst I agree entirely when you are writing your own web pages, should you find yourself needed to incorporate other people's HTML based designs, we have found Spark to be a perfect fit.

It allows us to literally copy and paste the HTML from our designers and substitute anything "dynamic" with bindings to our model, put in loops and conditionals etc. based on the HTML rather than re-writing it.

Reply

Sorry Quentin - I did not intend to start an argument, and I have no wish to continue one but I will answer your questions since you've asked them...

Is v4 compatibility in the stable release?  Or only in the latest development build?

You're right - I think that the release process of Spark has been very slow, probably because not much had been added to the framework until recently, but that is no excuse. This perhaps has had a negative effect on adoption in that the releases were not marked "official" - but of course with a code base that wasn't changing much, downloading the latest development build is equally safe, and that is how we chose to run our projects, but I understand that some may not want to do that.

Compilation against v4 has been around since April 2010 to my knowledge, but dynamic compilation of views against v4 has only recently made it into the main trunk - not because it wasn't there to be used, but because nobody had yet pulled those changes in to the trunk - something I sought to rectify personally. If you were struggling to get the latest dev build zip files, you certainly weren't very vocal about it, I would have helped you out at the drop of a hat.

I can't say I've never had annoying times with Spark, I don't think anyone can, but quite honestly, I've never been able to reproduce in Production the problems you've experienced, and I've never had them happen accidentally on any of the 8 projects we run. Should I not be announcing this fact equally loudly? Or maybe we've just been lucky? I can simulate the problem in development sure and I'm not trying to dismiss your claims - clearly they have happened, I was merely saying that the solution is out there - you yourself said you had implemented a fix. Without further communication from you about this - how are we to know any better? I have personally tested this on the codebetter canvas solution and the fix suggested worked - and continued to work.

I'm not going to argue with you about the definition of a race condition, you clearly think it is and that's fine. The fact that the AppDomain is (behind the scenes) recycling assemblies that are loaded into memory for Spark to use is not the fault of Spark. Spark loaded them successfully the first time else it would't run at all. The fact that the fix involves explicitly loading those assemblies at start-up to keep a handle on them each time this happens is perfectly fine as a solution - I can't see where you find fault with it, and you certainly haven't come back saying it doesn't work for you.

The old age of a post or lack of popularity of a StackOverflow question does not strengthen your argument, it weakens it. The very fact that there is not much traffic in that discussion is directly proportional to the number of developers experiencing this particular issue. There are a lot more deployments of Spark in production than you realize and if everyone was experiencing the problem, we would have heard about it by now - even if it was via the grapevine. The fact that the answer has been marked as accepted by the question asker would be enough for me to try it, but hey, if you want to wait for 25 up-votes first - be my guest. Having only one up-vote doesn't make it any less correct - it makes it less visited or experienced.

What does paid work have to do with it? Spark has a certain level of Intellisense support, period.

Paid work has everything to do with it I'm afraid. You're right, Spark has a certain level of IntelliSense support. Period. If I was working on it paid full time to do so then there would be more Intellisense support by now. Period. It's a simple equation involving man power and Razor has had full time developers on it now for over a year and when they want to add C# IntelliSense, they stroll over to the VS tools team and put their case forward. Us folk on the outside have to make use of what extensibility points are provided. Guess how many public extensibility points there are for C# language IntelliSense support - that's right - zero, we have to roll our own. And NDjango doesn't have C# IntelliSense, it has Type Completion - that's different and more important, and that's what's coming in Spark pretty soon, and something I believe is the last really big thing that's lacking. The rest is minor tooling and productivity pieces

And Razor has it too, now.  What was that about a bet?

HA! I knew that one might come back to bite me! Technically speaking I said before Razor RTW's which gives me a little more time ;) But I'll give you that one - it's there now, and I'm actually in talks with the team trying to figure out if they've been kind enough to give us a way to piggy back off the work instead of keeping it closed and internal - but we'll see what comes of that...

I call bias.

I have already stated openly that I am biased - and in no way do I think you should keep quiet about your views - of course you're entitled to them because of the experiences you've had, but that shouldn't stop me from coming out and announcing an entirely different experience. I didn't begin by working on the source code of Spark and then using it in triumph, I was a user (sometimes in anger) that became enamoured with its elegance and decided to give something back to a project that has helped me enormously over the last 2 years.

Any way, I apologize for my strong statements the other day, I was probably just cranky. And apologies if they offended you, I did not mean to shut you down even though reading it again now it came across that way...

Beyond all of that we'll just have to agree to disagree, and good luck on your travels. My offer of help as always, still stands...
All the best,
Rob

Reply

I don't intend to start an argument either, Robert.

Frankly, we love how Spark can enable us to build the front end of a web application.  We want to use it.  I think it's 100% awesome, on several levels, that such a project exists at all.

The successes of Spark should be detailed as much as, if not more than, the troubles encountered with Spark.

As dev's we have to make decisions often on what to use and what not to use.  Information, quantity and quality, is the important resource to help us make those decisions with confidence.

Like many OSS projects, Spark would benefit from simply more quantity of information.

We can agree to disagree as our perspectives from which we view Spark are different.  I don't even know that we disagree as much as we are simply emphasizing different parts of the Spark picture.

Best to you as well.

Reply

I don't like how spark has 20 alternative ways to do them all, sometimes the code is an attribute and sometimes it's an element? When I look at this, or my team looks at this, they are going to have to dig out a google search just to figure out what is supposed to be markup and not. (notice how I did not say bing.)

Reply

I was all about Razor until I read a post by Rob Conery about Haml/NHaml; the more I see it, the more I love it. It's just so clean, though having closing tags is nice reminder of which elements are nested in which. Now Razor seems only slightly less cluttered than WebForms.

WebForms is just too much garbage. Spark looks good, but something about it's aesthetics turns me away from it; I think it's the angle brackets mixed in so tightly with code. It also doesn't (appear to) have the huge reduction in code that NHaml does.

Reply

No experience whatsoever with any of the View Engines and MVC, but starting to learn some new things and trying to get away from Web Forms one step at a time.

If I look at the example I like Razor's syntax best. It's personal and without hands-on experience, but I don't like the brackets in Spark and in the Loops and Conditions examples, Spark looks to be too much hidden inside the Html for my taste.
I like how short NHaml is, but it's too abstract to my tastes with too many types of prefixes to tell the compiler what you mean.
I like Razor because it's essentially what you do already with just one character to say "Razor code is coming up." It's just a neat way of inline C#.

Reply

India Vijaya Anand

Razor and Sharp looks more readable!

Reply

For me Razor is the best. Compact but expressive.

Reply

I prefer Razor out of all of these. NHaml is just weird Smile

Reply

A far more complete list of ASP.NET MVC view engines is available on a stackoverflow wiki: stackoverflow.com/.../1451355#1451355

(albeit with less examples)

Reply

NHaml is awesome. Razor is great too, but I really love working with NHaml. What's also pretty cool about it is that your markup tends to sync really nicely with your css selectors.  

Reply

I like razor!

By the way, thank you so much for posting.

Reply

Cutting and pasting that does not work in MVC3. To get the extension to work, I had to create a class file:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace incMvcSite.Classes {
    public static class HtmlPrefixScopeExtensions {
        public static IDisposable BeginHtmlFieldPrefixScope(this HtmlHelper html, string htmlFieldPrefix) {
            return new HtmlFieldPrefixScope(html.ViewData.TemplateInfo, htmlFieldPrefix);
        }

        private class HtmlFieldPrefixScope : IDisposable {
            private readonly TemplateInfo templateInfo;
            private readonly string previousHtmlFieldPrefix;

            public HtmlFieldPrefixScope(TemplateInfo templateInfo, string htmlFieldPrefix) {
                this.templateInfo = templateInfo;

                previousHtmlFieldPrefix = templateInfo.HtmlFieldPrefix;
                templateInfo.HtmlFieldPrefix = htmlFieldPrefix;
            }

            public void Dispose() {
                templateInfo.HtmlFieldPrefix = previousHtmlFieldPrefix;
            }
        }
    }
}
In the Razor (.cshtml) file, I added the following:
@using incMvcSite.Classes
@using(Html.BeginHtmlFieldPrefixScope("Permission")) {
    
        Permission

        // The Html.EditorFor's would go here...
    
}
Notice the using to bring me extension class into scope. That allows the second using line to work.
Now the problem is that when posting back, the object is not updated. In my controller, I used a second parameter to specify my prefix:
TryUpdateModel(modelUser.Permission, "Permission");
This added the prefix to all field in the HTML, and the TryUpdateModel loaded the object with prefixed control names. Now you can properly namespace your controls for embedded edit lists, and for partial views of models with the same property names.

Shawn Zernik
Internetwork Consulting

Reply

I am worried its creating a nightmare in maintaining the view since the view engines are being replaced. What might happen is special view engines would crop up for optimization and there is going to be hell.

Best is the MVP web forms with jquery templating engines.

Reply

Hi all,

Just a quick heads up for anyone who's interested, I've adopted the NHaml project an am looking to resurrect it, with an initial re-release sometime in December.

Russ

Reply

like Razor's syntax best. It's personal and without hands-on experience, but I don't like the brackets in Spark and in the Loops and Conditions examples, Spark looks to be too much hidden inside the Html for my taste.

Reply

Australia Travis Simon

Most people don't like NHaml until they have to use it. And they resist it for about a day. And then, something happens, and they become converts, singing its praises.

If you think it looks weird, or you don't like it, force yourself to spend a day working with it - you won't regret it.

Reply

Pingbacks and trackbacks (7)+

Add comment