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:

   1:  <p>
   2:      <%=Html.LabelFor(m => m.FirstName)%>
   3:      <%=Html.EditorFor(m => m.FirstName)%>
   4:      <%=Html.ValidationMessageFor(m => m.FirstName)%>
   5:  </p>
   6:  <p>
   7:      <%=Html.LabelFor(m => m.LastName)%>
   8:      <%=Html.EditorFor(m => m.LastName)%>
   9:      <%=Html.ValidationMessageFor(m => m.LastName)%>
  10:  </p>

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

   1:  public class Person
   2:  {
   3:      [DisplayName("First Name")]
   4:      [Required(ErrorMessage = "First Name is required.")]
   5:      [StringLength(50, ErrorMessage = "Last name must be less than 50 characters.")]
   6:      public string FirstName { get; set; }
   8:      [DisplayName("Last Name")]
   9:      [Required(ErrorMessage = "Last Name is required.")]
  10:      [StringLength(50, ErrorMessage = "Last name must be less than 50 characters.")]
  11:      public string LastName { get; set; }
  12:  }

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:

   1:  public static MvcHtmlString ValidatedEditorFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression)
   2:  {
   3:      var html =
   4:          htmlHelper.LabelFor(expression).ToString() +
   5:          htmlHelper.EditorFor(expression).ToString() +
   6:          htmlHelper.ValidationMessageFor(expression).ToString();
   8:      return MvcHtmlString.Create(html);
   9:  }

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

   1:  <p>
   2:      <%=Html.ValidatedEditorFor(m => m.FirstName)%>
   3:  </p>
   4:  <p>
   5:      <%=Html.ValidatedEditorFor(m => m.LastName)%>
   6:  </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.

   1:  <p>
   2:      <%:Html.ValidatedEditorFor(m => m.FirstName)%>
   3:  </p>
   4:  <p>
   5:      <%:Html.ValidatedEditorFor(m => m.LastName)%>
   6:  </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.

posted on Sunday, January 17, 2010 12:52 PM Print
# re: Encapsulate Multiple HTML Helpers to Make Views More DRY
1/17/2010 4:52 PM
I don't like code like this :

[DisplayName("First Name")]

Is there a way to set attributes in code by referencing a file containing text resources. In this example, setting the attribute programmatically allows you to change the label text at runtime rather than compile time
# re: Encapsulate Multiple HTML Helpers to Make Views More DRY
1/17/2010 6:17 PM
@GazNewt - Yep, you can definitely do that. Check out this post on Stackoverflow as an example: In that case, it's a localization example but you could the same thing for regular labels even when you're only supporting one language.

Either way, it's another example of why this approach is so much *better* - because you have the flexibility to do stuff like this - rather than just putting all of this directly in your views.
# re: Encapsulate Multiple HTML Helpers to Make Views More DRY
1/19/2010 12:53 AM
Why just not use Html.EditorForModel()
# re: Encapsulate Multiple HTML Helpers to Make Views More DRY
1/19/2010 7:21 AM
@ali62b - In certain circumstances, you absolutely could use Html.EditorforModel(). The EditorForModel() just spits out the editors for every property in your view but you still have the opportunity to customize certain properties by using the various meta data options that Data Annotations provides you on your view models (e.g., DisplayName, UIHint, etc.). The example in the post above would be for those circumstances where you were *not* using EditorForModel() because it did not provide the customizations that you needed (i.e., HTML or otherwise). That is, any circumstances where you found yourself creating views with an approach that did not include EditorForModel().

Post Comment

Title *
Name *
Comment *  

View Steve Michelotti's profile on LinkedIn

profile for Steve Michelotti at Stack Overflow, Q&A for professional and enthusiast programmers

Google My Blog

Tag Cloud