The Curly Brace Tax

(I shamelessly stole this title from a chat with Matt Hinze)

99% of the examples you see out there for ASP.NET MVC are using WebFormsViewEngine.  That's fine, it's familiar, it benefits from intellisense, compilation, and refactoring support.  But all of that comes at a price, and that price is (at times) incredibly wordy.

Picture this example, taken from the Site.master (master page) in the Preview 5 new project template.

At the top of the page, they want to render some text if the user is logged in, and different text if the user isn't logged in.  Here are the screens:

When you're not logged in:
mvc-masterpage-login

And when you're logged in...
mvc-masterpage-logout

This is implemented using ASPX code that looks like this:

<%if (Request.IsAuthenticated) {%>Welcome <%= Html.Encode(Page.User.Identity.Name) %>![ <%=Html.ActionLink("Logout", "Logout", "Account") %> ]<%}else {%> [ <%=Html.ActionLink("Login", "Login", "Account") %> ]<%}%>

Now let's take the same example and convert it to NVelocity, another view engine with a looser syntax:

#if ($isAuthenticated) Welcome $html.encode($user.name)! [ $html.actionlink("Logout", "logout", "account") ] #else [ $html.actionlink("Login", "login", "account") ] #end

We'd have to stuff the $isAuthenticated and $user values into ViewData, but that's a piece of cake.  This is a great example of how concise we can get if we don't rely on all that strong typing.  The beauty of this is, the key that you use for ViewData becomes the object you interact with on the view.

In NVelocity:

  • case doesn't matter
  • no need to open up <% %> tags, you can embed it directly in your template
  • type doesn't matter.  It's evaluated at runtime.

There are some downsides, however:

  • You don't get compile time checking for your views.  If I wrote $htlm.actionlink(..)  I'd get an error at runtime.
  • Performance.  Compiled views are much faster than interpreted ones.  It's likely that this doesn't matter for most sites out there.
  • No intellisense.  (The arguments to ActionLink above are not obvious, so you just have to memorize it)
  • No Refactoring support.  This is a big one.  If you rename your actions, you'll have to do a string comparison search to get the various links you might having lying around in your view.

That last one is really the only one that I miss when doing something in NVelocity.  In any case, try it out, see what you think!

#1 Chris Missal avatar
Chris Missal
10.05.2008
7:29 PM

The example is fairly concise, and I'm working in pages that have a lot more going on than that. I like it, it's clean. Maybe we'll have to adopt NVelocity for a future release... :D


#2 Joey Beninghove avatar
Joey Beninghove
10.05.2008
7:58 PM

Figured someone had to say it...Don't forget to mention NHaml!:DBut yeah, more and more folks are starting to realize that the lack of intellisense for these angle-bracket-less template languages is not a real show-stopper.I do agree that it could hurt refactoring, but R# is usually pretty good about finding string literals when performing renames.Although, now that I think about it, I've never tried to see if it would work in NVelocity or NHaml templates.Might have to try that now...I'm building a MVC site right now using NHaml and have really loved it.Just need to get SaSS (DRY CSS) working in ASP.NET MVC and I'll be golden.


#3 Chad Myers avatar
Chad Myers
10.05.2008
8:28 PM

Not crazy about logic in my view like that (WebFormsViewEngine or no)What about:<%= this.UserAuthStatus<AuthController>(c=>c.Login(), c=>c.Logout()) %>UserAuthStatus being an extension method to ViewPage<T> that does the logic. It can be tested in isolation outside the view and could also be applied to master pages as well (by making it an ext. method for ViewMasterPage or something like that)?


#4 Ben Scheirman avatar
Ben Scheirman
10.05.2008
9:16 PM

I suppose I agree with you there, but I don't really mind small if statements on the view (especially if they're as concise as the example).Your example has a ton of compiler noise though.An NVelocity equivalent might be..$html.loginDisplay("login", "login", "logout")But the value of having this in a helper somewhere isn't really apparent.You're probably not going to reuse this anywhere, and we might want to see the HTML we're using for this.Of course, we still have those magic strings in there, but I think in some cases that's fine.


#5 Chad Myers avatar
Chad Myers
10.05.2008
9:19 PM

@Ben:I'll take Compiler Noise over Refactor-ability any day.Magic strings in my view = BAD in my experience.So if there's a way to keep it strongly typed and yet with no apparent logic in the view, it's a winner IMHO.


#6 Mark Nijhof avatar
Mark Nijhof
10.06.2008
4:45 AM

I am not familiar with ASP.Net MVC except from what I read in the blogs, but isn't this supposed to separate logic from presentation? In both examples I still see logic, would a place holder not be a more appropriate way and have something else decide what should be inside the place holder? The comments from Chad and you make more sense, but even there I would not be happy with it. Maybe it is my lack of understanding how ASP.Net MVC works?-Mark


#7 Ben Scheirman avatar
Ben Scheirman
10.06.2008
8:11 AM

Mark,Some view logic I am fine with on the view (like simple if statements).In other cases where you have complex logic, push that into the controller or into a view helper.This keeps your views pretty simple.


#8 Mark Nijhof avatar
Mark Nijhof
10.06.2008
8:23 AM

The issue that I have with these things is that simple logic has a tendency to grow into well not so simple logic ;) because it is easy to implement that extra needed functionality there instead of taking it out and move it in the proper place. Of course when you have a disiplined team working that would do this then everything is fine. So maybe a better question is: when is it getting too complex, is there a precise rule you can attach to where the logic should be implemented? And with "should be" I mean when can it be done in the view.-Mark


#9 Anthony Bouch avatar
Anthony Bouch
10.06.2008
1:21 PM

I also like the NVelocity syntax - in particular when building templates that a less technical user or designer is going to style for a theme. We just publish a list of tags like $navigation, $recentComments, $pager, $content, $footer etc.. and the designer can experiment freely with the style and layout. In some cases we'll use a helper method on the server to pre-bake a chunk of Xhtml before assigning it to a variable in the ViewData. In other cases we'll include other templates using #include('navigation.vm') or #parse('sidebar.vm').


#10 DevilsAdvocate avatar
DevilsAdvocate
11.12.2008
11:54 AM

What about a LoginStatus control or helper method that checks the login status and presents the correct HTML bytes? Something like a simple check for authenticated users could easily grow to include displaying different status messages based on other user attributes such as roles, type of user, etc.


#11 DevilsAdvocate avatar
DevilsAdvocate
11.12.2008
11:54 AM

What about a LoginStatus control or helper method that checks the login status and presents the correct HTML bytes? Something like a simple check for authenticated users could easily grow to include displaying different status messages based on other user attributes such as roles, type of user, etc.