How to use dashes in HTML-5 data-* attributes in ASP.NET MVC

153,121

Solution 1

Update: MVC 3 and newer versions have built-in support for this. See JohnnyO's highly upvoted answer below for recommended solutions.

I do not think there are any immediate helpers for achieving this, but I do have two ideas for you to try:

// 1: pass dictionary instead of anonymous object
<%= Html.ActionLink( "back", "Search",
    new { keyword = Model.Keyword, page = Model.currPage - 1},
    new Dictionary<string,Object> { {"class","prev"}, {"data-details","yada"} } )%>

// 2: pass custom type decorated with descriptor attributes
public class CustomArgs
{
    public CustomArgs( string className, string dataDetails ) { ... }

    [DisplayName("class")]
    public string Class { get; set; }
    [DisplayName("data-details")]
    public string DataDetails { get; set; }
}

<%= Html.ActionLink( "back", "Search",
    new { keyword = Model.Keyword, page = Model.currPage - 1},
    new CustomArgs( "prev", "yada" ) )%>

Just ideas, haven't tested it.

Solution 2

This problem has been addressed in ASP.Net MVC 3. They now automatically convert underscores in html attribute properties to dashes. They got lucky on this one, as underscores are not legal in html attributes, so MVC can confidently imply that you'd like a dash when you use an underscore.

For example:

@Html.TextBoxFor(vm => vm.City, new { data_bind = "foo" })

will render this in MVC 3:

<input data-bind="foo" id="City" name="City" type="text" value="" />

If you're still using an older version of MVC, you can mimic what MVC 3 is doing by creating this static method that I borrowed from MVC3's source code:

public class Foo {
    public static RouteValueDictionary AnonymousObjectToHtmlAttributes(object htmlAttributes) {
        RouteValueDictionary result = new RouteValueDictionary();
        if (htmlAttributes != null) {
            foreach (System.ComponentModel.PropertyDescriptor property in System.ComponentModel.TypeDescriptor.GetProperties(htmlAttributes)) {
                result.Add(property.Name.Replace('_', '-'), property.GetValue(htmlAttributes));
            }
        }
        return result;
    }
}

And then you can use it like this:

<%: Html.TextBoxFor(vm => vm.City, Foo.AnonymousObjectToHtmlAttributes(new { data_bind = "foo" })) %>

and this will render the correct data-* attribute:

<input data-bind="foo" id="City" name="City" type="text" value="" />

Solution 3

It's even easier than everything suggested above. Data attributes in MVC which include dashes (-) are catered for with the use of underscore (_).

<%= Html.ActionLink("« Previous", "Search",
 new { keyword = Model.Keyword, page = Model.currPage - 1},
 new { @class = "prev", data_details = "Some Details"   })%>

I see JohnnyO already mentioned this.

Solution 4

In mvc 4 Could be rendered with Underscore(" _ ")

Razor:

@Html.ActionLink("Vote", "#", new { id = item.FileId, }, new { @class = "votes", data_fid = item.FileId, data_jid = item.JudgeID, })

Rendered Html

<a class="votes" data-fid="18587" data-jid="9" href="/Home/%23/18587">Vote</a>

Solution 5

You can implement this with a new Html helper extension function which will then be used similarly to the existing ActionLinks.

public static MvcHtmlString ActionLinkHtml5Data(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName, object routeValues, object htmlAttributes, object htmlDataAttributes)
{
    if (string.IsNullOrEmpty(linkText))
    {
        throw new ArgumentException(string.Empty, "linkText");
    }

    var html = new RouteValueDictionary(htmlAttributes);
    var data = new RouteValueDictionary(htmlDataAttributes);

    foreach (var attributes in data)
    {
        html.Add(string.Format("data-{0}", attributes.Key), attributes.Value);
    }

    return MvcHtmlString.Create(HtmlHelper.GenerateLink(htmlHelper.ViewContext.RequestContext, htmlHelper.RouteCollection, linkText, null, actionName, controllerName, new RouteValueDictionary(routeValues), html));
}

And you call it like so ...

<%: Html.ActionLinkHtml5Data("link display", "Action", "Controller", new { id = Model.Id }, new { @class="link" }, new { extra = "some extra info" })  %>

