Atributos HTML para EditorFor () en ASP.NET MVC

129

¿Por qué no puedo pasar atributos html a EditorFor()? p.ej;

<%= Html.EditorFor(model => model.Control.PeriodType, 
    new { disabled = "disabled", readonly = "readonly" }) %>

No quiero usar metadatos

Actualización : la solución fue llamar a esto desde la vista:

 <%=Html.EditorFor( model => model.Control.PeriodEndDate, new {Modifiable=model.Control.PeriodEndDateModifiable})%>

y usar ViewData["Modifiable"]en mi EditorTemplates / String.ascx personalizado donde tengo cierta lógica de vista que determina si agregar atributos de solo lectura y / o deshabilitados a la entrada El objeto anónimo pasado EditorFor()es un parámetro llamado additionalViewDatay sus propiedades se pasan a la plantilla del editor en el ViewDatacolección.

Typo Johnson
fuente
19
En serio, aún en MVC3 esta limitación no tiene sentido y es realmente molesto. La gente de todo el mundo pasa tiempo y se saca el pelo con estas tonterías. Estoy enviando esto a Microsoft Connect.
Junior Mayhé
3
¿Soy solo yo, o eso parece mucho bs para algo que debería ser tan simple?
Eric K
Quizás esto lo ayude: [EditorFor-Implementation con clases CSS y atributos HTML] [1] [1]: stackoverflow.com/questions/12675708/…
FunThom
Posible duplicado de EditorFor () y propiedades html
Gyrfalcon

Respuestas:

96

EditorForfunciona con metadatos, por lo que si desea agregar atributos html, siempre puede hacerlo . Otra opción es simplemente escribir una plantilla personalizada y usar TextBoxFor:

<%= Html.TextBoxFor(model => model.Control.PeriodType, 
    new { disabled = "disabled", @readonly = "readonly" }) %>    
Darin Dimitrov
fuente
Los metadatos no funcionarán porque los atributos html varían dependiendo de otras propiedades en el modelo, en otras palabras, el dominio o la lógica del modelo vietnamita deberían determinar los atributos html y no los metadatos estáticos. O me estoy perdiendo el punto, ¿puedo establecer metadatos dinámicamente?
Typo Johnson
¿Qué es PeriodType? ¿No es una propiedad simple? Si es un objeto complejo, puede personalizar toda la plantilla colocando un parcial en ~/Views/ControllerName/EditorTemplates/SomeType.ascxdonde SomeTypeestá el nombre del tipo de la PeriodTypepropiedad.
Darin Dimitrov
Además, su código sugerido significaría pasar todo el modelo a una plantilla parcial que acceda a una propiedad específica, ¿esto significaría que tengo un parcial para cada propiedad?
Typo Johnson
2
Te entiendo ahora. Puedo usar <% = Html.EditorFor (model => model.Control.PeriodEndDate, new {Modificable = model.Control.PeriodEndDateModifiable})%> y usar ViewData ["PeriodEndDateModifiable"] en mis EditorTemplates / String.ascx personalizadas. Gracias
Typo Johnson
1
@ JuniorMayhé, no llamaría a esto una limitación aburrida. Si piensas más cuidadosamente, entenderás que esto tiene sentido. De hecho, el objetivo de la ayuda de EditorFor es que podría tener una plantilla correspondiente. Esta plantilla puede contener cualquier marcado. No necesariamente un solo elemento. No tendría ningún sentido definir @classuna plantilla de editor que contenga 3 divs, por ejemplo. El ayudante Html.TextBoxFor le permite definir esto porque sabe lo que genera este ayudante: un cuadro de texto, por lo que tiene sentido definir una clase para un elemento de entrada.
Darin Dimitrov
115

La actualización MVC 5.1 ahora admite directamente el siguiente enfoque, por lo que también funciona para el editor incorporado. http://www.asp.net/mvc/overview/releases/mvc51-release-notes#new-features (Es un caso de Gran pensamiento mental por igual o leen mi respuesta :)

Fin de actualización

Si está utilizando su propia plantilla de editor o con MVC 5.1, que ahora admite el siguiente enfoque directamente para editores integrados.

@Html.EditorFor(modelItem => item.YourProperty, 
  new { htmlAttributes = new { @class="verificationStatusSelect", style = "Width:50px"  } })

luego en su plantilla (no se requiere para tipos simples en MVC 5.1)

