Create a Custom HTML Helper in ASP.NET MVC Using Razor
22nd March, 2018
  • Stefan Pauwels

  • Software Engineer

Make it easier to refactor your Razor view

Are you working in a large MVC.NET project? How do you manage complexity in your Razor views? Web pages often have duplicate HTML code. One way to eliminate these lines is to use partial views. Partial views are useful if the HTML block makes up a larger part of the page. However, if the HTML we want to wrap only contains a few lines, we can use a custom HTML helper. This blog will explain how you can create such a helper in a Razor view!

What are HTML helpers?

An HTML helper allows you to create arbitrary HTML code. The Razor view engine already defines a lot of helpers for you. Examples are:

  • @Html.TextBoxFor(model=>model.name)
  • @Html.ActionLink()

In short, HTML helpers help to keep pages clean and readable by reducing an HTML block of multiple elements into a single line.

Creating a custom HTML helper

When you have a specific use case that isn’t covered by the default HTML helpers, you can create your own helper. Creating your own helper has the advantage that the page is cleaner and more readable. An additional benefit is that we can now write a unit test for this particular HTML helper.

For example, imagine a website that allows a user to upload an image of their bike trip. If the user clicks on the image, a file selector popup shows up. This popup allows the user to upload a new image.

The HTML block without the HTML helper looks like this:

@model BikeWeb.ViewModels.BikeViewModel

<div class="pull-left upload-img-wrapper">
	<label class="upload-img" data-content="Change Image">
		<img class="img-responsive" height="250" src="@Model.ImageSource" width="250"></img>
	</label>
	<input id="ImageName" name="ImageName" style="display:none;" type="file" value="58.jpg" />
</div>
<rest of the page excluded for brevity>

When you hover over the image, it fades out and the green banner at the bottom pops up to hint to the user that he can upload a new image.

   

To simplify the HTML block on both the edit and create-page, we need to create an ImageUpload HTML helper that can be called from our view that creates the HTML block. The image element needs the URL of the image and the label needs a translated text. We get both from the BikeViewModel.

Creating the ImageUpload HTML helper

using BikeWeb.ViewModels;
using System.Text;
using System.Web;
using System.Web.Mvc;
using System.Web.Mvc.Html;

namespace BikeWeb.Extensions
{
 public static class HtmlHelperExtensions
 {
   public static IHtmlString ImageUpload(this HtmlHelper<BikeViewModel> htmlHelper, BikeViewModel viewModel)
   {
     var outerDiv = new TagBuilder("div");
     outerDiv.AddCssClass("pull-left upload-img-wrapper");
     var label = new TagBuilder("label");
     label.AddCssClass("upload-img");
     label.MergeAttribute("data-content", viewModel.ButtonText);

     var image = new TagBuilder("img");
     image.AddCssClass("img-responsive");
     image.MergeAttribute("src", viewModel.imageSource);
     image.MergeAttribute("width", "250");
     image.MergeAttribute("height", "250");

     var textbox = InputExtensions.TextBoxFor(htmlHelper, m => m.ImageName, new { type = "file", style = "display:none" });

     StringBuilder htmlBuilder = new StringBuilder();
     htmlBuilder.Append(label.ToString(TagRenderMode.StartTag));
     htmlBuilder.Append(image.ToString(TagRenderMode.Normal));
     htmlBuilder.Append(label.ToString(TagRenderMode.EndTag));
     htmlBuilder.Append(textbox.ToHtmlString());
     outerDiv.InnerHtml = htmlBuilder.ToString();
     var html = outerDiv.ToString(TagRenderMode.Normal);

     return MvcHtmlString.Create(html);
   }
 }
}

A couple of notes on this code fragment:

  • ImageUpload is a new C# extension method that we created, it is added dynamically to the HtmlHelper class by using the this keyword. This allows us to use ImageUpload in the view.
  • The TagBuilder class is used to create the HTML elements.
  • It was even possible to use the output of another HTML helper, like the TextBoxFor function, by calling it directly from InputExtensions.

Finally, the elements are merged together into a single div and are returned as an HTML encoded string.

Refactoring the view to include the ImageUpload helper

The new view, with the HTML block refactored into an ImageUpload HTML Helper, looks like this:

@model BikeWeb.ViewModels.BikeViewModel
@using BikeWeb.Extensions

@Html.ImageUpload(Model);
<rest of the page excluded for brevity>

After the refactor we went from five lines to one line. This makes the view a lot cleaner, and as an additional bonus the ImageUpload helper can now be reused on other pages too! By storing the HTML code in a single place, we can easily update the HTML implementation and the changes will be reflected on all pages that use the ImageUpload helper.

Some notes on this refactored view:

  • @using BikeWeb.Extensions imports our extension method into this page
  • @Html.ImageUpload calls this extension method

Conclusion

In this blog post we have shown you what an HTML helper is and how you can create one in ASP.NET MVC. More specifically, if you follow the steps above you should now be able to create your own ImageUpload HTML helper. Knowing what an HTML helper is and how to use it, will make it substantially easier for you to refactor your Razor view.