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

8

339

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.
Songsongbird answered 26/3, 2010 at 0:46 Comment(1)
this is an old question with outdated answer - users of MVC 3 and above should view this question #2898233Sorkin
V
118

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.

Vulcan answered 26/3, 2010 at 1:27 Comment(3)
Hi. If you want to use the 1st approach, just make sure that your dictionary is of type Dictionary<String, Object>.Kellner
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).Oculo
Confusing the whole world with this chosen answer until noticing the real answer above!Darindaring
E
667

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="" />
Expositor answered 23/12, 2010 at 1:8 Comment(10)
This doesn't work for me for some reason. View source shows data_*. Using MVC3. Any ideas?Gangue
Hi Simon, did you solve your problem? If not, can you provide your code that's causing you the problem?Expositor
Still no luck with WebGrid.GetHtml(htmlAttributes: new { data_some : "thing" }). :'(Darindaring
+1 for specifying why underscores would work. It didn't occur to me that underscores were not valid in attribute names for HTML!Troxell
@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.dllAecium
@Aecium - I agree with you on where the magic's at (by experience); do you know where that is documented? Reflector?Godless
@Matthew I'm afraid I don't know - I just took the source apart.Aecium
This AnonymousObjectToHtmlAttributes method is publicly available as a static method on the HtmlHelper class.Carce
This is undocumented besides on StackOverflow - this website puts MSDN to shame.Verdun
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.Doradorado
V
118

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.

Vulcan answered 26/3, 2010 at 1:27 Comment(3)
Hi. If you want to use the 1st approach, just make sure that your dictionary is of type Dictionary<String, Object>.Kellner
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).Oculo
Confusing the whole world with this chosen answer until noticing the real answer above!Darindaring
G
61

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.

Guria answered 6/6, 2012 at 8:14 Comment(2)
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.Demilitarize
This really needs voting up as it's by far the simplest answer and works perfectly!Blackford
C
28

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>
Convulsive answered 24/2, 2016 at 9:40 Comment(2)
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 thereFlann
Yes use form collection and access it with ID or Name, and %23 is #Convulsive
C
4

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

Carrion answered 12/1, 2011 at 21:6 Comment(0)
P
3

I ended up using a normal hyperlink along with Url.Action, as in:

<a href='<%= Url.Action("Show", new { controller = "Browse", id = node.Id }) %>'
  data-nodeId='<%= node.Id %>'>
  <%: node.Name %>
</a>

It's uglier, but you've got a little more control over the a tag, which is sometimes useful in heavily AJAXified sites.

HTH

Psia answered 15/7, 2010 at 18:46 Comment(0)
E
0

I do not like use pure "a" tag, too much typing. So I come with solution. In view it look

<%: Html.ActionLink(node.Name, "Show", "Browse", 
                    Dic.Route("id", node.Id), Dic.New("data-nodeId", node.Id)) %>

Implementation of Dic class

public static class Dic
{
    public static Dictionary<string, object> New(params object[] attrs)
    {
        var res = new Dictionary<string, object>();
        for (var i = 0; i < attrs.Length; i = i + 2)
            res.Add(attrs[i].ToString(), attrs[i + 1]);
        return res;
    }

    public static RouteValueDictionary Route(params object[] attrs)
    {
        return new RouteValueDictionary(Dic.New(attrs));
    }
}
Entree answered 11/12, 2010 at 12:49 Comment(1)
Surely there is a better class name... an extra t on the end at least!Valerio
B
-2

You can use it like this:

In Mvc:

@Html.TextBoxFor(x=>x.Id,new{@data_val_number="10"});

In Html:

<input type="text" name="Id" data_val_number="10"/>
Basipetal answered 8/1, 2016 at 7:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.