@Html.TextBoxFor(m => m, ViewData["htmlAttributes"])
AntonK
fuente
55
Esta nueva característica resuelve perfectamente el problema original solicitado hace cuatro años.
CoderSteve
1
Si en lugar de TextBoxFor y ayudantes como ese uso toneladas de plantillas de editor personalizadas, no diría que es una idea fantástica agregar ViewData [...] en cada una de ellas ... :(
Alexander
42

A partir de MVC 5.1, ahora puede hacer lo siguiente:

@Html.EditorFor(model => model, new { htmlAttributes = new { @class = "form-control" }, })

http://www.asp.net/mvc/overview/releases/mvc51-release-notes#new-features

vtforester
fuente
El enlace de arriba está muerto. Está disponible en MVC 5.1, más información aquí: asp.net/mvc/overview/releases/mvc51-release-notes#new-features
Alaa Masoud
1
Gracias, arreglé el enlace también.
vtforester
2
¿Cómo fusionar htmlAttributes?
Bart Calixto
Esto solo funciona con las EditorForplantillas integradas / predeterminadas . Si implementa la suya, debe seguir la respuesta de AntonK.
mxmissile
6

Ahora ASP.Net MVC 5.1 tiene un soporte integrado para ello.

De las notas de la versión

Ahora permitimos pasar atributos HTML en EditorFor como un objeto anónimo.

Por ejemplo:

@Html.EditorFor(model => model, 
              new { htmlAttributes = new { @class = "form-control" }, })
Murali Murugesan
fuente
4

Aquí está la sintaxis del código VB.Net para atributos html en MVC 5.1 EditorFor

@Html.EditorFor(Function(x) x.myStringProp, New With {.htmlAttributes = New With {.class = "myCssClass", .maxlength="30"}}))
Max
fuente
3

¿Por qué no solo usar

@Html.DisplayFor(model => model.Control.PeriodType)
Mhoque
fuente
30
Una razón es que DisplayFor no representa una entrada, por lo que el valor se pierde en la devolución de datos.
ProfK
2
Otra razón es el escenario específico donde el editor está bloqueado actualmente, pero no siempre, y desea comunicar que los datos son potencialmente editables, pero no ahora.
Mir
2

Si no desea usar Metadatos, puede usar un [UIHint("PeriodType")]atributo para decorar la propiedad o si es un tipo complejo, no tiene que decorar nada. EditorFor buscará un archivo PeriodType.aspx o ascx en la carpeta EditorTemplates y lo usará en su lugar.

John Farrell
fuente
Gracias, puedo terminar haciendo eso si el if / else en mi plantilla de editor solo es necesario para ciertos campos
Typo Johnson
Pensé que dijiste que no podías cambiar el modelo (no tu código), por lo que decorar con UIHint directamente no sería una opción.
Darrel Lee
2

He estado luchando con el mismo problema hoy por una casilla de verificación que se une a un bool anulable, y como no puedo cambiar mi modelo (no mi código) tuve que encontrar una mejor manera de manejar esto. Es un poco fuerza bruta, pero debería funcionar en el 99% de los casos que pueda encontrar. Obviamente, tendría que hacer una población manual de atributos válidos para cada tipo de entrada, pero creo que los obtuve todos para la casilla de verificación.

En mi plantilla de editor Boolean.cshtml:

@model bool?

@{
    var attribs = new Dictionary<string, object>();
    var validAttribs = new string[] {"style", "class", "checked", "@class",
        "classname","id", "required", "value", "disabled", "readonly", 
        "accesskey", "lang", "tabindex", "title", "onblur", "onfocus", 
        "onclick", "onchange", "ondblclick", "onmousedown", "onmousemove", 
        "onmouseout", "onmouseover", "onmouseup", "onselect"};
    foreach (var item in ViewData) 
    {
        if (item.Key.ToLower().IndexOf("data_") == 0 || item.Key.ToLower().IndexOf("aria_") == 0) 
        {
            attribs.Add(item.Key.Replace('_', '-'), item.Value);
        } 
        else 
        {
            if (validAttribs.Contains(item.Key.ToLower()))
            {
                attribs.Add(item.Key, item.Value);
            }
        }
    }
}

@Html.CheckBox("", Model.GetValueOrDefault(), attribs)
Isócrono
fuente
Agregué Dictionary<string,string> attribs = new Dictionary<string,string>();y attribs.Add("tabindex","16");en uno de esos @( )bloques en la parte superior de mi página. Luego lo hice @Html.CheckBox("IsActive", Model.IsActive.HasValue ? Model.IsActive : false, attribs)en mi página y me da un error: "'System.Web.Mvc.HtmlHelper <MyProject.Models.MyObject>' no contiene una definición para 'CheckBox' y el mejor método de extensión sobrecarga 'System.Web. Mvc.InputExtensions.CheckBox (System.Web.Mvc.HtmlHelper, string.bool, object) 'tiene algunos argumentos no válidos ".
vapcguy
2

Todavía puedes usar EditorFor. Simplemente pase el estilo / cualquier atributo html como ViewData.

@Html.EditorFor(model => model.YourProperty, new { style = "Width:50px" })

Debido a que EditorFor usa plantillas para renderizar, puede anular la plantilla predeterminada para su propiedad y simplemente pasar el atributo de estilo como ViewData.

Entonces su EditorTemplate desea lo siguiente:

@inherits System.Web.Mvc.WebViewPage<object>

@Html.TextBoxFor(m => m, new { @class = "text ui-widget-content", style=ViewData["style"] })

fuente
1
Html.TextBoxFor(model => model.Control.PeriodType, 
    new { @class="text-box single-line"})

puedes usar así; mismo resultado con Html.EditorFor, y puede agregar sus atributos html

zamoldar
fuente
Excepto TextBoxForignora cualquier tipo de DisplayFormateso que esté intentando aplicar.
Eric K
1

