Attributs HTML pour EditorFor () dans ASP.NET MVC

129

Pourquoi ne puis-je pas transmettre d'attributs html à EditorFor()? par exemple;

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

Je ne veux pas utiliser de métadonnées

Mise à jour : la solution était d'appeler ceci depuis la vue:

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

et utiliser ViewData["Modifiable"]dans mon EditorTemplates / String.ascx personnalisé où j'ai une logique de vue qui détermine s'il faut ajouter des attributs en lecture seule et / ou désactivés à l'entrée.L'objet anonyme transmis EditorFor()est un paramètre appelé additionalViewDataet ses propriétés sont transmises au modèle d'éditeur dans le ViewDatacollection.

Typo Johnson
la source
19
Sérieusement, toujours dans MVC3, cette limitation n'a aucun sens et c'est vraiment ennuyeux. Les gens du monde entier passent du temps et se tirent les cheveux avec ce non-sens. J'envoie ceci à Microsoft Connect.
Junior Mayhé
3
Est-ce juste moi, ou cela semble-t-il beaucoup de bs pour quelque chose qui devrait être tellement simple?
Eric K du
Peut-être que cela vous aidera: [EditorFor-Implementation with CSS classes and HTML attributes] [1] [1]: stackoverflow.com/questions/12675708
...
Possible duplication des propriétés EditorFor () et html
Gyrfalcon

Réponses:

96

EditorForfonctionne avec les métadonnées, donc si vous souhaitez ajouter des attributs html, vous pouvez toujours le faire . Une autre option consiste simplement à écrire un modèle personnalisé et à utiliser TextBoxFor:

<%= Html.TextBoxFor(model => model.Control.PeriodType, 
    new { disabled = "disabled", @readonly = "readonly" }) %>    
Darin Dimitrov
la source
Les métadonnées ne fonctionneront pas car les attributs html varient en fonction d'autres propriétés du modèle, en d'autres termes, la logique de domaine ou de viemodel doit déterminer les attributs html et non les métadonnées statiques. Ou est-ce que je rate le point, puis-je définir des métadonnées de manière dynamique?
Typo Johnson
C'est quoi PeriodType? N'est-ce pas une simple propriété? S'il s'agit d'un objet complexe, vous pouvez personnaliser l'ensemble du modèle en plaçant un partiel dans ~/Views/ControllerName/EditorTemplates/SomeType.ascxoù se SomeTypetrouve le nom de type de la PeriodTypepropriété.
Darin Dimitrov le
De plus, votre code suggéré signifierait passer le modèle entier dans un modèle partiel qui accède à une propriété spécifique, cela signifierait que j'ai un partiel pour chaque propriété?
Typo Johnson
2
Je te comprends maintenant. Je peux utiliser <% = Html.EditorFor (model => model.Control.PeriodEndDate, new {Modifiable = model.Control.PeriodEndDateModifiable})%> et utiliser ViewData ["PeriodEndDateModifiable"] dans mes EditorTemplates / String.ascx personnalisés. Merci
Typo Johnson
1
@ JuniorMayhé, je n'appellerais pas cela une limitation ennuyeuse. Si vous réfléchissez plus attentivement, vous comprendrez que cela a du sens. En fait, tout l'intérêt de l'assistant EditorFor est que vous pouvez avoir un modèle correspondant. Ce modèle peut contenir n'importe quel balisage. Pas nécessairement un seul élément. Cela n'aurait absolument aucun sens de définir @classun modèle d'éditeur contenant 3 div par exemple. L'assistant Html.TextBoxFor vous permet de définir cela parce que vous savez ce que cet assistant génère - une zone de texte, il est donc logique de définir une classe sur un élément d'entrée.
Darin Dimitrov
115

La mise à jour MVC 5.1 prend désormais directement en charge l'approche ci-dessous, elle fonctionne donc également pour l'éditeur intégré. http://www.asp.net/mvc/overview/releases/mvc51-release-notes#new-features (C'est soit un cas de Great mind pensant de la même manière, soit ils lisent ma réponse :)

Mettre fin à la mise à jour

Si vous utilisez votre propre modèle d'éditeur ou avec MVC 5.1 qui prend désormais en charge l'approche ci-dessous directement pour les éditeurs intégrés.

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

puis dans votre modèle (non requis pour les types simples dans MVC 5.1)

