Attributi HTML per EditorFor () in ASP.NET MVC


129

Perché non riesco a passare gli attributi HTML a EditorFor()? per esempio;

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

Non voglio usare i metadati

Aggiornamento : la soluzione era chiamare questo dalla vista:

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

e uso ViewData["Modifiable"]nel mio EditorTemplates / String.ascx personalizzato in cui ho una logica di visualizzazione che determina se aggiungere attributi di sola lettura e / o disabilitati all'input L'oggetto anonimo passato in EditorFor()è un parametro chiamato additionalViewDatae le sue proprietà vengono passate al modello editor nel ViewDatacollezione.


19
Seriamente, ancora in MVC3 questa limitazione non ha senso ed è davvero fastidiosa. Le persone in tutto il mondo trascorrono del tempo e si tolgono i capelli con questa assurdità. Sto inviando questo a Microsoft Connect.
Junior Mayhé,


3
Sono solo io, o sembra un sacco di bs per qualcosa che dovrebbe essere davvero semplice?
Eric K,

Forse questo ti aiuterà a: [EditorFor-Attuazione con le classi CSS e HTML attributi] [1] [1]: stackoverflow.com/questions/12675708/...
FunThom

Possibile duplicato delle proprietà EditorFor () e html
Gyrfalcon,

Risposte:


96

EditorForfunziona con i metadati, quindi se vuoi aggiungere attributi html puoi sempre farlo . Un'altra opzione è semplicemente scrivere un modello personalizzato e usare TextBoxFor:

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

I metadati non funzioneranno perché gli attributi html variano a seconda delle altre proprietà nel modello, in altre parole la logica di dominio o viemodel dovrebbe determinare gli attributi html non i metadati statici. O mi manca il punto, posso impostare i metadati in modo dinamico?
Typo Johnson,

Che cosa è PeriodType? Non è una proprietà semplice? Se si tratta di un oggetto complesso, è possibile personalizzare l'intero modello posizionando un parziale in ~/Views/ControllerName/EditorTemplates/SomeType.ascxcui si SomeTypetrova il nome del tipo della PeriodTypeproprietà.
Darin Dimitrov,

Anche il tuo codice suggerito significherebbe passare l'intero modello in un modello parziale che accede a una proprietà specifica, questo significherebbe che ho un parziale per ogni proprietà?
Typo Johnson,

2
Adesso ti capisco. Posso usare <% = Html.EditorFor (model => model.Control.PeriodEndDate, new {Modifiable = model.Control.PeriodEndDateModifiable})%> e usare ViewData ["PeriodEndDateModifiable"] nel mio EditorTemplates / String.ascx personalizzato. Grazie
Typo Johnson,

1
@ JuniorMayhé, non lo definirei una limitazione noiosa. Se pensi più attentamente capirai che questo ha senso. In effetti, il punto fondamentale dell'helper EditorFor è che potresti avere un modello corrispondente. Questo modello potrebbe contenere qualsiasi markup. Non necessariamente un singolo elemento. Non avrebbe assolutamente senso definire @classun modello di editor che contenga, ad esempio, 3 div. L'helper Html.TextBoxFor ti consente di definirlo perché sai cosa genera questo helper: una casella di testo, quindi ha senso definire una classe in un elemento di input.
Darin Dimitrov,

115

L'aggiornamento MVC 5.1 ora supporta direttamente l'approccio seguente, quindi funziona anche con l'editor integrato. http://www.asp.net/mvc/overview/releases/mvc51-release-notes#new-features (O è un caso di grande mente che pensa allo stesso modo o leggono la mia risposta :)

Fine aggiornamento

Se stai usando il tuo modello di editor o con MVC 5.1 che ora supporta l'approccio seguente direttamente per gli editor integrati.

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

quindi nel modello (non richiesto per tipi semplici in MVC 5.1)

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

5
Questa nuova funzionalità risolve perfettamente il problema originale richiesto quattro anni fa.
CoderSteve,

