Writing Custom HTML Helpers for ASP.NET MVC

Switching from developing ASP.NET WebForms to MVC might seem awkward at first, but Ed demonstrates why the change needn't be painful, and how best to build a set of tools for rapid MVC development. This article is hosted on Github, so feel free to contribute suggestions, improvements or translations.

As a web forms developer, I found the transition to MVC to be a bit of a shock at first. Without fully understanding the nature of MVC, I found the lack of a Toolbox filled with server controls to be confusing. However, once it became clear that the goal of MVC was to expose HTML markup and give developers full control over what is rendered to the browser, I quickly embraced the idea.

Transitioning from Web Forms

In MVC development, HTML helpers replace the server control, but the similarities aren’t exactly parallel. Whereas web forms and server controls were intended to bring the workflow of desktop forms to the web, MVC’s HTML helpers simply provide a shortcut to writing out raw HTML elements that are frequently used.

The HTML helper, in most cases, is a method that returns a string. When we call a helper in a View using the razor syntax @Html, we are accessing the Html property of the View, which is an instance of the HtmlHelper class.

Writing extensions for the HtmlHelper class will allow us to create our own custom helpers to encapsulate complex HTML markup. Custom helpers also promote the use of reusable code and are unit testable. Custom helpers can be configured by passing values to the constructor, via fluent configuration, strongly typed parameters, or a combination of both, and ultimately return a string.

How to Begin

The first step in writing an HTML helper is finding code within our project that you intend on reusing. For the extent of this article I will be using an alert message as an example. The Alert is a UI element that displays a message which has a default, success, warning, or information style. The Alert element’s markup is simple in construction but gives us an adequate sample for the scope of this article.

1588-Alert-box.jpg

With our code targeted, we’ll examine the alert markup and see how we can break it down and construct reusable pieces of content. The alert is made up of a div container, a message, a close button and can have multiple styles applied to it. If we examine the markup, we can see that there are parts of the element that are static, and parts that can be broken down into options that can be set as parameters.

1588-markup-details.jpg

1588-parameter-breakdown.jpg

In addition to the HTML, the alert will require CSS and JavaScript in order to function. The CSS and JavaScript are beyond the scope of this article but are included in the final example at the end of this article.

Writing a Specification

Personally, once I have my HTML defined, I prefer to start writing a specification before I do anything else. Writing a specification isn’t a necessary step for creating a custom HTML helper, but it gives me a guide to how the HTML helper will function and what the desired syntax will be. A spec will also promote the use of semantic code, which improves discoverability for others that may be using the helper.

Using a text file, I spec out how the helper will function and, since I will be unit testing the code, I’ll save the spec to my test project. There are several parameters that will be required to configure the Alert element: the text to be displayed, the style of the alert, and a close button that can be toggled. I’ll also be giving the Alert a default configuration that will only require that the text parameter be set. In addition to the elements configuration, custom attributes will be configurable through the htmlAttributes parameter as an anonymous object. Each configuration of the Alert helper is written out in the spec so it can be followed during implementation.

Unit Testing

ASP.NET MVC is highly regarded for its ability to be unit tested, and custom HTML helpers can be thoroughly tested too. With the right setup, unit testing your custom helper isn’t difficult:

First, we need to create an instance of the HtmlHelper class so our extension method can be tested. Next, the custom method is called, and finally we can check the results against our expectations.

So, before we can write our test, we will need to create an instance of the HtmlHelper class. However, the HtmlHelper class has no default constructor, so a little work must be done up front to get an instance. To create an instance of HtmlHelper, we must specify a context and view data container – for the scope of this article, fakes will do just fine. Since each test will require an instance of HtmlHelper, I’ve created an HtmlHelperFactory class to create the instances.

Now that the HtmlHelperFactory is available, getting an instance of HtmlHelper is as simple as calling HtmlHelperFactory.Create().

Using the first spec, I’ll create a unit test for the default alert. In this test, the Alert method will be called, and should return the HTML markup we defined, the message specified, with no additional style, and a visible close button.

First, we arrange our expected output and create an instance of HtmlHelper:

At this point, the method has not been defined yet, so we’ll use what has been defined in the spec to guide us:

Finally, we check our results with the expected output using Assert.AreEqual:

So now the first unit test is written, but before it can be put to use the Alert method must be created. This test-first approach ensures that the custom Helper we write gives us the result we defined as HTML in our spec and, as each spec is fulfilled, this process will be repeated until all the specs are completed and satisfied.

Basic Implementation

Before creating our implementation, there are a few things we should know about MVC and the HtmlHelper class. The HtmlHelper class provides methods that help you create HTML controls programmatically; all HtmlHelper methods generate HTML and return the result as a string.

We’ll begin by creating a new class and implementing the IHtmlString interface – this provides the ToHtmlString method, which is used by MVC to render the control to the View. Next we override the ToString method of our class; the ToString and ToHtmlString methods will return the same result, which is a common practice for HtmlHelpers.

Now that we have an HtmlHelper class, we need to be able to call it from MVC. We’ll do this by writing an extension method that returns our custom HtmlHelper class:

At this point a complete scaffold of our code is complete, and our unit test should execute but fail to pass.

To finish our basic implementation and pass the unit test, we’ll need to set up our parameters and render the HTML. MVC provides the TagBuilder class for building HTML, which we’ll use to build our render method:

The HTML helper should now pass the unit test.

With the basic implementation complete, we can easily expand on the HTML helper by adding additional options. Following our spec, we’ll add the option to change the style of the alert; once again, we start with a unit test and then modify our code to complete the test:

