Cómo usar guiones en atributos de datos HTML * 5 en ASP.NET MVC

325

Estoy tratando de usar atributos de datos HTML5 en mi proyecto ASP.NET MVC 1. (Soy un novato de C # y ASP.NET MVC).

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

Los "datos-detalles" en los htmlAttributes anteriores dan el siguiente error:

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

Funciona cuando uso data_details, pero supongo que debe comenzar con "data-" según la especificación.

Mis preguntas:

  • ¿Hay alguna manera de hacer que esto funcione y usar atributos de datos HTML5 con Html.ActionLink o ayudantes Html similares?
  • ¿Existe algún otro mecanismo alternativo para adjuntar datos personalizados a un elemento? JS procesará estos datos posteriormente.
Shameem
fuente
55
esta es una pregunta antigua con una respuesta desactualizada: los usuarios de MVC 3 y superiores deben ver esta pregunta stackoverflow.com/questions/2897733/…
ED-209

Respuestas:

115

Actualización: MVC 3 y versiones más recientes tienen soporte incorporado para esto. Vea la respuesta altamente votada de JohnnyO a continuación para obtener soluciones recomendadas.

No creo que haya ayudantes inmediatos para lograr esto, pero tengo dos ideas para que pruebes:

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

Solo ideas, no lo he probado.

Morten Mertner
fuente
55
Hola. Si desea utilizar el primer enfoque, asegúrese de que su diccionario sea del tipo Diccionario <Cadena, Objeto>.
Ondrej Stastny
40
Si bien esto funciona técnicamente, la forma recomendada (comenzando con MVC 3) es usar un guión bajo en lugar del guión (como señaló JohnnyO).
Curtis compra
3
¡Confundir al mundo entero con esta respuesta elegida hasta notar la respuesta real de arriba!
Rubens Mariuzzo
648

Este problema se ha solucionado en ASP.Net MVC 3. Ahora convierten automáticamente guiones bajos en propiedades de atributo html a guiones. Tuvieron suerte con este, ya que los guiones bajos no son legales en los atributos html, por lo que MVC puede implicar con confianza que desea un guión cuando usa un guión bajo.

Por ejemplo:

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

renderizará esto en MVC 3:

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

Si todavía usa una versión anterior de MVC, puede imitar lo que está haciendo MVC 3 creando este método estático que tomé prestado del código fuente de MVC3:

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

Y luego puedes usarlo así:

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

y esto representará el atributo correcto data- *:

<input data-bind="foo" id="City" name="City" type="text" value="" />
Johnny Oshika
fuente
66
Esto no funciona para mí por alguna razón. Ver fuente muestra data_ *. Usando MVC3. ¿Algunas ideas?
Simon Hartcher
Hola Simon, ¿resolviste tu problema? Si no, ¿puede proporcionar su código que le está causando el problema?
Johnny Oshika
Todavía no hay suerte con WebGrid.GetHtml(htmlAttributes: new { data_some : "thing" }). : '(
Rubens Mariuzzo
+1 para especificar por qué los guiones bajos funcionarían. ¡No se me ocurrió que los guiones bajos no fueran válidos en los nombres de atributos para HTML!
Umar Farooq Khawaja
2
@RubensMariuzzo esto no está RouteValueDictionaryincluido en los Html.Something()métodos de MVC3 . Es posible que WebGridno se haya actualizado de la misma manera, o puede consultar la versión en elSystem.Web.Helpers.dll
Keith
58

Es incluso más fácil que todo lo sugerido anteriormente. Los atributos de datos en MVC que incluyen guiones (-) se atienden con el uso de guión bajo (_).

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

Veo que JohnnyO ya mencionó esto.

Oliver
fuente
12
El método responsable de esto es: HtmlHelper.AnonymousObjectToHtmlAttributes (objeto), en caso de que alguien se pregunte por qué su extensión personalizada no reemplazará el guión bajo con un guión.
Nikkelmann
1
¡Esto realmente necesita ser votado ya que es, con mucho, la respuesta más simple y funciona perfectamente!
Dan Diplo
21

En mvc 4 podría representarse con subrayado ("_")

Maquinilla de afeitar:

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

HTML rendido

<a class="votes" data-fid="18587" data-jid="9" href="/Home/%23/18587">Vote</a>
mzonerz
fuente
hola, ¿hay alguna manera de PUBLICAR el data-fid al controlador, a través de un Ajax o enviar? preguntándose cómo aprovechar los datos. Por ejemplo, si tuviera un data-dateatributo, ¿cómo podría publicarlo en el controlador / acción? También lo que es% 23 allí
transformador
Sí, use la recopilación de formularios y acceda con ID o nombre, y% 23 es #
mzonerz
4

Puede implementar esto con una nueva función de extensión auxiliar de HTML que luego se utilizará de manera similar a los ActionLinks existentes.

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

Y lo llamas así ...

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

Simples :-)

editar

un poco más de una escritura aquí

WestDiscGolf
fuente
3

Terminé usando un hipervínculo normal junto con Url.Action, como en:

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

Es más feo, pero tiene un poco más de control sobre la aetiqueta, que a veces es útil en sitios muy AJAXified.

HTH

Keith Williams
fuente
0

No me gusta usar la etiqueta "a" pura, escribir demasiado. Entonces vengo con la solución. A la vista se ve

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

Implementación de la clase Dic

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));
    }
}
msa.im
fuente
1
Seguramente hay un mejor nombre de clase ... ¡un extra tal final al menos!
hvaughan3
-2

Puedes usarlo así:

En Mvc:

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

En HTML:

<input type="text" name="Id" data_val_number="10"/>
gururaj nadager
fuente