The title is quite vague, but this is the best I can call this post. Bear with me, I’ll explain what I mean.

While working with Asp.Net MVC, in Razor views I use Html helpers a lot. My view end up looking like this:

@Html.TextBoxFor(m => m.Title)
@Html.TextAreaFor(m => m.Description)

And later if I write JavaScript and need to reference one of the input fields, I usually do this:

var title = document.getElementById("Title");
var description = document.getElementById("Description");

or if you use jQuery you go like this:

var title = $("#Title");
var description = $("#Description");

This is fine and works. Until you start renaming field names in your model. And then you need to track down where you hard-coded the input id. And many times these references slip away (they do from me!) and remain unchanged.

Would it not be great if these changed could be picked up automatically? or even better, do some sort of strongly-typed reference to the element if it is used in JavaScript?

Just use @Html.IdFor(m => m.Name)

And your JS code would look like this:

var title = document.getElementById("@Html.IdFor(m => m.Title)");
var description = $("#@Html.IdFor(m => m.Description)");

Below I’ve re-invented the wheel. [Facepalm]

So I trolled through MVC source code and figured out how it generates id’s for elements and here are my findings:

using System;
using System.Linq.Expressions;
using System.Web.Mvc;

public static class HtmlHelpers
{
    public static String ElementId<T, TResult>(this HtmlHelper<T> htmlHelper, Expression<Func<T, TResult>> selector)
    {
        var text = System.Web.Mvc.ExpressionHelper.GetExpressionText(selector);
        var fullName = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(text);
        var id = TagBuilder.CreateSanitizedId(fullName);

        return id;
    }
}

As you can see, I have not made any custom code, all components are from MVC and are applied in the same order as MVC does when it generates id for elements when you call for @Html.EditorFor(). So this guarantees the provided id from this function will match the id in your form.

In the view you use it like this:

@Html.ElementId(m => m.Title)

And your JS code would look like this:

var title = document.getElementById("@Html.ElementId(m => m.Title)");
var description = document.getElementById("@Html.ElementId(m => m.Description)");

A bit of mouthful, but gives you the safety net of strong types. I have not actually used it widely, so not sure how it’ll work out in the longer run.

  • It is not possible to just use @Html.IdFor(model => model.Name)? :-)

    http://msdn.microsoft.com/en-us/library/hh833709%28v=vs.108%29.aspx

    • Oh, sh…! I’ve just re-invented the wheel! DUH!

      Thank you for pointing this out. I shall update my post.

      • They have a @Html.NameFor(model => model.Name) as well! :-)

        I invested my own as well before noticing this one. ;-)

        • I guess you need to invent you own bicycle before you notice how useful the stuff around you is -)

  • Romias

    And, what about if I have my JS in a different file… I’m not mixing JS with HTML. Any idea on how to go that way?

    • That’s a real bugger to deal with! In the past I’ve done script-tag on the page that only contains id-definitions. And the JS-file works with them. Something like

      var productNameId = ‘@Html.IdFor(m => m.Product.Name)’;

      In you js you work with variable `productNameId`. But I don’t like this one – we have disconnected declaration of element names and the actual scripts. If you know any better way, let me know!