Finally, we’ll make our helper more flexible by giving the end user the ability to define additional HTML attributes. The TagBuilder‘s MergeAttributes method adds a specified attribute to the tag being rendered. In addition to the MergeAttributes method, the HtmlHelper AnonymousObjectToHtmlAttributes is used to allow an anonymous object to be used to define additional parameters:

The basic implementation is now complete, and the custom helper can be referernced and called in any ASP.NET MVC project. Using the synax outlined in the spec, an Alert can easily be placed anywhere in our project:

Fluent Configuration

Next, we’ll build upon the basic HTML helper we have created thus far, with the goal of adding a Fluent API configuration to our existing Alert helper. The Fluent API won’t add functionality to the helper – instead we will be simplifying the syntax used for setting options on our helper; some refer to this as adding syntactic sugar.

Taking a look at the basic implementation of the Alert helper, we can see that there are several options that can be set when calling the Alert helper:

Instead of using the constructor as the only manner of setting options on our helper, we will guide the user of our helper through the options. In addition to making options simpler to set, we will also be making our code easier to read. The end result should be a syntax which resembles the spoken intent of its use:

Designing a Fluent API requires some planning; again, I prefer to write a specification of how I intend the code to function. Using the specification from earlier I’ll plan out the code and show the expected results.

For the Alert helper, we’ll be eliminating the need to specify the alert style as an enumerator and instead just call a method that defines the style by name. In this example we’ll use interfaces to chain our methods together so the user is guided through the options. In addition to guiding the user, we can control what options are available as well – for example, when the style is set, only the HideCloseButton and Attributes methods will be available.

To create the API we’ll need to define an interface for setting the style, and a second interface for setting the remaining options. Our Alert helper will implement both interfaces.

The IAlertBox interface outlines three method signatures – Success, Warning, and Info – which will all return the IAlertBoxFluentOptions interface. We will use these methods to set our alert style:

The IAlertBoxFluentOptions interface outlines the rest of the options for the Alert. The method signatures HideCloseButton and Attributes also return IAlertBoxFluentOptions, which will prevent the style option from being set again.

Now that we have defined our interfaces, we will need to create a class which implements IAlertBoxFluentOptions. Because the class will be returned at the end of the method call, it will also be responsible for rendering HTML and should function as an HTML Helper which implements the IHtmlString interface.

The AlertBoxFluentHelper class is essentially extending the functionality of the AlertBox class. The helper needs to be able to call back to the AlertBox we began building to set properties and call the render method, and to accomplish these task we will pass an instance of AlertBox in the constructor of the AlertFluentHelper. Using the AlertBox referenced as “parent”, we can call the parent’s ToString and ToHtmlString methods and return the results.

To complete the Fluent API we need to implement both IAlertBox and IAlertBoxFluentHelperOptions in the AlertBox class. Each method will be responsible for setting the desired value, and returns a new AlertBoxFluentOptions object passing itself to the constructor.

The Fluent API is now complete and ready to be used.

1588-fluent-api-set-style.jpg

1588-fluent-api-set-attributes.jpg

While the code required for creating a Fluent API may seem unnecessary or overly complex for this example, more complex HTML Helpers could greatly benefit from this type of API. Complexity, frequency of use, and the type of end user your HTML Helper is being designed for are all considerations when for including a Fluent API for your project.

Stronly Typed Helpers

Finally we’ll complete our overview of custom HTML Helpers by learning about strongly typed HTML Helpers. If you are already familiar with MVC, then you have probably seen or used strongly typed HTML Helpers. These helpers use the ElementFor convention, meaning the helper has a For suffix to indicate that it is strongly typed.

To create a strongly typed helper we need to understand what makes them different from regular HTML Helpers. Strongly typed helpers are used to pass data from the Model to the helper using expression syntax. The HtmlHelper class is a strongly typed subclass of the HtmlHelper class.

In addition to HtmlHelper<TModel>, we will be using Expression<Func<T,T>> to collect information about the Model. Expression<Func<T,T>>, which is part of LINQ, represents an expression tree of a delegate which accepts a parameter and returns a result. When writing HTML Helpers, the expression will allow the Helper to accept lambda expressions as parameters. All of the hard work of extracting data from the Model via lambda expression has been done for us in MVC; the ModelMetadata.FromLambdaExpression method will return the Model data based on the expression.

Since we already have the basic Alert Html Helper built, we only need to create additional extension methods and allow them to call the Alert helper:

In usage you get this:

The strongly typed HTML helpers provide better compile time support and benefit from intellisense. Adding this option to your HTML helper extends its usefulness, and gives the end user more flexibility.

Usage

Once the custom helper is complete simply add a refernce to your project and call your helper from the view.

1588-alert-box-usage.jpg

Considerations

HTML helpers are intended to save time by making our code more reusable. As a result, when creating HTML Helpers, you should consider how often the HTML is going to be used and how the code will be used in a project. Even though HTML helpers save time, they are also an investment. Choosing when to create one and what level of configuration is needed should be decided on a per project basis.

If your goal is to simplify a project, then a basic HTML helper will due. If you plan to use the code across multiple projects, or to create a suite of tools, then Fluent APIs and strongly typed variations of your helpers will increase the value to the end user.

In Conclusion

Custom HTML helpers are a powerful tool in MVC, used to encapsulate reusable HTML fragments and, throughout this article, we’ve identified common practices for creating them. I’ve used the custom Alert helper to demonstrate how to create a basic implementation, a Fluent API configuration and the strongly typed convention and the complete project, containing all source code, unit tests and the MVC solution, can be downloaded from GitHub.

1588-Github_contribute.pngThis article is hosted on Github, and we’d love you to be involved if you have suggestions or ideas for enhancements.