Encapsulate Multiple HTML Helpers to Make Views More DRY

MVC 2 is adding many new features that make views more elegant to write. For example, the new EditorFor() Html helper returns the appropriate HTML input elements for a given property so you don't have to explicitly specify text box, dropdown, or whatever control you need and can even use the [UIHint] attribute to specify the editor you want to use (either an OOTB or custom editor). This results in view markup that often looks like this:

  
<p>
    <%=Html.LabelFor(m => m.FirstName)%>
    <%=Html.EditorFor(m => m.FirstName)%>
    <%=Html.ValidationMessageFor(m => m.FirstName)%>
</p>
<p>
    <%=Html.LabelFor(m => m.LastName)%>
    <%=Html.EditorFor(m => m.LastName)%>
    <%=Html.ValidationMessageFor(m => m.LastName)%>
</p>

This allows us to simply use attributes to specify meta-data on our view models like this:

  
public class Person  
{
    [DisplayName("First Name")]
    [Required(ErrorMessage = "First Name is required.")]
    [StringLength(50, ErrorMessage = "Last name must be less than 50 characters.")]
    public string FirstName { get; set; }
 
    [DisplayName("Last Name")]
    [Required(ErrorMessage = "Last Name is required.")]
    [StringLength(50, ErrorMessage = "Last name must be less than 50 characters.")]
    public string LastName { get; set; }
}

For example, the [DisplayName] attribute used on line #3 and line #8 above will be honored by the LabelFor() html helper method. This new functionality of MVC 2 has been blogged about quite a bit to date and provides great improvements over MVC 1. In summary these improvements are:

  • Display Names with LabelFor() – Ability to specify display name on our view models meta data and not have to worry about specifying it in the views.
  • HTML input with EditorFor() - Ability to simply use EditorFor() in the view and allow MVC to pick the appropriate editor either automatically or by honoring the [UIHint] attribute as part of meta data in our view models.
  • Validation with ValidationFor() - Ability for the validation to honor DataAnnotations attributes and have this all "baked in" including the actual validation execution itself (including client-side valdiation).

While this is not an exhaustive list for the improvements in MVC 2, the common theme in all this is that, in the most typical cases, each view model property has 3 components: 1) the label, 2) the editor, and 3) validation.  Looking at the first code sample above, I'm calling the same three HTML helper methods for every view model property (i.e., LabelFor(), EditorFor(), and ValidationMessageFor()). The only thing different between them is the view model property being used for the lambda expressions.  For a single property, the same lambda expression is being used for all three HTML helpers. This doesn't make our views very DRY. If we were doing this sort of thing in C#, we'd create a method to encapsulate the repetition and there's no reason we shouldn't do same thing for the code in our views. Therefore, we can create a simple HTML helper that encapsulates these like this:

  
public static MvcHtmlString ValidatedEditorFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression)  
{
    var html =
        htmlHelper.LabelFor(expression).ToString() +
        htmlHelper.EditorFor(expression).ToString() +
        htmlHelper.ValidationMessageFor(expression).ToString();
 
    return MvcHtmlString.Create(html);
}

This allows us to now change our original view code to this while still maintaining the same HTML markup that is generated:

  
<p>
    <%=Html.ValidatedEditorFor(m => m.FirstName)%>
</p>
<p>
    <%=Html.ValidatedEditorFor(m => m.LastName)%>
</p>

Also, if you're using MVC 2 with ASP.NET 4 instead of ASP.NET 3.5, you can take advantage of the new <%: %> syntax rather than the old <%= %> syntax.

  
<p>
    <%:Html.ValidatedEditorFor(m => m.FirstName)%>
</p>
<p>
    <%:Html.ValidatedEditorFor(m => m.LastName)%>
</p>

Now the views are extremely simple and with a single HTML helper we'll automatically get a label, an editor, and validation. Also notice that the HTML helper is using MvcHtmlString rather than just a string as all HTML helpers in MVC 2 are now HTML encoded by default.

The snippet above is only showing two view model properties but if your view has 10, 20, or even 100 or more properties, the difference becomes even more dramatic since the code is only one-third the size. Additionally, the trend you see is that more and more of your development paradigm is being moved away from the views and into your C# view models. This allows for dramatically cleaner/simpler views while also enabling the overall code base to be more testable and less error prone.