web-dev-qa-db-fra.com

MVC 3: Ajout conditionnel de l'attribut désactivé avec les HtmlHelpers

J'ai une application Web ASP.Net MVC 3 et j'ajoute une case à cocher à une page d'affichage à l'aide de la classe HtmlHelper, comme ceci ...

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

Ce que je veux faire est d'ajouter conditionnellement l'attribut disabled en fonction d'une propriété d'état d'affichage. En gros, ce qui suit serait idéal ...

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

Malheureusement, en raison de la nature de l'attribut disabled, cela ne fonctionnera pas car la valeuranyattribuée à l'attribut disabled (même "false") sera traduite par true.

J'ai déjà réfléchi à quelques solutions pour résoudre ce problème. La question n'est donc pas de savoir comment je peux le faire. Mais plutôt, existe-t-il un moyen simple comme la méthode souhaitée ci-dessus? ou dois-je recourir à l'un des éléments suivants? ..

Ce que je sais que je pourrais faire ...

  1. Créez une instruction if/else et écrivez dans différentes lignes Html.CheckBox (ce qui n'est pas excellent pour la lisibilité - et possible avec un avertissement de balisage - pas sûr)

  2. Ignore la classe HtmlHelper et écrit à la main la balise permettant de meilleurs attributs conditionnels (garde le code plus court, mais ajoute une incohérence)

  3. Créez un assistant personnalisé prenant un paramètre "disabled" (la solution la plus propre, mais nécessite des méthodes supplémentaires non souhaitées - probablement la meilleure option jusqu'à présent).

65
musefan

Définissez ceci quelque part dans votre vue/assistants

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

Alors utilisez:

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

Voici ma réponse à cette question similaire: https://stackoverflow.com/a/13922813/495000


J'ai créé le Helper suivant - il faut un objet booléen et anonyme. Si disabled est défini sur true, il ajoute l'attribut disabled à l'objet anonyme (qui est en fait un dictionnaire) avec la valeur "disabled", sinon il n'ajoute pas la propriété.

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

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

   return dictionary;
}

Un exemple en action:

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

Pour moi, l’un des grands avantages de cette approche est qu’elle fonctionne avec presque tous les MVC HtmlHelpers car ils ont tous des surcharges qui acceptent un objet RouteValueDictionary au lieu d’un objet anonyme.

Mises en garde:
HtmlHelper.AnonymousObjectToHtmlAttributes() utilise du code ninja pour faire avancer les choses. Je ne sais pas trop à quel point il est performant ... mais c'est suffisant pour ce que je l'utilise. Votre kilométrage peut varier.

Je n'aime pas particulièrement le nom - mais je ne pourrais rien trouver de mieux. Renommer est facile.

Je n'aime pas non plus la syntaxe d'utilisation, mais encore une fois, je ne pourrais rien trouver de mieux. Il ne devrait pas être difficile de changer. Une méthode d'extension sur object est une idée ... vous vous retrouveriez avec new { @class = "someClass" }.ConditionalDisable(true) mais si vous ne voulez que l'attribut disable et n'avez rien d'autre à ajouter, vous obtenez quelque chose de brut comme new {}.ConditionalDisable(true); et vous vous retrouvez également avec une méthode d'extension qui apparaît pour tous les object... ce qui n'est probablement pas souhaitable.

30
Mir

Si vous voulez une syntaxe plus succincte sans nécessiter de fonction d'assistance, vous pouvez utiliser une instruction ternaire lors de la définition du dictionnaire utilisé pour les attributs HTML de l'aide @ HTML.Checkbox ...

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

Dans ce cas, Model.ReadOnly a la valeur false, null est transmis en tant que dictionnaire d'attributs HTML.

9
Andy Brudtkuhl
@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;
    }
}
1
Alan Singfield

Que pensez-vous de ma solution simple? Cela fonctionne facilement avec les deux types HtmlAttributes possibles:

  • Dictionary<string, object>
  • Anonymous Object:

First ajoutez le simple extension class suivant à votre projet:

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

Maintenant visible:

Exemple1 (HtmlAttributes tapez Anonymous Object)

@{
  var hasDisabled=true; 
}

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

Exemple 2 (HtmlAttributes tapez Dictionary<string, object>)

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

Maintenant changez simplement la valeur hasDisabled en true ou false!


Exemple3 (propriétés conditionnelles multiples)

@{
  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))
.
1
RAM

L'ajout de l'attribut désactivé côté client fonctionne pour moi. Notez que vous devez vérifier quels champs sont autorisés à être modifiés côté serveur, mais c'est également le cas lorsque l'attribut désactivé est déclaré de manière décorative.

Dans cet exemple, j'ai désactivé tous les enfants d'un formulaire à l'aide de jQuery.

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

            $(document).ready(function() {

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

        </script>
    }
0
gb2d