1
Se invece di TextBoxFor e degli aiutanti del genere uso tonnellate di modelli di editor personalizzati, non direi che è una fantastica idea aggiungere ViewData [...] in ciascuno di essi ... :(
Alexander

42

A partire da MVC 5.1, ora è possibile effettuare le seguenti operazioni:

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

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


Il link sopra è morto. Questo è disponibile in MVC 5.1, maggiori informazioni qui: asp.net/mvc/overview/releases/mvc51-release-notes#new-features
Alaa Masoud

1
Grazie, ho risolto anche il collegamento.
vtforester

2
come unire htmlAttributes?
Bart Calixto,

Funziona solo con i EditorFormodelli predefiniti / integrati . Se implementi il ​​tuo, devi seguire la risposta di AntonK.
mxmissile,

6

Ora ASP.Net MVC 5.1 ha ottenuto un supporto integrato per esso.

Dalle note di rilascio

Ora consentiamo il passaggio di attributi HTML in EditorFor come oggetto anonimo.

Per esempio:

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

4

Ecco la sintassi del codice VB.Net per gli attributi html in MVC 5.1 EditorFor

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

3

Perché non solo usare

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

30
Uno dei motivi è che DisplayFor non esegue il rendering di un input, quindi il valore viene perso al postback.
ProfK,

2
Un altro motivo è lo scenario specifico in cui l'editor è attualmente, ma non sempre, bloccato e si desidera comunicare che i dati sono potenzialmente modificabili, ma non ora.
Mir

2

Se non vuoi usare i metadati puoi usare un [UIHint("PeriodType")]attributo per decorare la proprietà o se è un tipo complesso non devi decorare nulla. EditorFor cercherà quindi un file PeriodType.aspx o ascx nella cartella EditorTemplates e lo utilizzerà invece.


Grazie, potrei finire per farlo se il if / else nel mio modello di editor è richiesto solo per determinati campi
Typo Johnson

Pensavo avessi detto che non potevi cambiare il modello (non il tuo codice), quindi decorare direttamente con UIHint non sarebbe un'opzione.
Darrel Lee,

2

Oggi ho lottato con lo stesso problema per una casella di controllo che si lega a un valore nulla, e dato che non riesco a cambiare il mio modello (non il mio codice), ho dovuto trovare un modo migliore di gestirlo. È un po 'bruta, ma dovrebbe funzionare per il 99% dei casi che potrei incontrare. Dovresti ovviamente fare un po 'di popolamento manuale di attributi validi per ogni tipo di input, ma penso di averli presi tutti per la casella di controllo.

Nel mio modello di 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)

Ho aggiunto Dictionary<string,string> attribs = new Dictionary<string,string>();e attribs.Add("tabindex","16");in uno di quei @( )blocchi nella parte superiore della mia pagina. Poi l'ho fatto @Html.CheckBox("IsActive", Model.IsActive.HasValue ? Model.IsActive : false, attribs)sulla mia pagina e mi dà un errore: "'System.Web.Mvc.HtmlHelper <MyProject.Models.MyObject>' non contiene una definizione per 'CheckBox' e il miglior sovraccarico del metodo di estensione 'System.Web. Mvc.InputExtensions.CheckBox (System.Web.Mvc.HtmlHelper, string.bool, object) "ha alcuni argomenti non validi".
vapcguy,

2

Puoi ancora usare EditorFor. Basta passare l'attributo style / qualunque html come ViewData.

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

Poiché EditorFor utilizza i modelli per il rendering, è possibile ignorare il modello predefinito per la proprietà e semplicemente passare l'attributo di stile come ViewData.

Quindi il tuo EditorTemplate vorrebbe quanto segue:

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

puoi usare così; stesso output con Html.EditorFore puoi aggiungere i tuoi attributi html


Tranne TextBoxForignora qualsiasi tipo di DisplayFormatciò che si sta tentando di applicare.
Eric K,

1

Basta creare il proprio modello per il tipo in 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"})

Richiama l'editor dalla tua vista con la proprietà del modello:

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

Perdonate la sintassi VB. È così che andiamo.


1

Nel mio caso stavo cercando di creare un modello di editor di input di numeri HTML5 che potesse ricevere ulteriori attributi. Un approccio più accurato sarebbe quello di scrivere il tuo HTML Helper, ma dato che avevo già il mio modello .ascx, ho seguito questo approccio:

<%@ 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])%>
       <% }
   } %> />

Questo brutto bit crea un input di tipo numerico e cerca un dizionario ViewData con gli "attributi" chiave. Esaminerà il dizionario aggiungendo le sue coppie chiave / valore come attributi. L'attributo Regex nell'attributo ID non è correlato ed è presente perché, quando utilizzato in una raccolta, GetFullHtmlFieldId()restituisce un ID contenente parentesi quadre[] che normalmente sfuggirebbe a caratteri di sottolineatura.

Questo modello viene quindi chiamato in questo modo:

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

Verbo, ma funziona. Probabilmente potresti usare la riflessione nel modello per usare i nomi delle proprietà come nomi di attributo invece di usare un dizionario.


1

Impostare la condizione utilizzando ViewDatanel controller

ViewData["Modifiable"] = model.recProcessed;

Quindi utilizzare questo viewdata nel modello dell'editor per impostare l'attributo html del controllo

@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 soluzione successiva (uniranno HtmlAttributes locali e definiti in EditorTemplates):

Shared \ EditorTemplates \ String.cshtml:

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

estensioni:

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
Grazie, funziona. Ad eccezione di tutti gli attributi data_xxx, in cui ho dovuto sostituire _ con - durante il casting source1e source2come IDictionary<string, object>. Ho svolto questa funzione: private static IDictionary<string, object> ToHtmlAttributesDictionary(this IEnumerable<KeyValuePair<string, object>> dico) { return dico.ToDictionary(s => s.Key.Replace('_', '-'), s => s.Value); }
Marien Monnier,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.