Posts from  July 2010


7.23.2010

What we are doing with HtmlTags Part 2 : Form Fields

Most simple forms we write, especially in LOB applications, are repetitive sections of inputs and/or displays. Take a look at an exmaple from the MVC Music Store sample, the album editor template, notice anything repetitive?

<%@ Import Namespace="MvcMusicStore"%>

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<MvcMusicStore.Models.Album>" %>

<script src="/Scripts/MicrosoftAjax.js" type="text/javascript"></script>
<script src="/Scripts/MicrosoftMvcAjax.js" type="text/javascript"></script>
<script src="/Scripts/MicrosoftMvcValidation.js" type="text/javascript"></script>

<p>
    <%: Html.LabelFor(model => model.Title)%>
    <%: Html.TextBoxFor(model => model.Title)%>
    <%: Html.ValidationMessageFor(model => model.Title)%>
</p>
<p>
    <%: Html.LabelFor(model => model.Price)%>
    <%: Html.TextBoxFor(model => model.Price)%>
    <%: Html.ValidationMessageFor(model => model.Price)%>
</p>
<p>
    <%: Html.LabelFor(model => model.AlbumArtUrl)%>
    <%: Html.TextBoxFor(model => model.AlbumArtUrl)%>
    <%: Html.ValidationMessageFor(model => model.AlbumArtUrl)%>
</p>
<p>
    <%: Html.LabelFor(model => model.Artist)%>
    <%: Html.DropDownList("ArtistId", new SelectList(ViewData["Artists"] as IEnumerable, "ArtistId", "Name", Model.ArtistId))%>
</p>
<p>
    <%: Html.LabelFor(model => model.Genre)%>
    <%: Html.DropDownList("GenreId", new SelectList(ViewData["Genres"] as IEnumerable, "GenreId", "Name", Model.GenreId))%>
</p>

It didn’t take me long to notice that the duplication was driving me absolutely crazy! Yet, the community is hailing these template helpers as the next best thing since web forms and for the life of me I have no idea why! Why stop at the abstraction of DisplayFor, InputFor and LabelFor? Our views were suffering the same problem, even with a spark and HtmlTags spin:

<div class="fields">
  !{Html.LabelFor(m => m.Quantity).AddClass("label")}
  !{Html.InputFor(m => m.Quantity).AddClass("field")}
</div>

<div class="fields">
  !{Html.LabelFor(m => m.Price).AddClass("label")}
  !{Html.InputFor(m => m.Price).AddClass("field")}
</div>

Template Convention

Both frameworks (HtmlTags or MVC2 Templates) can be extended to support the abstraction we produced, but HtmlTags is geared to do this in a much cleaner and flexible fashion. We created the idea of an edit template that includes the label and input (and validation if you so desire), all of which gets nested in some container tag. We rolled all of this up into two conventions: EditTemplateFor and DisplayTemplateFor. The difference being whether or not the field on the form is editable. I’ll admit that we could probably roll this up further into a TemplateFor convention that has configurable builders much like DisplayFor/InputFor/LabelFor in HtmlTags, but right now these are static conventions in that they don’t have hot swappable builders. That is an effort for another day :)

So here is what the views would look like now (sigh of relief):

<%: Html.EditTemplateFor(m => m.Title) %>
<%: Html.EditTemplateFor(m => m.Price) %>
<%: Html.EditTemplateFor(m => m.AlbumArtUrl)%>
<%: Html.EditTemplateFor(m => m.Artist)%>
<%: Html.EditTemplateFor(m => m.Genre)%>

!{Html.EditTemplateFor(m => m.Quantity)}
!{Html.EditTemplateFor(m => m.Price)}

How it works

The following is the EditeTemplateFor convention, the display version only varies by calling DisplayFor instead of InputFor. We simply build our label, adding a class that is statically configurable. Then we take a list of fields, in the event we have more than one falling under a single label, and for each we wrap it in a div with the class "field" and then we put the label and inputs into a div with the class "fields."
public static class TemplateHelpers
{
  public static string FieldDivClass = "field";
  public static string FieldsDivClass = "fields";
  public static string LabelClass = "label";

  public static HtmlTag EditTemplateFor<T>(this T model, params Expression<Func<T, object>>[] fieldSelectors) where T : class
  {
    Contract.Requires(fieldSelectors.Any());

    var label = model.LabelFor(fieldSelectors[0]).AddClass(LabelClass);

    var fields = fieldSelectors
      .Select(f => Tags.Div
              .AddClass(FieldDivClass)
              .Nest(model.InputFor(f)));

    return Tags.Div
      .AddClass(FieldsDivClass)
      .Nest(label)
      .Nest(fields.ToArray());      
  }

  public static HtmlTag EditTemplateFor<T>(this HtmlHelper<T> helper,
                       params Expression<Func<T, object>>[] fieldSelectors) where T : class
  {
    return EditTemplateFor(helper.ViewData.Model, fieldSelectors);
  }
Here is an alteration of the above to work with the MVC Music store ablum editor:
public static class TemplateHelpers
{
  public static string FieldDivClass = "field";
  public static string FieldsDivClass = "fields";
  public static string LabelClass = "label";
  public static string ValidationMessageClass = "validation-message";

  public static HtmlTag EditTemplateFor<T>(this HtmlHelper helper, Expression<Func<T, object>> field) where T : class    {
    var label = helper.LabelFor(field);
    var input = helper.InputFor(field);
    var validation = Tags.Span.AddClass(ValidationMessageClass)

    return Tags.Paragraph
      .Nest(label,field,validation);      
  }
}

Reproducing this with MVC2 Templates

