Условно отключить Html.DropDownList

Как я могу изменить это объявление DropDownList, чтобы отключенный атрибут был условно включен/отключен?

<%= Html.DropDownList("Quantity", new SelectList(...), new{@disabled="disabled"} %>

нерабочий пример:

<%= Html.DropDownList("Quantity", new SelectList(...), new{@disabled=Model.CanEdit?"false":"disabled"} %>

p.s. добавление условия if вокруг всего оператора не является желаемым подходом:)

EDIT: на основе этого метода расширения из другого вопроса. Я придумал следующее расширение:

public static IDictionary<string, object> Disabled (this object obj, bool disabled)
{
  return disabled ? obj.AddProperty ("disabled", "disabled") : obj.ToDictionary ();
}

который затем можно использовать как

<%= Html.DropDownList("Quantity", new SelectList(...), new{id="quantity"}.Disabled(Model.CanEdit) %>

Ответ 1

Пожалуйста, не пишите код спагетти. Для этой цели есть помощники Html:

public static MvcHtmlString DropDownList(this HtmlHelper html, string name, SelectList values, bool canEdit)
{
    if (canEdit)
    {
        return html.DropDownList(name, values);
    }
    return html.DropDownList(name, values, new { disabled = "disabled" });
}

И затем:

<%= Html.DropDownList("Quantity", new SelectList(...), Model.CanEdit) %>

Или, может быть, вы могли бы придумать что-то еще лучше (если модель содержит параметры):

<%= Html.DropDownList("Quantity", Model) %>

Вы также получите бонус, чтобы иметь более единый тестируемый код.

Ответ 2

Нет необходимости добавлять вспомогательные методы, вы можете просто использовать

<%= Html.DropDownList("Quantity", new SelectList(...), IsEditable == true ? new { @disabled = "disabled" } as object : new {} as object %>

Если вы должны удалить записи as object, это не сработает, потому что по умолчанию new {} - это динамический объект, скомпилированный во время выполнения, поэтому два возможных объекта должны иметь одинаковые свойства. Но параметр атрибутов Html на самом деле является просто объектом, поэтому эту динамику можно отличить как объекты, чтобы обойти это.

Это решение даже позволяет использовать несколько атрибутов HTML, где один является необязательным, а другой - нет, т.е. class='whatever' не является необязательным, но disabled - это значит, что вы помещаете class='whatever' в оба объекта, но только один из них во-первых. Ответ Димитрова не поддерживает какие-либо пользовательские атрибуты, отличные от отключенных.

Ответ 3

Один из вариантов - создать пользовательскую версию Html.DropDownList, которая принимает дополнительный параметр и делает то, что вы хотите... но тогда вам нужно будет создать новый для каждого типа помощника - TextBoxFor, TextAreaFor, CheckBoxFor и т.д.... и вам все равно нужно выяснить, как заставить его работать.

Вместо этого я решил создать Html Helper для замены обычного анонимного объекта HtmlAttributes, поскольку он будет совместим со всеми помощниками, которые используют HtmlAttributes без какой-либо специальной работы. Это решение также позволяет вам передавать дополнительные атрибуты, такие как Class, Name или что угодно. Он не блокирует вас только отключением.

Я создал следующий Помощник - он принимает логический и анонимный объект. Если значение отключено, оно добавляет отключенный атрибут анонимному объекту (который фактически является Словарем) со значением "отключено", в противном случае он не добавляет свойство вообще.

public static RouteValueDictionary ConditionalDisable(
   bool disabled, 
   object htmlAttributes = null)
{
   var dictionary = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes);

   if (disabled)
      dictionary.Add("disabled", "disabled");

   return dictionary;
}


Пример этого в действии:

@Html.TextBoxFor(m => m.SomeProperty,    
   HtmlHelpers.ConditionalDisable(true, new { @class = "someClass"))


Огромное преимущество этого подхода для меня заключалось в том, что он работает практически со всеми MVC HtmlHelpers, поскольку у всех их есть Overloads, которые принимают RouteValueDictionary вместо анонимного объекта.

Предостережение:
HtmlHelper.AnonymousObjectToHtmlAttributes() использует некоторую работу ниндзя причудливого кода, чтобы все было сделано. Я не совсем уверен, насколько он эффективен... но этого было достаточно для того, для чего я его использую. Ваш пробег может отличаться.

Мне не нравится это имя, но я не мог придумать ничего лучшего. Переименование легко.

Я также не люблю синтаксис использования, но опять-таки я не мог придумать ничего лучшего. Это не должно быть трудно изменить. Метод расширения на object - это одна идея... вы получите new { @class = "someClass" }.ConditionalDisable(true), но тогда, если вам нужен только атрибут disable, и у вас нет ничего лишнего, чтобы добавить вас к чему-то большему, например new {}.ConditionalDisable(true); и вы также получаете метод расширения, который отображается для всех объектов... что, вероятно, нежелательно.

Ответ 4

@bool IsEditable=true;

@if (IsEditable)
{
    Html.DropDownListFor(m => m, selectList);
}
else
{
    Html.DropDownListFor(m => m, selectList, new { disabled = "disabled" })
}

Ответ 5

Сильно типизированная версия:

 public static MvcHtmlString DropDownListFor<TModel, TProperty>(this HtmlHelper<TModel>    html,
                                                                   Expression<Func<TModel, TProperty>> expression,
                                                                   IEnumerable<SelectListItem> selectList,
                                                                   string optionText, bool canEdit)
    {
        if (canEdit)
        {
            return html.DropDownListFor(expression, selectList, optionText);
        }
        return html.DropDownListFor(expression, selectList, optionText, new { disabled = "disabled" });
    }

Ответ 6

Я не знаю, предлагает ли ASP.NET более сжатый особый подход, но предположительно вы могли бы сделать:

<%= Html.DropDownList("Quantity", new SelectList(...), Model.CanEdit? new{@class="quantity"} : new{@class="quantity", @disabled:"disabled"}) %>