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.

Typo Johnson
fonte
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" }) %>    
Darin Dimitrov
fonte
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"])
AntonK
fonte
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

vtforester
fonte
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" }, })
Murali Murugesan
fonte
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"}}))
Máx.
fonte
3

Por que não usar apenas

@Html.DisplayFor(model => model.Control.PeriodType)
Mhoque
fonte
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á.

John Farrell
fonte
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)
Isócrono
fonte
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"] })

fonte
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

zamoldar
fonte
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.

Darrel Lee
fonte
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.

xr280xr
fonte
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 })
reine varghese
fonte
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" }})
Sergey G.
fonte
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