 public static MvcHtmlString EditTemplateFor<T>(this HtmlHelper<T> helper, Expression<Func<T, object>> field) where T : class
  {
    var label = helper.LabelFor(field);
    var input = helper.EditorFor(field);
    var validation = helper.ValidationMessageFor(field);

    var template = String.Format("<p>{0}{1}{2}</p>", label, input, validation);
    return MvcHtmlString.Create(template);
  }

  public static MvcHtmlString EditTemplateFor2<T>(this HtmlHelper<T> helper, Expression<Func<T, object>> field) where T : class
  {
    var label = helper.LabelFor(field);
    var input = helper.EditorFor(field);

    var template = String.Format("<div class=\"fields\">{0}<div class=\"field\">{1}</div></div>", label, input);
    return MvcHtmlString.Create(template);
  }

The Verdict

This was my best attempt at producing the same behavior with ASP.Net MVC templates, and it's already run into some serious issues.

  1. I have to work with strings.
    1. Html is not a string, so why should I have to work with it as such.
    2. The MVC2 template return an MvcHtmlString unlike HtmlTags which returns HtmlTag
    3. I could improve this with an HtmlTextWriter but that’s not part of the template abstraction and is yet another burden to include. HtmlTags supports a model OOB!
  2. I cannot modify my label to add the class “label”. Instead, I would have to wrap the label with another tag to get a class applied or I would have to alter my label template and then that class would be applied anytime I used a label in my entire application, not just in a form field.
  3. The HtmlTags version is much more readable, maintainable and representative of the structure of the html.
  4. I cannot plug this into the scaffolding in ASP.Net MVC with EditorForModel, this is the failure in their approach to scaffolding, since it’s built on an unchangeable model with no configuration and modification, it only works for the narrow out of the box case (smells of web forms days if you ask me). In a subsequent blog post I’ll show how we can continue to build up, instead of top down, to produce flexible scaffolding on top of HtmlTags!
  5. I can literally “unit” test the HtmlTags version (if it added value to test it) without spinning up a view engine to render the result, and I don’t have to parse html!

-Wes

7.23.2010

What we are doing with HtmlTags Part 1 : Why HtmlTags?

Back in January, Jeremy Miller posted a nice article on HtmlTags: Shrink your Views with FubuMVC Html Conventions. We were immediately in love with the idea and have spent several months adapting the conventions to work with our ASP.Net MVC applications. I was having a conversation with Ryan recently, reflecting on how far we’ve come and how we had no vision of that when we first read that article. I want to share some of that, so I will be working on a series of blog posts to show “What we are doing with HtmlTags.” But first: why?

A lot of buzz has been generated around reusability and views, particularly html. Html being a highly compositional language, but lacking any ability for reuse, has led to copy paste hell. Several different philosophies are employed in View Engines (VE) to try to address this issue and recently MVC2 was released with it’s templated helpers. The problem with reusing html directly is that lacks the benefits of separating the concern of what we need from the html (building it) and generating it, classically, the difference between “What” I want versus “How” I get it. The power of the Linq abstraction shows the benefits of separating “What” versus “How.” HtmlTags is another model based approach that separates the “What” and “How.” It goes even further by allowing the “How” to be configured via conventions. Here is a list of the benefits this separation has brought to our team:

  1. View engine agnostic. HtmlTags is not coupled to any particular VE. It can even be returned from a controller action and rendered directly to the response!
  2. Testability with out spinning up a costly integration environment, I can write assertions directly against the model!
    1. Html is a language that lacks the ability to query it in any reasonable fashion.
    2. We integrate security as an aspect to our conventions that build our Html. Now, we can write a simple test to ensure that the security shows/hides elements in the model by simply asserting the lack of their presence! Try parsing html to do this after you spin up an integration test to access the view output!
  3. Translation: HtmlTags is a model that is easily traversed. We leverage this to transform the html model into an export model that can then be translated to csv, pdf and other formats. Now we get exports for free on any view result simply by writing the guts of the view in HtmlTags!
  4. The ability to translate from our data model to the html model directly, in c#, instead of using a view engine with a mish mash of concerns (html & code).
    1. This avoids reshaping of our data to get it to easily work with our view engine. Instead we can translate directly, on our view models, and have the full power of c# at our finger tips.
    2. This also helps with refactoring, where the story is still very poor with VE content.
    3. Simply put: avoids the html / code disparity
  5. Composable conventions, we build upon the HtmlTags DisplayFor, LabelFor and InputFor to build up higher level concepts in our application, like a form field (composed of fields for a label, input, validation etc). This gives us a on stop spot to restructure the html for every form in our application, no more shotgun surgery on views!
    1. We also leverage concepts around menus, buttons, forms etc.
  6. Reusable across projects: we don’t have to try to copy/paste a bunch of templates, we simply drop in a dll with default conventions and configure from there.
  7. Avoid fat fingering html tag names, attributes etc. We don’t have to think as much about html anymore, amazing concept! Templated html still has issues with fat fingering as <div> is all over whereas we have a nice Tags.Div static entry point to generate a div tag. We do this for all tags we use. This is the concept of reuse between templates that is rather lacking from MVC2 templates without bee sting hell. This is a great example of leveraging a statically typed language to fail fast, we don’t have to render a few to find out if we fat fingered some html. All html tags, attributes etc are kept as constants collections.
  8. Avoid hard coded css classes: we make our class names configurable and put them right with the builder so any project can override them. Try doing that without a bunch of bee stings in a templated helper and a nightmare of classes coupled to the template to provide the constants in code. This reminds me of code behind hell from web forms days. Then try reusing that across projects.
    1. I’ve seen some really scary stuff in samples with templated helpers where people try to access attributes. property types, etc and conditionally render sections. The only alternative is to put that code into helpers that are separated from the view and lead to a coupling mess when trying to reuse the templates across projects.

The next set of posts will cover examples of how we are using HtmlTags and how it’s paying dividends.

-Wes