ASP.NET MVC: Supplying HTML attributes with EditorFor

My colleague Simon Ince and I recently discovered that we had been working on the same problem independently so we combined our efforts and have written a joint blog post on our team blog.

The problem was something that I’d encountered in a number of blog posts that used modified editor templates (jQuery UI datepicker, aria-required and autocomplete). In each of these posts I ended up adding a custom template, and then updating it to output an attribute in the rendered HTML. The thing that bothered me about all of these posts is that they didn’t compose well. For example, if you wanted to combine autocomplete and aria-required then you’d have to merge the changes to the templates. In the blog post, Simon and I walk through a fairly simple way to solve this problem with the introduction of HtmlAttributeProvider.

aria-required

In the remainder of this post I thought I’d show how the previous solutions would look with the new HtmlAttributeProvider. In the joint post we show how the aria-required scenario can be handled simply by adding the following line of code to global.asax:

HtmlAttributeProvider.Register(metadata =>metadata.IsRequired, "aria-required", true);

This simply says that when the model metadata has the IsRequired field set (e.g. if the property on the model has the Required attribute applied) then output an attribute with name “aria-required” and value “true”.

jQuery UI datepicker

In the case of the jQuery UI datepicker the approach is also simplified. In fact, we no longer need to create the Date.cshtml editor template. Instead we can simply add the following line of code to global.asax:

HtmlAttributeProvider.Register(metadata => metadata.DataTypeName == "Date", "class", "date");

This code says that if the DataTypeName is Date (e.g. if you’ve added the DataType(DataType.Date) attribute to the model property) then it should add the “date” class to the element. Note that the attribute provider handles merging attribute values, so this will add the “date” class to the other class values (i.e. it plays nicely with the classes that are added by default)

jQuery UI autocomplete

The autocomplete change requires a little bit more effort, but only a little! To trigger the autocomplete behaviour, we added a “data-autocomplete-url” attribute with the value of the URL that returns the autocomplete data. The generation of the URL uses HtmlHelper, so we need to use the overload of HtmlAttributeProvider.Register that allows us to pass a delegate that takes the HtmlHelper as a parameter:

HtmlAttributeProvider.Register((html, metadata) =>
{
string autocompleteUrl = html.GetAutoCompleteUrl(metadata);
if (!string.IsNullOrEmpty(autocompleteUrl))
{
return new[] { new KeyValuePair<string, object>(
"data-autocomplete-url", autocompleteUrl) };
}
return null;
});

The Register overload here expects a collection of attributes (KeyValuePairs) to be returned. The previous examples could have been written using this overload, but we opted to add a Register overload to simplify that common scenario.

Summary

Whilst it is nice that the previous scenarios have been simplified by the introduction of HtmlAttributeProvider, the real benefit is the composability as you can simply add multiple registrations!