How to create a Custom HTML Helper in ASP.NET MVC Using Razor?
Make it easier to refactor your Razor view
Are you working on a large MVC.NET project? Want to manage the 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 post 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.
What are the benefits of using HTML helpers?
HTML helpers make it easier to create HTML markup in Razor views, reducing the amount of code that needs to be written. This makes it easier to read and maintain codebases. They also help to ensure that the generated HTML is consistent and valid.
How to create 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.
How can HTML helpers make it easier to refactor Razor views?
By using HTML helpers, it is easier to refactor Razor views as it allows the HTML markup to be encapsulated in one place. This means that if changes are required, they only need to be made in one place. This can save time and reduce the risk of introducing bugs.
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.