MVC 3: условное добавление отключенного атрибута с помощью HtmlHelpers

У меня есть веб-приложение ASP.Net MVC 3, и я добавляю флажок на страницу просмотра с использованием класса HtmlHelper, например...

@Html.CheckBox("CheckBox1", true, new { @class = "Class1" })

Что я хочу сделать, это условно добавить отключенный атрибут на основе свойства представления. В принципе, было бы идеально...

@Html.CheckBox("CheckBox1", true, new { @class = "Class1", @disabled = Model.ReadOnly })

К сожалению, из-за характера отключенного атрибута это не будет работать, потому что любое значение, присвоенное отключенному атрибуту (даже "false" ), будет переведено как true.

Я уже думал о нескольких решениях, чтобы обойти эту проблему, поэтому вопрос заключается не в том, как я могу это сделать. Но, скорее, есть простой способ, как и желаемый метод выше? или мне нужно прибегнуть к одному из следующих?..

То, что я знаю, я мог бы сделать...

  • Создайте инструкцию if/else и напишите в разные строки Html.CheckBox (не очень хорошо читаемые), и возможно сбрасывать предупреждение о разметке - не уверен)

  • Пропустить класс HtmlHelper и вручную записать тег, позволяющий улучшить условные атрибуты (сокращает код, но добавляет несогласованность)

  • Создайте настраиваемый помощник, который принимает параметр "отключен" (самое чистое решение, но требует нежелательных дополнительных методов - возможно, лучший вариант до сих пор)

Ответ 1

Определите это где-нибудь в вашем представлении/помощниках

@functions {
 object getHtmlAttributes (bool ReadOnly, string CssClass) 
 {
     if (ReadOnly) {
         return new { @class = CssClass, @readonly = "readonly" };
     }
     return new { @class = CssClass };
 }
}

Затем используйте:

@Html.TextBox("name", "value", @getHtmlAttributes(Model.ReadOnly, "test"))

Ответ 2

Вот мой ответ из этого похожего вопроса: fooobar.com/questions/94145/...


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

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() использует некоторую работу ниндзя для модного кода, чтобы все было сделано. Я не совсем уверен, насколько он эффективен... но этого было достаточно для того, для чего я его использую. Ваш пробег может отличаться.

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

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

Ответ 3

Если вам нужен более сложный синтаксис, не требующий вспомогательной функции, вы можете использовать трехмерный оператор при определении словаря, используемого для атрибутов html @HTML.Checkbox helper...

@Html.CheckBox("CheckBox1", true, Model.ReadOnly 
       ? new { @class = "Class1", @disabled = Model.ReadOnly } 
       : null)

В этом случае Model.ReadOnly является false, null передается как словарь атрибутов html.

Ответ 4

Выполнение добавления отключенной клиентской части атрибута работает для меня. Обратите внимание, что вы должны проверить, какие поля разрешены для редактирования на стороне сервера, но это верно для того, где атрибут disabled объявлен также декоративно.

В этом примере я отключил все элементы формы, используя jQuery.

    if (Model.CanEdit)
    {
        <script type="text/javascript">

            $(document).ready(function() {

                $('#editForm *').attr('disabled', true);
            });

        </script>
    }

Ответ 5

@Html.TextBoxFor(m => m.FieldName, Html.FixBoolAttributes(new {
    @class = "myClass",
    @readonly = myFlag  
}))


public static class BooleanAttributeFix
{
    /// <summary>
    /// Normalises HTML boolean attributes so that readonly=true becomes readonly="readonly" and
    /// readonly=false removes the attribute completely.
    /// </summary>
    /// <param name="htmlHelper"></param>
    /// <param name="htmlAttributes"></param>
    /// <returns></returns>
    public static RouteValueDictionary FixBoolAttributes(this HtmlHelper htmlHelper, object htmlAttributes)
    {
        var attrs = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes);

        foreach(var attrName in new[] { "disabled", "readonly" })
        {
            object value;
            if(attrs.TryGetValue(attrName, out value))
            {
                if(isTruthy(value))
                {
                    // Change from readonly="true" to readonly="readonly"
                    attrs[attrName] = attrName; 
                }
                else
                {
                    // Remove attribute entirely
                    attrs.Remove(attrName); 
                }
            }
        }
        return attrs;
    }

    /// <summary>
    /// Apply similar loose rules like javascript does for whether a value is true or not.
    /// e.g. 1 = true, non-empty string = true and so on.
    /// </summary>
    /// <param name="val"></param>
    /// <returns></returns>
    private static bool isTruthy(object val)
    {   
        if(val == null)
            return false;

        if(val is string)
        {
            return !String.IsNullOrEmpty((string)val);
        }

        Type t = val.GetType();

        if(t.IsValueType && Nullable.GetUnderlyingType(t) == null)
        {
            // If a non-nullable value type such as int we want to check for the
            // default value e.g. 0.
            object defaultValue = Activator.CreateInstance(t);

            // Use .Equals to compare the values rather than == as we want to compare
            // the values rather than the boxing objects.
            // See http://stackoverflow.com/questions/6205029/comparing-boxed-value-types
            return !val.Equals(defaultValue);
        }

        return true;
    }
}

Ответ 6

Что вы думаете о моем простом решении? Он легко работает с обоими возможными типами HtmlAttributes:

  • Dictionary<string, object>
  • Anonymous Object:

Сначала добавьте следующий простой extension class в ваш проект:

public static class HtmlAttributesExtensions
{
    public static IDictionary<string, object> AddHtmlAttrItem(this object obj, string name, object value, bool condition)
    {
        var items= !condition ? new RouteValueDictionary(obj) : new RouteValueDictionary(obj) {{name, value}};
        return UnderlineToDashInDictionaryKeys(items);
    }
    public static IDictionary<string, object> AddHtmlAttrItem(this IDictionary<string, object> dictSource, string name, object value, bool condition)
    {
        if (!condition)
            return dictSource;

        dictSource.Add(name, value);
        return UnderlineToDashInDictionaryKeys(dictSource);
    }
    private static IDictionary<string, object> UnderlineToDashInDictionaryKeys(IDictionary<string,object> items)
    {
        var newItems = new RouteValueDictionary();
        foreach (var item in items)
        {
            newItems.Add(item.Key.Replace("_", "-"), item.Value);
        }
        return newItems;
    }
}

Теперь в режиме просмотра:

Пример1 (HtmlAttributes введите Anonymous Object)

@{
  var hasDisabled=true; 
}

@Html.CheckBox("CheckBox1"
              , true
              , new { @class = "Class1"}
               .AddHtmlAttrItem("disabled", "disabled", hasDisabled))
.

Пример 2 (HtmlAttributes type as Dictionary<string, object>)

@Html.CheckBox("CheckBox1"
              , true
              , new Dictionary<string, object> { { "class", "Class1" }
               .AddHtmlAttrItem("disabled", "disabled", hasDisabled))
.

Теперь просто измените значение makeItReadOnly на true в false!


Пример3 (Несколько условных свойств)

@{
  var hasDisabled=true;
  var hasMax=false ;
  var hasMin=true ;
}

@Html.CheckBox("CheckBox1"
              , true
              , new { @class = "Class1"}
               .AddHtmlAttrItem("disabled", "disabled", hasDisabled)
               .AddHtmlAttrItem("data-max", "100", hasMax)
               .AddHtmlAttrItem("data-min", "50", hasMin))
.