@Html.TextBoxFor(m => m, ViewData["htmlAttributes"])
AntonK
la source
5
Cette nouvelle fonctionnalité résout parfaitement le problème d'origine posé il y a quatre ans.
CoderSteve
1
Si au lieu de TextBoxFor et d'aides comme ça, j'utilise des tonnes de modèles d'éditeur personnalisés, je ne dirais pas que c'est une idée fantastique d'ajouter ViewData [...] dans chacun d'eux ... :(
Alexander
42

À partir de MVC 5.1, vous pouvez désormais effectuer les opérations suivantes:

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

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

vtforester
la source
Le lien ci-dessus est mort. Ceci est disponible dans MVC 5.1, plus d'infos ici: asp.net/mvc/overview/releases/mvc51-release-notes#new-features
Alaa Masoud
1
Merci, j'ai également corrigé le lien.
vtforester
2
comment fusionner htmlAttributes?
Bart Calixto
Cela ne fonctionne qu'avec les EditorFormodèles intégrés / par défaut . Si vous implémentez la vôtre, vous devez suivre la réponse d'AntonK.
mxmissile
6

Maintenant, ASP.Net MVC 5.1 a un support intégré pour cela.

À partir des notes de version

Nous autorisons maintenant le passage des attributs HTML dans EditorFor en tant qu'objet anonyme.

Par exemple:

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

Voici la syntaxe du code VB.Net pour les attributs html dans MVC 5.1 Editor

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

Pourquoi ne pas simplement utiliser

@Html.DisplayFor(model => model.Control.PeriodType)
Mhoque
la source
30
L'une des raisons est que DisplayFor ne rend pas une entrée, donc la valeur est perdue lors de la publication.
ProfK
2
Une autre raison est le scénario spécifique où l'éditeur est actuellement verrouillé mais pas toujours, et vous voulez communiquer que les données sont potentiellement modifiables - mais pas maintenant.
Mir
2

Si vous ne souhaitez pas utiliser de métadonnées, vous pouvez utiliser un [UIHint("PeriodType")]attribut pour décorer la propriété ou s'il s'agit d'un type complexe, vous n'avez rien à décorer. EditorFor recherchera ensuite un fichier PeriodType.aspx ou ascx dans le dossier EditorTemplates et l'utilisera à la place.

John Farrell
la source
Merci, je peux finir par le faire si le if / else dans mon modèle d'éditeur n'est requis que pour certains champs
Typo Johnson
Je pensais que vous aviez dit que vous ne pouviez pas changer le modèle (pas votre code), donc décorer directement avec UIHint ne serait pas une option.
Darrel Lee
2

Je suis aux prises avec le même problème aujourd'hui pour une case à cocher qui se lie à un bool nullable, et comme je ne peux pas changer mon modèle (pas mon code), j'ai dû trouver une meilleure façon de gérer cela. C'est un peu la force brute, mais cela devrait fonctionner dans 99% des cas que je pourrais rencontrer. Vous auriez évidemment à faire un remplissage manuel des attributs valides pour chaque type d'entrée, mais je pense que je les ai tous cochés.

Dans mon modèle d'éditeur 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)
Isochrone
la source
J'ai ajouté Dictionary<string,string> attribs = new Dictionary<string,string>();et attribs.Add("tabindex","16");dans l'un de ces @( )blocs en haut de ma page. Ensuite, je l'ai fait @Html.CheckBox("IsActive", Model.IsActive.HasValue ? Model.IsActive : false, attribs)sur ma page et cela me donne une erreur: "'System.Web.Mvc.HtmlHelper <MyProject.Models.MyObject>' ne contient pas de définition pour 'CheckBox' et la meilleure surcharge de méthode d'extension 'System.Web. Mvc.InputExtensions.CheckBox (System.Web.Mvc.HtmlHelper, string.bool, object) 'a des arguments non valides ".
vapcguy
2

Vous pouvez toujours utiliser EditorFor. Passez simplement l'attribut style / n'importe quel attribut html en tant que ViewData.

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

Étant donné que EditorFor utilise des modèles pour le rendu, vous pouvez remplacer le modèle par défaut de votre propriété et simplement transmettre l'attribut de style en tant que ViewData.

Ainsi, votre EditorTemplate aimerait ce qui suit:

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

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

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

vous pouvez utiliser comme ça; même sortie avec Html.EditorFor, et vous pouvez ajouter vos attributs html

Zamoldar
la source
Sauf TextBoxForignore tout type de ce DisplayFormatque vous essayez d'appliquer.
Eric K
1

Créez simplement votre propre modèle pour le type dans 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"})

Appelez l'éditeur depuis votre vue avec la propriété model:

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

Pardonnez la syntaxe VB. C'est comme ça que nous roulons.

Darrel Lee
la source
1

Dans mon cas, j'essayais de créer un modèle d'éditeur d'entrée de numéro HTML5 qui pourrait recevoir des attributs supplémentaires. Une approche plus soignée serait d'écrire votre propre HTML Helper, mais comme j'avais déjà mon modèle .ascx, j'ai opté pour cette approche:

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

Cet horrible bit crée une entrée de type numérique et recherche un dictionnaire ViewData avec la clé «attributs». Il parcourra le dictionnaire en ajoutant ses paires clé / valeur en tant qu'attributs. Le Regex dans l'attribut ID n'est pas lié et existe car lorsqu'il est utilisé dans une collection, GetFullHtmlFieldId()renvoie un identifiant contenant des crochets []qu'il échapperait normalement sous forme de traits de soulignement.

Ce modèle est alors appelé comme ceci:

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

Verbose, mais ça marche. Vous pouvez probablement utiliser la réflexion dans le modèle pour utiliser des noms de propriétés comme noms d'attributs au lieu d'utiliser un dictionnaire.

xr280xr
la source
1

Définissez la condition à l'aide ViewDatadu contrôleur

ViewData["Modifiable"] = model.recProcessed;

Ensuite, utilisez ce modèle viewdata dans l'éditeur pour définir l'attribut html du contrôle

@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
la source
0

MVC 5.1 et une solution supérieure (fusionnera les HtmlAttributes locaux et définis dans les EditorTemplates):

Shared \ EditorTemplates \ String.cshtml:

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

Extensions:

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

Usage:

@Html.EditorFor(m => m.PromotionalCode, new { htmlAttributes = new { ng_model = "roomCtrl.searchRoomModel().promoCode" }})
Sergey G.
la source
1
Merci, ça marche. Sauf pour tous les attributs data_xxx, où j'ai dû remplacer _ par - lors du casting source1et en source2tant que IDictionary<string, object>. J'ai fait cette fonction: private static IDictionary<string, object> ToHtmlAttributesDictionary(this IEnumerable<KeyValuePair<string, object>> dico) { return dico.ToDictionary(s => s.Key.Replace('_', '-'), s => s.Value); }
Marien Monnier