Atributos HTML para EditorFor () no ASP.NET MVC


129

Por que não posso transmitir atributos html para EditorFor()? por exemplo;

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

Não quero usar metadados

Atualização : a solução foi chamar isso da exibição:

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

e uso ViewData["Modifiable"]em meu EditorTemplates / String.ascx personalizado, onde tenho alguma lógica de exibição que determina se deve adicionar atributos somente leitura e / ou desativados à entrada. O objeto anônimo passado EditorFor()é um parâmetro chamado additionalViewDatae suas propriedades são passadas para o modelo de editor no diretório ViewDatacoleção.


19
Sério, ainda no MVC3 essa limitação não faz sentido e é realmente irritante. Pessoas de todo o mundo estão gastando tempo e arrancando os cabelos com esse absurdo. Estou enviando isso para o Microsoft Connect.
Junior Mayhé 21/09/12


3
Sou só eu, ou isso parece muito bs para algo que deveria ser muuuuito simples?
Eric K

Talvez isso ajude você: [EditorPara-Implementação com classes CSS e atributos HTML] [1] [1]: stackoverflow.com/questions/12675708/…
FunThom 4/13/13

Possível duplicados de EditorFor () e html propriedades
Gyrfalcon

Respostas:


96

EditorForfunciona com metadados; portanto, se você quiser adicionar atributos html, sempre poderá fazê-lo . Outra opção é simplesmente escrever um modelo personalizado e usar TextBoxFor:

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

Os metadados não funcionarão porque os atributos html variam dependendo de outras propriedades do modelo; em outras palavras, a lógica domain ou viemodel deve determinar os atributos html e não os metadados estáticos. Ou estou perdendo o ponto, posso definir os metadados dinamicamente?
Typo Johnson

O que é PeriodType? Não é uma propriedade simples? Se for um objeto complexo, você poderá personalizar o modelo inteiro colocando um parcial em ~/Views/ControllerName/EditorTemplates/SomeType.ascxonde SomeTypeestá o nome do tipo da PeriodTypepropriedade.
Darin Dimitrov

Além disso, seu código sugerido significaria passar o modelo inteiro para um modelo parcial que acessa uma propriedade específica, isso significa que eu tenho uma parcial para cada propriedade?
Typo Johnson

2
Eu te pego agora. Eu posso usar <% = Html.EditorFor (model => model.Control.PeriodEndDate, new {Modifiable = model.Control.PeriodEndDateModifiable})%> e usar ViewData ["PeriodEndDateModifiable"] em meus EditorTemplates / String.ascx personalizados. Obrigado
Typo Johnson

1
@ JuniorMayhé, eu não chamaria isso de uma limitação chata. Se você pensar com mais cuidado, entenderá que isso faz sentido. De fato, o ponto principal do auxiliar EditorFor é que você pode ter um modelo correspondente. Este modelo pode conter qualquer marcação. Não é necessariamente um único elemento. Não faria sentido definir @classum modelo de editor que contenha 3 divs, por exemplo. O auxiliar Html.TextBoxFor permite definir isso porque você sabe o que esse auxiliar gera - uma caixa de texto, portanto faz sentido definir uma classe para um elemento de entrada.
Darin Dimitrov

115

A atualização MVC 5.1 agora suporta diretamente a abordagem abaixo, portanto também funciona para o editor incorporado. http://www.asp.net/mvc/overview/releases/mvc51-release-notes#new-features (Ou é um caso de uma grande mente pensando da mesma forma ou eles leem minha resposta :)

Finalizar atualização

Se você estiver usando seu próprio modelo de editor ou com o MVC 5.1, que agora suporta a abordagem abaixo diretamente para editores internos.

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

depois no seu modelo (não é necessário para tipos simples no MVC 5.1)

@Html.TextBoxFor(m => m, ViewData["htmlAttributes"])

5
Esse novo recurso resolve perfeitamente o problema original solicitado há quatro anos.
CoderSteve