Simplemente cree su propia plantilla para el tipo en Views / Shared / EditorTemplates / MyTypeEditor.vbhtml

@ModelType MyType

@ModelType MyType
@Code
    Dim name As String = ViewData("ControlId")
    If String.IsNullOrEmpty(name) Then
        name = "MyTypeEditor"
    End If
End Code

' Mark-up for MyType Editor
@Html.TextBox(name, Model, New With {.style = "width:65px;background-color:yellow"})

Invoque el editor desde su vista con la propiedad modelo:

@Html.EditorFor(Function(m) m.MyTypeProperty, "MyTypeEditor", New {.ControlId = "uniqueId"})

Perdón por la sintaxis de VB. Así es como rodamos.

Darrel Lee
fuente
1

En mi caso, estaba tratando de crear una plantilla de editor de entrada de número HTML5 que pudiera recibir atributos adicionales. Un enfoque más ordenado sería escribir su propio HTML Helper, pero como ya tenía mi plantilla .ascx, seguí este enfoque:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
<input id="<%= Regex.Replace(ViewData.TemplateInfo.GetFullHtmlFieldId(""), @"[\[\]]", "_") %>" name="<%= ViewData.TemplateInfo.HtmlFieldPrefix %>" type="number" value="<%= ViewData.TemplateInfo.FormattedModelValue %>"
<% if (ViewData["attributes"] != null)
   {
       Dictionary<string, string> attributes = (Dictionary<string, string>)ViewData["attributes"];
       foreach (string attributeName in attributes.Keys){%>
        <%= String.Format(" {0}=\"{1}\"", attributeName, attributes[attributeName])%>
       <% }
   } %> />

Este bit feo crea una entrada de tipo de número y busca un Diccionario ViewData con la clave "atributos". Recorrerá iterativamente el diccionario agregando sus pares clave / valor como atributos. La expresión regular en el atributo ID no está relacionada y está allí porque cuando se usa en una colección, GetFullHtmlFieldId()devuelve una identificación que contiene corchetes de la []que normalmente se escaparía como guiones bajos.

Esta plantilla se llama así:

Html.EditorFor(m => m.Quantity, "NumberField", new { attributes = new Dictionary<string, string>() { { "class", "txtQuantity" } } }

Detallado, pero funciona. Probablemente podría usar la reflexión en la plantilla para usar nombres de propiedades como nombres de atributos en lugar de usar un diccionario.

xr280xr
fuente
1

Establezca la condición usando ViewDataen el controlador

ViewData["Modifiable"] = model.recProcessed;

Luego, use estos datos de vista en la plantilla del editor para establecer el atributo html del control

@Html.RadioButton(prefix, li.Value, li.Selected, @ViewData["Modifiable"].ToString().ToLower() == "true" ? (object)new  { @id = li.Value, @disabled = "disabled" } : new { @id = li.Value })
reine varghese
fuente
0

MVC 5.1 y una solución superior (fusionará los atributos HtmlAttributes locales y se definirán en las plantillas del editor):

Shared \ EditorTemplates \ String.cshtml:

@Html.TextBoxFor(model => model, new { @class = "form-control", placeholder = ViewData.ModelMetadata.Watermark }.ToExpando().MergeHtmlAttributes(ViewData["htmlAttributes"].ToExpando()))

Extensiones:

public static IDictionary<string, object> MergeHtmlAttributes(this ExpandoObject source1, dynamic source2)
{
    Condition.Requires(source1, "source1").IsNotNull().IsLongerThan(0);

    IDictionary<string, object> result = source2 == null
        ? new Dictionary<string, object>()
        : (IDictionary<string, object>) source2;

    var dictionary1 = (IDictionary<string, object>) source1;

    string[] commonKeys = result.Keys.Where(dictionary1.ContainsKey).ToArray();
    foreach (var key in commonKeys)
    {
        result[key] = string.Format("{0} {1}", dictionary1[key], result[key]);
    }

    foreach (var item in dictionary1.Where(pair => !result.ContainsKey(pair.Key)))
    {
        result.Add(item);
    }

    return result;
}

public static ExpandoObject ToExpando(this object anonymousObject)
{
    IDictionary<string, object> anonymousDictionary = new RouteValueDictionary(anonymousObject);
    IDictionary<string, object> expando = new ExpandoObject();
    foreach (var item in anonymousDictionary)
        expando.Add(item);
    return (ExpandoObject)expando;
}

public static bool HasProperty(this ExpandoObject expando, string key)
{
    return ((IDictionary<string, object>)expando).ContainsKey(key);
}

Uso:

@Html.EditorFor(m => m.PromotionalCode, new { htmlAttributes = new { ng_model = "roomCtrl.searchRoomModel().promoCode" }})
Sergey G.
fuente
1
Gracias, eso funciona. Excepto por todos los atributos data_xxx, donde tuve que reemplazar _ con - cuando lancé source1y source2como IDictionary<string, object>. Hice esta función: private static IDictionary<string, object> ToHtmlAttributesDictionary(this IEnumerable<KeyValuePair<string, object>> dico) { return dico.ToDictionary(s => s.Key.Replace('_', '-'), s => s.Value); }
Marien Monnier