The search for a better View Engine – ASP.NET MVC
August 20, 2009
I have been evaluating the different view engines available for ASP.NET MVC. In my last two posts I evaluated Brail and finally dismissed it since it didn’t match up to what I was looking for.
Just to recap – I’m looking for a view engine that will allow a web designer to create the views on my behalf, or at the very least work closely with the developers in terms of styling and layout. The default ASP.NET view engine is simply not sufficient in this regard – the design was always aimed at developers, not designers. On a higher level, projects like MVC Contrib are making this problem even worse. Don’t get me wrong – I like MVC Contrib. I’m simply making the point that while we are making development easier for developers, we are making it harder for designers to work with developers.
If you don’t believe me, go ask a designer to style this table.
<%= Html.Grid(Model.People).Columns(column => {
column.For(x => x.Id).Named("Person ID");
column.For(x => x.Name);
column.For(x => x.DateOfBirth).Format("{0:d}");
})
.Attributes(style => "width:100%")
.Empty("There are no people.")
.RowStart(row => "<tr foo='bar'>") %>
I had a quick look at the different view engines available to see if any of them could help us in the right direction.
NHaml
NHaml is part of MVC Contrib and uses Haml syntax from Ruby.
#foo
- foreach (var product in ViewData)
- if (product.Category.CategoryName != null)
%h2=product.Category.CategoryName
- break
%ul.productlist
- foreach (var product in ViewData)
%li
= Html.Image("/Content/Images/" + product.ProductID + ".jpg", product.ProductName)
.productdetail
=Html.ActionLink(product.ProductName, "Detail", new { ID=product.ProductID })
%br
Price:
=String.Format("{0:C2}", product.UnitPrice)
%span.editlink
(
=Html.ActionLink("Edit", "Edit", new { ID=product.ProductID })
)
Thanks to Scott Hanselman for the snippet. Clearly this is not what I’m looking for.
Spark
I mentioned on Twitter that I’m evaluating different view engines and someone immediately recommended Spark to me. I actually remembered that Phil Haack blogged about it so I was quite optimistic.
<viewdata products="IEnumerable[[Product]]"/>
<ul if="products.Any()">
<li each="var p in products">${p.Name}</li>
</ul>
<else>
<p>No products available</p>
</else
The syntax is certainly different and very html friendly (the Spark motto is “Html friendly – Less is more”). However it’s still not what I’m looking for – while a designer would find the syntax reasonably familiar, I’m convinced that mixing html with evaluated code in this manner is not helping our cause in this regard.
NVelocity
My initial impression of NVelocity was very positive. There seems to be very little information available for this view engine, but a couple of the examples looked very promising. Here is what a foreach loop looks like.
#foreach( $recipe in $viewdata )
<li><a href="Edit/$recipe.ID">$recipe.Name</a></li>
#end
Which is very much what I’m looking for. However, take a look at the following snippet.
$HtmlHelper.LabelFor('elementid', 'Name:', "%{class='required', accessKey='N'}")
And we’re back to square one. Or even worse:
#foreach($person in $people)
#beforeall
<table>
<tr>
<th>Name</th>
<th>Age</th>
</tr>
#before
<tr
#odd
Style='color:gray'>
#even
Style='color:white'>
#each
<td>$person.Name</td>
<td>$person.Age</td>
#after
</tr>
#between
<tr><td colspan='2'>$person.bio</td></tr>
#afterall
</table>
#nodata
Sorry No Person Found
#end
Finally, an answer
I completely understand why most view engines have complex syntax – we are trying to duplicate the functionality from the default ASP.NET view engine. A developer isn’t going to use a view engine if it can’t do everything the built-in one can do, right? Well, not necessarily…
A colleague introduced me to Smarty, a view engine designed to allow developers and designers to work together. Here is a foreach loop in Smarty.
<ul>
{foreach from=$myArray item=foo}
<li>{$foo}</li>
{/foreach}
</ul>
I was immediately impressed by the syntax. Presentation logic is clearly separated from the actual html and it’s painfully obvious what is evaluated code and what’s not. There are even modifiers to allow a designer to specify that a variable must be displayed in upper- or lowercase or to specify the datetime format. I had a look at most of the functions and became convinced that this is exactly what I’m looking for.
There’s only one major problem – it’s a php view engine.
Writing my own view engine
I wanted to experiment and see if I could write a view engine (for ASP.NET) that uses the same syntax. I found it surprisingly simple in some areas and surprisingly difficult in others. It took me about an hour to be able to render a foreach statement (and another hour to be able to render nested foreach statements). The tricky part is in structuring the code and allowing different kinds of functions (at the moment you should be able to add your own functions as well).
So this is where I am at the moment – I first want to try and structure the code a little better and at least implement all the functions that Smarty supports by default. More on this will follow.
On a personal note
I usually don’t mention personal details in my blog, but I’m going to make an exception here. I won’t be doing any blogging for at least 2 weeks since I’m off to London and Europe during which time I’m going to watch the Belgian Grand Prix.
Happy coding.
NHaml has evolved a little since your code snippet
It would look more like the following
#foo
%h2=category.CategoryName
%ul.productlist
– foreach (var product in ViewData)
%li
%image{src='#{"/Content/Images/" + product.ProductID + ".jpg"}' alt=product.ProductName}
.productdetail
=Html.ActionLink(product.ProductName, "Detail", new { ID=product.ProductID })
%br
Price: #{product.UnitPrice.ToString("\{0:C2\}")}
%span.editlink
(
=Html.ActionLink("Edit", "Edit", new { ID=product.ProductID })
)
I removed the first loop because that should be something done by the controller not the view
Your snippet also make heavy use of helpers which i find make the code harder to read.
I converted the html.Image to a plain image tag.
Your final loop syntax u like from php would look like this in nhaml
%ul
-foreach(var foo in myArray)
%li=foo
And the inlining u like in php is now done in a similar way in nhaml.
Any code can be wrapped in #{…} and it will be interpreted
For example
The current date and time is #{DateTime.Now}
In smarty, there is a capture feature which is handy when u wanto capture an outcome from a helper and pass it as parameter into another helper.
I am looking for something equivalance on asp.net mvc. Sadly, no result so far.
@anonymous: Thanks for the update. However I'm still not convinced that NHaml is what I'm looking for – although you have illustrated that you CAN write neat markup with NHaml. But the same can be said for the regular Webforms View Engine – you simply need to restrict yourself. However what I'm looking for is (1) a View Engine that restricts you so that you can only write neat/simple markup (2) a clear distinction between what is application logic and what is display logic.
@exiang: Capture is one of the first functions I implemented.
Have you had a look at NDjango? Djangos popular view-engine ported to work with ASP.Net MVC
@anon: I had a very quick look but I didn't play around with it. There doesn't seem to be much in the line of examples, or maybe I just didn't search properly.
the problem ur trying to solve cannot be solved with a designer-friendly view/template system becoz the real problem is that mvc was conceived at a time when the ui designer and programmer were the same person
so while mvc technically decouples a model and a view it unfortunately couples the workflow implementation of the model and view. if the designer and the developer were the same person would u still use smarty/sharpy/whatever?
although smarty is the most concise syntax listed on this page its also the most terse and fails to abstract programming concepts that designers won't and/or don't grasp ie. variables, loops, if/else constructs
smarty was designed by a bunch of anal phpers who thought that designers should never ever see what php code looks like coz that would be "dangerous"
all smarty does is create a crippled subset of ur programming language that developers will end up writing on behalf of designers anyhow
the php world has already dissed smarty and moved on. smarty is officially no longer developed/maintained by the php team. its only around because of legacy projects and n00bs who think its a great way to protect php apps from designers
@quinton: That's a pretty interesting take on things.
To answer some of your questions – if the developer and designer were the same person, I would probably just stick with the regular view engine, since I can then understand both code AND style the views. Only issue then would be with maintainability. Problem is that your developers would then need to be great at markup, most developers aren't.
Smarty has been used extensively where designers write all the views – to think that the simple markup of Smarty is too complex for designers is… just wrong, IMO. The only problem comes in when we're writing too much code in the views – with Smarty this isn't a real possibility.
You're making this whole argument, but what's the alternative? Have developers do both? Developers suck at styling, IMO.
@jaco i'm an optimist but i'd love to see designers write smarty or any code for that matter. ur view is going to contain presentation logic. if this. then that. while loop this. for loop that. designers will crack at some point. do u think designers will know when/how to escape data to prevent XSS?
Sometimes u push data into a view. Sometimes u pull data from the view. Will designers know how or when to do this?
You certainly have to use a templating system to manage headers/footers and crap. just don't use a templating system that invents another language or a subset of the lang you are using
In php this is easy because of the alternative syntax
http://php.net/manual/en/control-structures.alternative-syntax.php
Is that so much more verbose than Smarty?
with syntax like that designers can still edit merrily away in dreamweaver without tripping over php (that was written by devs). but thats php. it has its roots in templating and string manipulation. the connecting to databases and other language crap came later
if u wanna b hero to all the suckers who ported MVC pattern from desktop to www you should design a pattern that effectively enables a designer and a developer to work independently and therefore in parallel as oppose to the MVC ping-pong workflow
BUT then there's all the haters who will proclaim that MVC is pure genius. Has only pros and zero cons. And then everyone will hate you :/
@quiton Ok we will have to agree to disagree. I just don't see a better alternative – not for ASP.NET developers/designers in any case.
I've chatted/had e-mails from quite a few people who have worked with designers who actually write Smarty views on their own.
MVC is a great pattern – but it's obviously not perfect. It's an interesting idea though – designing a new pattern to allow developers and designers to work together.