Atrybuty HTML dla EditorFor () w ASP.NET MVC


132

Dlaczego nie mogę przekazać atrybutów HTML do EditorFor()? na przykład;

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

Nie chcę używać metadanych

Aktualizacja : rozwiązaniem było nazwanie tego z widoku:

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

i użyj ViewData["Modifiable"]w moim niestandardowym EditorTemplates / String.ascx, gdzie mam pewną logikę widoku, która określa, czy dodać atrybuty tylko do odczytu i / lub wyłączone do danych wejściowych. Obiekt anonimowy przekazany do EditorFor()jest parametrem o nazwie, additionalViewDataa jego właściwości są przekazywane do szablonu edytora ViewDatakolekcja.


19
Poważnie, nadal w MVC3 to ograniczenie nie ma sensu i jest naprawdę irytujące. Ludzie na całym świecie spędzają czas i wyrywają sobie włosy z tych bzdur. Wysyłam to do Microsoft Connect.
Junior Mayhé


3
Czy to tylko ja, czy też wydaje się, że to dużo bs dla czegoś, co powinno być tak proste?
Eric K,

Może to ci pomoże: [EditorFor-Implementation z klasami CSS i atrybutami HTML] [1] [1]: stackoverflow.com/questions/12675708/…
FunThom

Możliwy duplikat właściwości EditorFor () i html
Gyrfalcon

Odpowiedzi:


98

EditorFordziała z metadanymi, więc jeśli chcesz dodać atrybuty html, zawsze możesz to zrobić . Inną opcją jest po prostu napisanie własnego szablonu i użycie TextBoxFor:

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

Metadane nie będą działać, ponieważ atrybuty html różnią się w zależności od innych właściwości w modelu, innymi słowy logika domeny lub viemodel powinna określać atrybuty html, a nie statyczne metadane. A może nie rozumiem, czy mogę dynamicznie ustawiać metadane?
Typo Johnson

Co to jest PeriodType? Czy to nie jest prosta właściwość? Jeśli jest to złożony obiekt, możesz dostosować cały szablon, umieszczając część w ~/Views/ControllerName/EditorTemplates/SomeType.ascxmiejscu, w którym SomeTypeznajduje się nazwa typu PeriodTypewłaściwości.
Darin Dimitrov

Również twój sugerowany kod oznaczałby przekazanie całego modelu do częściowego szablonu, który ma dostęp do określonej właściwości, co oznaczałoby, że mam część dla każdej właściwości?
Typo Johnson

2
Rozumiem cię teraz. Mogę użyć <% = Html.EditorFor (model => model.Control.PeriodEndDate, new {Modifiable = model.Control.PeriodEndDateModifiable})%> i użyć ViewData ["PeriodEndDateModifiable"] w moim niestandardowym EditorTemplates / String.ascx. Dzięki
Typo Johnson

1
@ JuniorMayhé, nie nazwałbym tego nudnym ograniczeniem. Jeśli dobrze się zastanowisz, zrozumiesz, że to ma sens. W rzeczywistości celem pomocnika EditorFor jest to, że możesz mieć odpowiedni szablon. Ten szablon może zawierać dowolne znaczniki. Niekoniecznie pojedynczy element. Nie miałoby absolutnie sensu definiowanie @classszablonu edytora, który zawiera na przykład 3 elementy div. Helper Html.TextBoxFor pozwala ci to zdefiniować, ponieważ wiesz, co ten helper generuje - pole tekstowe, więc sensowne jest zdefiniowanie klasy jako elementu wejściowego.
Darin Dimitrov

119

Aktualizacja MVC 5.1 obsługuje teraz bezpośrednio poniższe podejście, więc działa również w przypadku wbudowanego edytora. http://www.asp.net/mvc/overview/releases/mvc51-release-notes#new-features (To albo przypadek wspólnego myślenia Wielkiego Umysłu, albo przeczytali moją odpowiedź :)

Zakończ aktualizację

Jeśli używasz własnego szablonu edytora lub MVC 5.1, który obsługuje teraz poniższe podejście bezpośrednio dla wbudowanych edytorów.

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

następnie w swoim szablonie (niewymagane dla prostych typów w MVC 5.1)

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

5
Ta nowa funkcja doskonale rozwiązuje pierwotny problem postawiony cztery lata temu.
CoderSteve

