Drying up your razor views

Published on den 2 March 2012

We all know DRY is a good principle to follow but it's now always that easy. I will show you technique using templated delegates that keeps your code both flexible and DRY.

The problem

Take a look at this fieldset

<fieldset class="form">
	<legend>Elevinformation</legend>
	<h2>Elevinformation</h2>
	
	<div class="editor-prop">
	    <div class="editor-label">
	        Förnamn
	    </div>
	    <div class="editor-field">
	        @Html.EditorFor(model => model.Dto.FirstName)
	        @Html.ValidationMessageFor(model => model.Dto.FirstName)
	    </div>
	</div>
	<div class="editor-prop">
	    <div class="editor-label">
	        Efternamn
	    </div>
	    <div class="editor-field">
	        @Html.EditorFor(model => model.Dto.LastName)
	        @Html.ValidationMessageFor(model => model.Dto.LastName)
	    </div>
	</div>
	<div class="editor-prop">
	    <div class="editor-label">
	        Startår
	    </div>
	    <div class="editor-field">
	        @Html.EditorFor(model => model.Dto.StartYear)
	        @Html.ValidationMessageFor(model => model.Dto.StartYear)
	    </div>
	</div>
	<div class="editor-prop">
	    <div class="editor-label">
	        Slutår
	    </div>
	    <div class="editor-field">
	        @Html.EditorFor(model => model.Dto.EndYear)
	        @Html.ValidationMessageFor(model => model.Dto.EndYear)
	    </div>
	</div>
	<div class="editor-prop">
	    <div class="editor-label">
	        Email
	    </div>
	    <div class="editor-field">
	        @Html.EditorFor(model => model.Dto.Email)
	        @Html.ValidationMessageFor(model => model.Dto.Email)
	    </div>
	</div>
	<p>
	    <input type="submit" value="Spara" />
	</p>
</fieldset>

This produces the following view:

 ChangeInfoModel

As you can see we repeat parts like this many times.

<div class="editor-prop">
    <div class="editor-label">
        Email
    </div>
    <div class="editor-field">
        @Html.EditorFor(model => model.Dto.Email)
        @Html.ValidationMessageFor(model => model.Dto.Email)
    </div>
</div>

There are two things that bugs me a lot with this.

  1. For a single field I have 6 rows of cermony and 3 rows of content. Sure, the rows with content are more complex but it still sucks because I have to type that. I can template or scaffold it but that is just a workaround IMO.

  2. If I decide to change the html structure of my forms. Well.. in the current situation we better not because we have to do the same change across every field in every form of the whole application. That's like cooking spaghetti straw by straw. Don't do it if you want to stay sane!

The problem is that the label is not always a simple string and that I need to be able to specify the editor from time to time. I need control but I want to encapsulate the layout.

What we want/result

This is not the first time I face similar problems so I already knew pretty well how I wanted it to work. I want to inject the label and the field into a method that renders the whole section. I want it to be easy to use in most cases but able to handle more complex situations too.

Simple case example(Will expand to exactly the same html as above):

@Html.EditorField("Email", model => model.Dto.Email)

More advanced scenario:

@Html.EditorField(@<span class="important">Email</span>, @<text>
	@Html.TextBoxFor(model => model.Dto.Email, new { @class = "email" })
	<span>Place some extra info here for this field</span>
	@Html.ValidationMessageFor(model => model.Dto.Email)
</text>)

I'm not sure how that renders so here is a screenshot to show the real power:

  ChangeInfoModel

Awesome! Real html, no string escaping and stuff. And if you think about you will probably see how flexible that is.

That should give you an idea what the result will be and hopefully why I want this. Here is the whole form using the refactored code.

<fieldset class="form">
	<legend>Elevinformation</legend>
	<h2>Elevinformation</h2>
	@Html.EditorField("Förnamn", model => model.Dto.FirstName)
	@Html.EditorField("Efternamn", model => model.Dto.LastName)
	@Html.EditorField("Startår", model => model.Dto.StartYear)
	@Html.EditorField("Slutår", model => model.Dto.EndYear)
	@Html.EditorField("Email", model => model.Dto.Email)
	<p>
	    <input type="submit" value="Spara" />
	</p>
 </fieldset>

The code 

It turns out that there is a little nice feature in razor that let you define templated delegates. Phil Haack wrote about it.

I'm just going to give you the solution here and then I will explain a few things. To start with I left the commented method in there with hopes that someone can tell me how to not get that one and the last method to "collide" when I try to call them.

public static class TemplateDelegates
{

    ///// <summary>
    ///// Uses and EditorFor the selected property
    ///// </summary>
    //public static HelperResult EditorField<T, TProp>(this HtmlHelper<T> source, Func<T, HelperResult> label, Expression<Func<T, TProp>> property)
    //{
    //    return source.RenderEditorField(w => label(source.ViewData.Model).WriteTo(w), w =>
    //    {
    //        w.WriteLine(source.EditorFor(property));
    //        w.WriteLine(source.ValidationMessageFor(property));
    //    });
    //}

    /// <summary>
    /// Uses and EditorFor the selected property
    /// </summary>
    public static HelperResult EditorField<T, TProp>(this HtmlHelper<T> source, string label, Expression<Func<T, TProp>> property)
    {
        return source.RenderEditorField(w => w.WriteLine(label), w => {
            w.WriteLine(source.EditorFor(property));
            w.WriteLine(source.ValidationMessageFor(property));
        });
    }

    /// <summary>
    /// Gives you full controll of the html
    /// </summary>
    public static HelperResult EditorField<T>(this HtmlHelper<T> source, Func<T, HelperResult> label, Func<T, HelperResult> value)
    {
        return source.RenderEditorField(w => label(source.ViewData.Model).WriteTo(w), w => value(source.ViewData.Model).WriteTo(w));
    }

    private static HelperResult RenderEditorField<T>(this HtmlHelper<T> source, Action<TextWriter> writeLabel, Action<TextWriter> writeValue)
    {
        return new HelperResult(writer =>
        {
            writer.WriteLine("<div class='editor-prop'>");
            writer.WriteLine("\t<div class='editor-label'>");
            writeLabel(writer);
            writer.WriteLine("\t</div>");
            writer.WriteLine("\t<div class='editor-field'>");
            writeValue(writer);
            writer.WriteLine("\t</div>");
            writer.WriteLine("</div>");
        });
    }
}

By taking an Expression<Func<T, TProp>> property we can simply forward that to the EditorFor and ValidationMessageFor. This is the easiest method to use(The first one I showed).

When we need more controll we as for a HelperResult instead which is where templated delegates come in. Read the post by Haacked if you want to know more about that.

Conclusion

With only a few small methods I can now avoid repeating my layout code all day and focus on actually getting things done. And If I ever decide to change the html structure I'm in a much better position to do so because that is encapsulated into that method. If anyone can solve that overload problem for me I would be even happier:)

Then feel free to it or if you have any comments or questions mention @MikaelEliasson on Twitter.

CTO and co-founder at Bokio with a background as an elite athlete. Still doing a lot of sports but more for fun.

#development, #web, #orienteering, #running, #cycling, #boardgames, #personaldevelopment