Атрибуты HTML для EditorFor () в ASP.NET MVC


129

Почему я не могу передать атрибуты html в EditorFor()? например;

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

Я не хочу использовать метаданные

Обновление : решение заключалось в том, чтобы вызвать это из представления:

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

и использовать ViewData["Modifiable"]в моих пользовательских EditorTemplates / String.ascx, где у меня есть некоторая логика представления, которая определяет, следует ли добавлять атрибуты только для чтения и / или отключенные во входные данные. Переданный анонимный объект EditorFor()является вызываемым параметром, additionalViewDataи его свойства передаются в шаблон редактора в ViewDataколлекция.


19
А если серьезно, то до сих пор в MVC3 это ограничение не имеет смысла и очень раздражает. Люди во всем мире тратят время и выдергивают волосы с этой ерундой. Я отправляю это в Microsoft Connect.
Джуниор Майе


3
Это только у меня, или это похоже на то, что это должно быть так просто?
Eric K

Возможно, это поможет вам: [EditorFor-Реализация с классами CSS и атрибутами HTML] [1] [1]: stackoverflow.com/questions/12675708/…
FunThom

Возможный дубликат свойств EditorFor () и html
Gyrfalcon

Ответы:


96

EditorForработает с метаданными, поэтому, если вы хотите добавить атрибуты html, вы всегда можете это сделать . Другой вариант - просто написать собственный шаблон и использовать TextBoxFor:

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

Метаданные не будут работать, потому что атрибуты html различаются в зависимости от других свойств в модели, другими словами логика домена или модели должна определять атрибуты html, а не статические метаданные. Или я упустил суть, могу ли я установить метаданные динамически?
Опечатка Джонсон

Что есть PeriodType? Разве это не простая собственность? Если это сложный объект, вы можете настроить весь шаблон, поместив его часть, ~/Views/ControllerName/EditorTemplates/SomeType.ascxгде SomeType- имя типа PeriodTypeсвойства.
Дарин Димитров

Также ваш предлагаемый код будет означать передачу всей модели в частичный шаблон, который обращается к определенному свойству, это означает, что у меня есть частичный для каждого свойства?
Опечатка Джонсон

2
Я понимаю тебя сейчас. Я могу использовать <% = Html.EditorFor (model => model.Control.PeriodEndDate, new {Modifiable = model.Control.PeriodEndDateModifiable})%> и использовать ViewData ["PeriodEndDateModifiable"] в моих пользовательских EditorTemplates / String.ascx. Спасибо
Typo Johnson

1
@ JuniorMayhé, я бы не назвал это скучным ограничением. Если вы подумаете более внимательно, то поймете, что это имеет смысл. Фактически, весь смысл помощника EditorFor в том, что у вас может быть соответствующий шаблон. Этот шаблон может содержать любую разметку. Не обязательно отдельный элемент. @classНапример, было бы бессмысленно определять шаблон редактора, содержащий 3 блока. Помощник Html.TextBoxFor позволяет вам определить это, потому что вы знаете, что этот помощник генерирует - текстовое поле, поэтому имеет смысл определить класс для элемента ввода.
Дарин Димитров

115

Обновление MVC 5.1 теперь напрямую поддерживает описанный ниже подход, поэтому он работает и для встроенного редактора. http://www.asp.net/mvc/overview/releases/mvc51-release-notes#new-features (Это либо случай, когда Великий ум думает одинаково, либо они читают мой ответ :)

Конец обновления

Если вы используете собственный шаблон редактора или MVC 5.1, который теперь поддерживает приведенный ниже подход непосредственно для встроенных редакторов.

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

затем в вашем шаблоне (не требуется для простых типов в MVC 5.1)

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

5
Эта новая функция отлично решает первоначальную проблему, поставленную четыре года назад.
CoderSteve

