What is the ASP.NET Core equivalent of ViewData.TemplateInfo.GetFullHtmlFieldId("PropertyName")?

Viewed 1792

I'm migrating a web app to .NET Core, and it uses a few Razor editor templates for specific input types (e.g. outputting a date input for any DateTime model properties).

A couple of the templates use the following method to get the ID attribute value, for use elsewhere within the HTML:

ViewData.TemplateInfo.GetFullHtmlFieldId("PropertyName")

However, this method no longer seems to exist in ASP.NET Core.

The GetFullHtmlFieldName method still exists, so it's possible to get the same result (at least for everything I've tested) by doing:

Regex.Replace(ViewData.TemplateInfo.GetFullHtmlFieldName("PropertyName"), @"[\.\[\]]", "_")

But this seems a little untidy to me, not to mention that there could be edge cases which the old method deals with that I don't know about.

Half an hour of googling, reading the .NET Core docs and searching SO hasn't turned up anything useful. The only thing I could find which was remotely relevant was this answer (which just confirms the method is gone).

Does anyone know why GetFullHtmlFieldId no longer exists? Is it just an accidental omission, or is there a newer, better way of getting this now?

2 Answers

I had a look through the ASP.NET Core repository, and it looks like TemplateInfo.GetFullHtmlFieldId() was originally removed due to a static reference to HtmlHelper.

After a lot of digging around (the .NET Core source browser is an amazingly useful tool), I think the solution for my problem is to use IHtmlHelper.GenerateIdFromName. So, this code:

ViewData.TemplateInfo.GetFullHtmlFieldId("PropertyName")

should now be written as:

Html.GenerateIdFromName(ViewData.TemplateInfo.GetFullHtmlFieldName("PropertyName"))

Still not all that tidy (or consistent), but at least this way we're re-using the same internal logic that the framework uses to construct its IDs.

It shouldn't be hard to simulate the old behavior for your needs. Everything leads back to TagBuilder.CreateSanitizedId which exists in .NET Core too, but it changed and the logic down to the path it changed.

TemplateInfo class had the following in System.Web.Mvc:

private string _htmlFieldPrefix;

public string HtmlFieldPrefix
{
    get { return _htmlFieldPrefix ?? String.Empty; }
    set { _htmlFieldPrefix = value; }
}

public string GetFullHtmlFieldId(string partialFieldName)
{
    return HtmlHelper.GenerateIdFromName(GetFullHtmlFieldName(partialFieldName));
}

public string GetFullHtmlFieldName(string partialFieldName)
{
    // This uses "combine and trim" because either or both of these values might be empty
    return (HtmlFieldPrefix + "." + (partialFieldName ?? String.Empty)).Trim('.');
}

Afterwards it uses HtmlHelper to generate the id from the name:

public static string IdAttributeDotReplacement
{
   get { return WebPages.Html.HtmlHelper.IdAttributeDotReplacement; }
   set { WebPages.Html.HtmlHelper.IdAttributeDotReplacement = value; }
}

public static string GenerateIdFromName(string name, string idAttributeDotReplacement)
{
    if (name == null)
    {
        throw new ArgumentNullException("name");
    }

    if (idAttributeDotReplacement == null)
    {
        throw new ArgumentNullException("idAttributeDotReplacement");
    }

    // TagBuilder.CreateSanitizedId returns null for empty strings, return String.Empty instead to avoid breaking change
    if (name.Length == 0)
    {
        return String.Empty;
    }

    return TagBuilder.CreateSanitizedId(name, idAttributeDotReplacement);
}

What needs to be researched and adjusted as per your needs:

  1. IdAttributeDotReplacement property (gets or sets the character that is used to replace the dot (.) in the id attribute of rendered form controls) which also exists in .NET Core in Microsoft.AspNetCore.Mvc.Rendering
  2. TagBuilder.CreateSanitizedId(name, idAttributeDotReplacement) which I can see got modified to TagBuilder.CreateSanitizedId(string name, string invalidCharReplacement);

I don't really know about the method getting to be something else in .NET Core, but I'm looking forward to finding out.

Related