ChatGPT解决这个技术问题 Extra ChatGPT

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

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.

this is an old question with outdated answer - users of MVC 3 and above should view this question stackoverflow.com/questions/2897733/…

J
Johnny Oshika

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="" />

This doesn't work for me for some reason. View source shows data_*. Using MVC3. Any ideas?
Hi Simon, did you solve your problem? If not, can you provide your code that's causing you the problem?
Still no luck with WebGrid.GetHtml(htmlAttributes: new { data_some : "thing" }). :'(
+1 for specifying why underscores would work. It didn't occur to me that underscores were not valid in attribute names for HTML!
@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
M
Morten Mertner

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.


Hi. If you want to use the 1st approach, just make sure that your dictionary is of type Dictionary.
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).
Confusing the whole world with this chosen answer until noticing the real answer above!
O
Oliver

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.


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.
This really needs voting up as it's by far the simplest answer and works perfectly!
m
mzonerz

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>

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
Yes use form collection and access it with ID or Name, and %23 is #
W
WestDiscGolf

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


K
Keith Williams

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


S
Sergey Makridenkov

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));
    }
}

Surely there is a better class name... an extra t on the end at least!
M
Micho

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"/>