1
Если вместо TextBoxFor и подобных ему помощников я использую кучу настраиваемых шаблонов редакторов, я бы не сказал, что добавление ViewData [...] в каждый из них - фантастическая идея ... :(
Александр

42

Начиная с MVC 5.1, теперь вы можете делать следующее:

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

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


Ссылка выше мертва. Это доступно в MVC 5.1, подробнее здесь: asp.net/mvc/overview/releases/mvc51-release-notes#new-features
Алаа Масуд

1
Спасибо, ссылку тоже исправил.
vtforester 03

2
как слить htmlAttributes?
Барт Каликсто

Это работает только со встроенными / стандартными EditorForшаблонами. Если вы реализуете свой собственный, вы должны следовать ответу AntonK.
mxmissile 03


4

Вот синтаксис кода VB.Net для атрибутов html в редакторе MVC 5.1.

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

3

Почему бы просто не использовать

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

30
Одна из причин заключается в том, что DisplayFor не отображает ввод, поэтому значение теряется при обратной передаче.
ProfK

2
Другой причиной является конкретный сценарий, в котором редактор в настоящее время заблокирован, но не всегда, и вы хотите сообщить, что данные потенциально можно редактировать - но не сейчас.
Мир

2

Если вы не хотите использовать метаданные, вы можете использовать [UIHint("PeriodType")]атрибут для украшения свойства или, если это сложный тип, вам не нужно ничего украшать. Затем EditorFor будет искать файл PeriodType.aspx или ascx в папке EditorTemplates и использовать его вместо этого.


Спасибо, я могу это сделать, если if / else в моем шаблоне редактора требуется только для определенных полей
Typo Johnson

Я, хотя вы сказали, что не можете изменить модель (не свой код), поэтому декорирование с помощью UIHint напрямую не было бы вариантом.
Даррел Ли

2

Сегодня я боролся с той же проблемой для флажка, который привязывается к nullable bool, и, поскольку я не могу изменить свою модель (не свой код), мне пришлось придумать лучший способ справиться с этим. Это немного грубая сила, но она должна работать в 99% случаев, с которыми я могу столкнуться. Очевидно, вам придется вручную заполнить допустимые атрибуты для каждого типа ввода, но я думаю, что я получил их все для флажка.

В моем шаблоне редактора 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)

Я добавил Dictionary<string,string> attribs = new Dictionary<string,string>();и attribs.Add("tabindex","16");в одном из этих @( )блоков вверху моей страницы. Затем я сделал это @Html.CheckBox("IsActive", Model.IsActive.HasValue ? Model.IsActive : false, attribs)на своей странице и получил ошибку: «System.Web.Mvc.HtmlHelper <MyProject.Models.MyObject>» не содержит определения для «CheckBox» и перегрузки лучшего метода расширения «System.Web. Mvc.InputExtensions.CheckBox (System.Web.Mvc.HtmlHelper, string.bool, object) 'имеет недопустимые аргументы ".
vapcguy

2

Вы все еще можете использовать EditorFor. Просто передайте стиль / любой атрибут html как ViewData.

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

Поскольку EditorFor использует шаблоны для рендеринга, вы можете переопределить шаблон по умолчанию для своего свойства и просто передать атрибут стиля как ViewData.

Итак, вашему EditorTemplate нужно следующее:

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

вы можете использовать вот так; тот же вывод с Html.EditorFor, и вы можете добавить свои атрибуты html


Кроме того, что TextBoxForигнорирует любой тип, DisplayFormatкоторый вы можете попытаться применить.
Eric K

1

Просто создайте свой собственный шаблон для типа в 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"})

Вызовите редактор из вашего представления со свойством модели:

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

Простите за синтаксис VB. Вот так мы и катимся.


1

В моем случае я пытался создать шаблон редактора ввода чисел HTML5, который мог бы получать дополнительные атрибуты. Более аккуратным подходом было бы написать собственный HTML Helper, но поскольку у меня уже был шаблон .ascx, я выбрал следующий подход:

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

Этот уродливый бит создает ввод числового типа и ищет словарь ViewData с ключевыми «атрибутами». Он будет перебирать словарь, добавляя пары ключ / значение в качестве атрибутов. Регулярное выражение в атрибуте ID не имеет отношения и существует потому, что при использовании в коллекции GetFullHtmlFieldId()возвращает идентификатор, содержащий квадратные скобки, []которые обычно избегают как символы подчеркивания.

Затем этот шаблон вызывается так:

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

Многословно, но работает. Вероятно, вы могли бы использовать отражение в шаблоне, чтобы использовать имена свойств в качестве имен атрибутов вместо использования словаря.


1

Задайте условие, используя ViewDataв контроллере

ViewData["Modifiable"] = model.recProcessed;

Затем используйте эти данные представления в шаблоне редактора, чтобы установить атрибут html элемента управления.

@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 и выше (объединит локальные атрибуты HtmlAttributes и определенные в EditorTemplates):

Shared \ EditorTemplates \ String.cshtml:

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

Расширения:

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

Использование:

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

1
Спасибо, работает. За исключением всех атрибутов data_xxx, где мне пришлось заменить _ на - при преобразовании source1и source2as IDictionary<string, object>. Я выполнил эту функцию: private static IDictionary<string, object> ToHtmlAttributesDictionary(this IEnumerable<KeyValuePair<string, object>> dico) { return dico.ToDictionary(s => s.Key.Replace('_', '-'), s => s.Value); }
Мариен Монье
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.