1
Se, em vez de TextBoxFor e ajudantes como esse, eu usar vários modelos de editor personalizados, não diria que é uma idéia fantástica adicionar ViewData em cada um deles ... :(
Alexander

42

A partir do MVC 5.1, agora você pode fazer o seguinte:

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

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


O link acima está morto. Isto está disponível em MVC 5.1, mais informações aqui: asp.net/mvc/overview/releases/mvc51-release-notes#new-features
Alaa Masoud

1
Obrigado, eu também corrigi o link.
precisa saber é o seguinte

2
como mesclar htmlAttributes?
Bart Calixto

Isso funciona apenas com os EditorFormodelos internos / padrão . Se você implementar o seu próprio, deverá seguir a resposta do AntonK.
Mxmissile

6

Agora, o ASP.Net MVC 5.1 recebeu um suporte integrado.

Das notas de versão

Agora, permitimos a passagem de atributos HTML no EditorFor como um objeto anônimo.

Por exemplo:

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

4

Aqui está a sintaxe do código VB.Net para atributos html no MVC 5.1 Editor

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

3

Por que não usar apenas

@Html.DisplayFor(model => model.Control.PeriodType)

30
Uma razão é que DisplayFor não processa uma entrada; portanto, o valor é perdido na postagem.
ProfK

2
Outro motivo é o cenário específico em que o editor está atualmente, mas nem sempre está bloqueado, e você deseja comunicar que os dados são potencialmente editáveis ​​- mas não agora.
Mir

2

Se você não quiser usar os metadados, poderá usar um [UIHint("PeriodType")]atributo para decorar a propriedade ou, se for um tipo complexo, não precisará decorar nada. O EditorFor procurará um arquivo PeriodType.aspx ou ascx na pasta EditorTemplates e o utilizará.


Obrigado, eu pode acabar fazendo que se o if / else no meu modelo editor só é necessária para determinados campos
Typo Johnson

Embora você tenha dito que não poderia alterar o modelo (não o seu código), a decoração com o UIHint diretamente não seria uma opção.
Darrel Lee

2

Hoje estou lutando com o mesmo problema em uma caixa de seleção que se liga a um bool anulável e, como não consigo alterar meu modelo (não meu código), tive que criar uma maneira melhor de lidar com isso. É um pouco de força bruta, mas deve funcionar em 99% dos casos que eu possa encontrar. Você obviamente teria que fazer uma população manual de atributos válidos para cada tipo de entrada, mas acho que consegui todos eles para a caixa de seleção.

No meu modelo 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)

Eu adicionei Dictionary<string,string> attribs = new Dictionary<string,string>();e attribs.Add("tabindex","16");em um desses @( )blocos na parte superior da minha página. Então eu fiz @Html.CheckBox("IsActive", Model.IsActive.HasValue ? Model.IsActive : false, attribs)na minha página e isso me dá um erro: "'System.Web.Mvc.HtmlHelper <MyProject.Models.MyObject>' não contém uma definição para 'CheckBox' e o melhor método de extensão sobrecarrega 'System.Web. Mvc.InputExtensions.CheckBox (System.Web.Mvc.HtmlHelper, string.bool, object) 'possui alguns argumentos inválidos ".
vapcguy

2

Você ainda pode usar o EditorFor. Basta passar o estilo / qualquer atributo html como ViewData.

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

Como o EditorFor usa modelos para renderizar, você pode substituir o modelo padrão para sua propriedade e simplesmente passar o atributo de estilo como ViewData.

Portanto, seu EditorTemplate gostaria do seguinte:

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

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

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

você pode usar assim; mesma saída com Html.EditorFor, e você pode adicionar seus atributos html


Exceto TextBoxForignora qualquer tipo DisplayFormatque você possa estar tentando aplicar.
Eric K

1

Basta criar seu próprio modelo para o tipo em 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"})

Chame o editor da sua visualização com a propriedade model:

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

Perdoe a sintaxe do VB. É assim que rolamos.


1

No meu caso, eu estava tentando criar um modelo de editor de entrada de número HTML5 que pudesse receber atributos adicionais. Uma abordagem mais organizada seria escrever seu próprio HTML Helper, mas como eu já tinha meu modelo .ascx, segui essa abordagem:

<%@ 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 feio cria uma entrada de tipo numérico e procura um dicionário ViewData com a chave "attribute". Ele irá percorrer o dicionário adicionando seus pares chave / valor como atributos. O Regex no atributo ID não está relacionado e existe porque, quando usado em uma coleção, GetFullHtmlFieldId()retorna um ID contendo colchetes[] quais normalmente ele poderia escapar como sublinhados.

Este modelo é chamado assim:

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

Detalhado, mas funciona. Você provavelmente poderia usar a reflexão no modelo para usar nomes de propriedades como nomes de atributos, em vez de usar um dicionário.


1

Defina a condição usando ViewDatano controlador

ViewData["Modifiable"] = model.recProcessed;

Em seguida, use esses dados de visualização no modelo do editor para definir o atributo html do controle

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

0

MVC 5.1 e uma solução superior (mesclará HtmlAttributes locais e definidos no EditorTemplates):

Shared \ EditorTemplates \ String.cshtml:

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

Extensões:

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

1
Obrigado, isso funciona. Exceto por todos os atributos data_xxx, onde eu tive que substituir _ por - ao converter source1e source2como IDictionary<string, object>. Eu fiz esta função: private static IDictionary<string, object> ToHtmlAttributesDictionary(this IEnumerable<KeyValuePair<string, object>> dico) { return dico.ToDictionary(s => s.Key.Replace('_', '-'), s => s.Value); }
Marien Monnier
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.