1
Jeśli zamiast TextBoxFor i takich pomocników używam mnóstwa niestandardowych szablonów edytorów, nie powiedziałbym, że to fantastyczny pomysł, aby dodać ViewData [...] do każdego z nich ... :(
Alexander

42

Od wersji MVC 5.1 możesz teraz wykonać następujące czynności:

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

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


Powyższy link nie działa. To jest dostępne w MVC 5.1, więcej informacji tutaj: asp.net/mvc/overview/releases/mvc51-release-notes#new-features
Alaa Masoud

1
Dzięki, również poprawiłem link.
vtforester

2
jak scalić htmlAttributes?
Bart Calixto,

Działa to tylko z wbudowanymi / domyślnymi EditorForszablonami. Jeśli wdrażasz własne, musisz postępować zgodnie z odpowiedzią AntonK.
mxmissile

6

Teraz ASP.Net MVC 5.1 ma dla niego wbudowaną obsługę.

Z uwag do wydania

Teraz zezwalamy na przekazywanie atrybutów HTML w EditorFor jako anonimowy obiekt.

Na przykład:

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

4

Oto składnia kodu VB.Net dla atrybutów html w MVC 5.1 EditorFor

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

3

Dlaczego nie po prostu użyć

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

30
Jednym z powodów jest to, że DisplayFor nie renderuje danych wejściowych, więc wartość jest tracona podczas ogłaszania zwrotnego.
ProfK,

2
Innym powodem jest konkretny scenariusz, w którym edytor jest obecnie - ale nie zawsze - zablokowany, a Ty chcesz zakomunikować, że dane są potencjalnie edytowalne - ale nie teraz.
Mir,

2

Jeśli nie chcesz używać metadanych, możesz użyć [UIHint("PeriodType")]atrybutu do udekorowania właściwości lub jeśli jest to typ złożony, nie musisz niczego dekorować. EditorFor wyszuka plik PeriodType.aspx lub ascx w folderze EditorTemplates i użyje go zamiast tego.


Dzięki, mogę to zrobić, jeśli if / else w moim szablonie edytora jest wymagane tylko dla niektórych pól
Typo Johnson

Myślałem, że powiedziałeś, że nie możesz zmienić modelu (nie kodu), więc dekorowanie bezpośrednio za pomocą UIHint nie byłoby opcją.
Darrel Lee

2

Zmagałem się dzisiaj z tym samym problemem dla pola wyboru, które wiąże się z wartością zerową bool, a ponieważ nie mogę zmienić mojego modelu (nie mojego kodu), musiałem wymyślić lepszy sposób radzenia sobie z tym. To trochę brutalna siła, ale powinna działać w 99% przypadków, które mogę napotkać. Oczywiście musiałbyś ręcznie wypełnić prawidłowe atrybuty dla każdego typu danych wejściowych, ale myślę, że otrzymałem je wszystkie dla pola wyboru.

W moim szablonie edytora 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)

Dodałem Dictionary<string,string> attribs = new Dictionary<string,string>();i attribs.Add("tabindex","16");w jednym z tych @( )bloków na górze mojej strony. Potem zrobiłem @Html.CheckBox("IsActive", Model.IsActive.HasValue ? Model.IsActive : false, attribs)na mojej stronie i wyświetla mi się błąd: „„ System.Web.Mvc.HtmlHelper <MyProject.Models.MyObject> ”nie zawiera definicji„ CheckBox ”i przeciążenia najlepszej metody rozszerzenia„ System.Web. Mvc.InputExtensions.CheckBox (System.Web.Mvc.HtmlHelper, string.bool, object) „zawiera nieprawidłowe argumenty”.
vapcguy

2

Nadal możesz używać EditorFor. Po prostu przekaż styl / dowolny atrybut html jako ViewData.

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

Ponieważ EditorFor używa szablonów do renderowania, możesz zastąpić domyślny szablon dla swojej właściwości i po prostu przekazać atrybut stylu jako ViewData.

Więc Twój EditorTemplate chciałby, aby:

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

możesz używać w ten sposób; takie same dane wyjściowe Html.EditorFor, jak i możesz dodać atrybuty HTML


Z wyjątkiem TextBoxForignoruje wszelkie rodzaje DisplayFormatwniosków, które możesz próbować zastosować.
Eric K,

1

Po prostu utwórz własny szablon dla typu w 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"})

Wywołaj edytor z widoku z właściwością modelu:

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

Przepraszam za składnię VB. Tak właśnie się toczymy.


1

W moim przypadku próbowałem stworzyć szablon edytora wprowadzania liczb HTML5, który mógłby otrzymać dodatkowe atrybuty. Lepszym podejściem byłoby napisanie własnego pomocnika HTML, ale ponieważ mam już swój szablon .ascx, wybrałem następujące podejście:

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

Ten brzydki bit tworzy dane wejściowe typu liczbowego i szuka słownika ViewData z kluczowymi „atrybutami”. Będzie iterować przez słownik, dodając swoje pary klucz / wartość jako atrybuty. Regex w atrybucie ID jest niepowiązany i występuje, ponieważ gdy jest używany w kolekcji, GetFullHtmlFieldId()zwraca identyfikator zawierający nawiasy kwadratowe, []które normalnie byłyby oznaczone jako podkreślenia.

Ten szablon nazywa się wtedy następująco:

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

Pełne, ale działa. Prawdopodobnie możesz użyć odbicia w szablonie, aby użyć nazw właściwości jako nazw atrybutów zamiast używania słownika.


1

Ustaw warunek za pomocą ViewDataw kontrolerze

ViewData["Modifiable"] = model.recProcessed;

Następnie użyj tego widoku danych w szablonie edytora, aby ustawić atrybut HTML kontrolki

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

0

Rozwiązanie MVC 5.1 i nowsze (połączy lokalne HtmlAttributes i zdefiniowane w EditorTemplates):

Shared \ EditorTemplates \ String.cshtml:

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

Rozszerzenia:

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

Stosowanie:

@Html.EditorFor(m => m.PromotionalCode, new { htmlAttributes = new { ng_model = "roomCtrl.searchRoomModel().promoCode" }})

1
Dzięki, to działa. Z wyjątkiem wszystkich atrybutów data_xxx, gdzie musiałem zamienić _ na - podczas rzutowania source1i source2jako IDictionary<string, object>. Zrobiłem tę funkcję: private static IDictionary<string, object> ToHtmlAttributesDictionary(this IEnumerable<KeyValuePair<string, object>> dico) { return dico.ToDictionary(s => s.Key.Replace('_', '-'), s => s.Value); }
Marien Monnier
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.