ASP.NET MVC 3 Custom HTML Helpers- Best Practices/Uses
Asked Answered
G

3

33

New to MVC and have been running through the tutorials on the asp.net website.

They include an example of a custom html helper to truncate long text displayed in a table.

Just wondering what other solutions people have come up with using HTML helpers and if there are any best practices or things to avoid when creating/using them.

As an example, I was considering writing a custom helper to format dates that I need to display in various places, but am now concerned that there may be a more elegant solution(I.E. DataAnnotations in my models)

Any thoughts?

EDIT:

Another potential use I just thought of...String concatenation. A custom helper could take in a userID as input and return a Users full name... The result could be some form of (Title) (First) (Middle) (Last) depending on which of those fields are available. Just a thought, I have not tried anything like this yet.

Greatgrandaunt answered 26/1, 2011 at 13:0 Comment(1)
I am still interested in seeing any other interesting things people have done with Custom HTML Helpers. Feel free to post them!Greatgrandaunt
I
16

Well in the case of formatting the DisplayFormat attribute could be a nice solution:

[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}")]
public DateTime Date { get; set; }

and then simply:

@Html.DisplayFor(x => x.Date)

As far as truncating string is concerned a custom HTML helper is a good solution.


UPDATE:

Concerning your EDIT, a custom HTML helper might work in this situation but there's also an alternative approach which I like very much: view models. So if in this particular view you are always going to show the concatenation of the names then you could define a view model:

public class PersonViewModel
{
    public string FullName { get; set; }
}

Now the controller is going to query the repository to fetch the model and then map this model to a view model which will be passed to the view so that the view could simply @Html.DisplayFor(x => x.FullName). The mapping between models and view models could be simplified with frameworks like AutoMapper.

If answered 26/1, 2011 at 13:4 Comment(1)
very interesting. Almost seems like an custom html helper is a last resort.Greatgrandaunt
M
101

I use HtmlHelpers all the time, most commonly to encapsulate the generation of boilerplate HTML, in case I change my mind. I've had such helpers as:

  • Html.BodyId(): generates a conventional body id tag for referencing when adding custom css for a view.
  • Html.SubmitButton(string): generates either an input[type=submit] or button[type=submit] element, depending on how I want to style the buttons.
  • Html.Pager(IPagedList): For generating paging controls from a paged list model.
  • etc....

One of my favorite uses for HtmlHelpers is to DRY up common form markup. Usually, I have a container div for a form line, one div for the label, and one label for the input, validation messages, hint text, etc. Ultimately, this could end up being a lot of boilerplate html tags. An example of how I have handled this follows:

public static MvcHtmlString FormLineDropDownListFor<TModel, TProperty>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TProperty>> expression, IEnumerable<SelectListItem> selectList, string labelText = null, string customHelpText = null, object htmlAttributes = null)
{
    return FormLine(
        helper.LabelFor(expression, labelText).ToString() +
        helper.HelpTextFor(expression, customHelpText),
        helper.DropDownListFor(expression, selectList, htmlAttributes).ToString() +
        helper.ValidationMessageFor(expression));
}

public static MvcHtmlString FormLineEditorFor<TModel, TProperty>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TProperty>> expression, string templateName = null, string labelText = null, string customHelpText = null, object htmlAttributes = null)
{
    return FormLine(
        helper.LabelFor(expression, labelText).ToString() +
        helper.HelpTextFor(expression, customHelpText),
        helper.EditorFor(expression, templateName, htmlAttributes).ToString() +
        helper.ValidationMessageFor(expression));
}

private static MvcHtmlString FormLine(string labelContent, string fieldContent, object htmlAttributes = null)
{
    var editorLabel = new TagBuilder("div");
    editorLabel.AddCssClass("editor-label");
    editorLabel.InnerHtml += labelContent;

    var editorField = new TagBuilder("div");
    editorField.AddCssClass("editor-field");
    editorField.InnerHtml += fieldContent;

    var container = new TagBuilder("div");
    if (htmlAttributes != null)
        container.MergeAttributes(new RouteValueDictionary(htmlAttributes));
    container.AddCssClass("form-line");
    container.InnerHtml += editorLabel;
    container.InnerHtml += editorField;

    return MvcHtmlString.Create(container.ToString());
}

public static MvcHtmlString HelpTextFor<TModel, TProperty>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TProperty>> expression, string customText = null)
{
    // Can do all sorts of things here -- eg: reflect over attributes and add hints, etc...
}    

Once you've done this, though, you can output form lines like this:

<%: Html.FormLineEditorFor(model => model.Property1) %>
<%: Html.FormLineEditorFor(model => model.Property2) %>
<%: Html.FormLineEditorFor(model => model.Property3) %>

... and BAM, all your labels, inputs, hints, and validation messages are on your page. Again, you can use attributes on your models and reflect over them to get really smart and DRY. And of course this would be a waste of time if you can't standardize on your form design. However, for simple cases, where css can supply all the customization you need, it works grrrrrrrrreat!

Moral of the story -- HtmlHelpers can insulate you from global design changes wrecking hand crafted markup in view after view. I like them. But you can go overboard, and sometimes partial views are better than coded helpers. A general rule of thumb I use for deciding between helper vs. partial view: If the chunk of HTML requires a lot of conditional logic or coding trickery, I use a helper (put code where code should be); if not, if I am just outputting common markup without much logic, I use a partial view (put markup where markup should be).

Hope this gives you some ideas!

Memphian answered 27/1, 2011 at 0:15 Comment(2)
Great post! I'm definitely copy/pasting some from here if you don't mind ;)Outbuilding
Great answer. Took me a little while to figure out I needed using System.Web.Mvc.Html; to get the .EditorFor extensions to show up thoughIntendment
I
16

Well in the case of formatting the DisplayFormat attribute could be a nice solution:

[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}")]
public DateTime Date { get; set; }

and then simply:

@Html.DisplayFor(x => x.Date)

As far as truncating string is concerned a custom HTML helper is a good solution.


UPDATE:

Concerning your EDIT, a custom HTML helper might work in this situation but there's also an alternative approach which I like very much: view models. So if in this particular view you are always going to show the concatenation of the names then you could define a view model:

public class PersonViewModel
{
    public string FullName { get; set; }
}

Now the controller is going to query the repository to fetch the model and then map this model to a view model which will be passed to the view so that the view could simply @Html.DisplayFor(x => x.FullName). The mapping between models and view models could be simplified with frameworks like AutoMapper.

If answered 26/1, 2011 at 13:4 Comment(1)
very interesting. Almost seems like an custom html helper is a last resort.Greatgrandaunt
E
0
public static HtmlString OwnControlName<T, U>(this HtmlHelper<T> helper, Expression<Func<T, U>> expression, string label_Name = "", string label_Title = "", Attr attr = null)
        {
            TemplateBuilder tb = null;
            string template = null;
          if (expression == null) throw new ArgumentException("expression");
 obj = helper.ViewData.Model;
                tb.Build(obj, expression.Body as MemberExpression, typeof(T), new SimpleTemplate(new TextArea()), label_Name, label_Title, attr);
                template = tb.Get();
 return new MvcHtmlString(template);
}
Erosive answered 5/7, 2018 at 10:32 Comment(1)
Don't just post code only answers. It might be useful for op but not for future readers. I suggest you should add some explanation how your solution is going to work.Foregoing

© 2022 - 2024 — McMap. All rights reserved.