Simples :-)

edit

bit more of a write up here

Share:
153,121
Shameem
Author by

Shameem

Created 10+ iOS and Android apps.

Updated on July 09, 2020

Comments

  • Shameem
    Shameem almost 4 years

    I am trying to use HTML5 data- attributes in my ASP.NET MVC 1 project. (I am a C# and ASP.NET MVC newbie.)

     <%= Html.ActionLink("« Previous", "Search",
         new { keyword = Model.Keyword, page = Model.currPage - 1},
         new { @class = "prev", data-details = "Some Details"   })%>
    

    The "data-details" in the above htmlAttributes give the following error:

     CS0746: Invalid anonymous type member declarator. Anonymous type members 
      must be declared with a member assignment, simple name or member access.
    

    It works when I use data_details, but I guess it need to be starting with "data-" as per the spec.

    My questions:

    • Is there any way to get this working and use HTML5 data attributes with Html.ActionLink or similar Html helpers ?
    • Is there any other alternative mechanism to attach custom data to an element? This data is to be processed later by JS.
  • Ondrej Stastny
    Ondrej Stastny about 14 years
    Hi. If you want to use the 1st approach, just make sure that your dictionary is of type Dictionary<String, Object>.
  • Curtis Buys
    Curtis Buys over 13 years
    While this technically works, the recommended way (starting with MVC 3) is to use an underscore in place of the hyphen (as JohnnyO pointed out).
  • Simon Hartcher
    Simon Hartcher almost 13 years
    This doesn't work for me for some reason. View source shows data_*. Using MVC3. Any ideas?
  • Johnny Oshika
    Johnny Oshika almost 13 years
    Hi Simon, did you solve your problem? If not, can you provide your code that's causing you the problem?
  • Rubens Mariuzzo
    Rubens Mariuzzo over 11 years
    Confusing the whole world with this chosen answer until noticing the real answer above!
  • Rubens Mariuzzo
    Rubens Mariuzzo over 11 years
    Still no luck with WebGrid.GetHtml(htmlAttributes: new { data_some : "thing" }). :'(
  • Umar Farooq Khawaja
    Umar Farooq Khawaja over 11 years
    +1 for specifying why underscores would work. It didn't occur to me that underscores were not valid in attribute names for HTML!
  • Keith
    Keith about 11 years
    @RubensMariuzzo this isn't baked in to the RouteValueDictionary but into MVC3's Html.Something() methods. It's possible that WebGrid hasn't been upgraded in the same way, or you could check the version on the System.Web.Helpers.dll
  • Nikkelmann
    Nikkelmann about 11 years
    The method responsible for this is: HtmlHelper.AnonymousObjectToHtmlAttributes(object), in case anyone is wondering why their custom extension won't replace the underscore with an hyphen.
  • Dan Diplo
    Dan Diplo almost 11 years
    This really needs voting up as it's by far the simplest answer and works perfectly!
  • mlhDev
    mlhDev over 10 years
    @Keith - I agree with you on where the magic's at (by experience); do you know where that is documented? Reflector?
  • Keith
    Keith over 10 years
    @Matthew I'm afraid I don't know - I just took the source apart.
  • StriplingWarrior
    StriplingWarrior over 10 years
    This AnonymousObjectToHtmlAttributes method is publicly available as a static method on the HtmlHelper class.
  • Shawn J. Molloy
    Shawn J. Molloy about 10 years
    This is undocumented besides on StackOverflow - this website puts MSDN to shame.
  • hvaughan3
    hvaughan3 over 7 years
    Surely there is a better class name... an extra t on the end at least!
  • Transformer
    Transformer over 6 years
    hi, is there a way to POST the data-fid to the controller, via an Ajax or submit. wondering how to leverage the data. For e.g if I had a data-date in the attribute, How could I post it to the controller/action? Also what is %23 there
  • mzonerz
    mzonerz over 6 years
    Yes use form collection and access it with ID or Name, and %23 is #
  • Oskar Berggren
    Oskar Berggren almost 6 years
    Convenient - but i wish there was a better solution as it breaks simple "find text" in source code files when the same thing is spelt differently depending on being markup, C# or Javascript.