MVC 3: Bedingtes Hinzufügen des deaktivierten Attributs mit den HtmlHelpers

73

Ich habe eine ASP.Net MVC 3-Webanwendung und füge ein Kontrollkästchen zu einer Ansichtsseite hinzu, indem ich die HtmlHelper-Klasse wie folgt verwende ...

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

Ich möchte das deaktivierte Attribut basierend auf einer Ansichtsstatus-Eigenschaft bedingt hinzufügen. Grundsätzlich wäre folgendes ideal ...

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

Leider funktioniert dies aufgrund der Art des deaktivierten Attributs nicht, da alle dem deaktivierten Attribut zugewiesenen Werte (sogar "false") als wahr übersetzt werden.

Ich habe bereits über einige Lösungen nachgedacht, um dieses Problem zu umgehen. Die Frage ist also nicht, wie ich das tun kann. Aber gibt es einen einfachen Weg wie die oben beschriebene gewünschte Methode? oder muss ich auf eines der folgenden zurückgreifen? ..

Was ich weiß, könnte ich tun ...

  1. Erstellen Sie eine if / else-Anweisung und schreiben Sie in verschiedene Html.CheckBoxZeilen (nicht gut für die Lesbarkeit - und möglich mit einer Markup-Warnung - nicht sicher).

  2. Überspringen Sie die HtmlHelper-Klasse und schreiben Sie das Tag von Hand, um bessere bedingte Attribute zu ermöglichen (hält den Code kürzer, fügt aber Inkonsistenz hinzu)

  3. Erstellen Sie einen benutzerdefinierten Helfer, der einen "deaktivierten" Parameter verwendet (die sauberste Lösung, erfordert jedoch unerwünschte zusätzliche Methoden - wahrscheinlich die bisher beste Option).

Musefan
quelle
Schauen Sie sich meine Antwort hier an (verwendet den 3. Ansatz, den Sie aufgelistet haben): stackoverflow.com/a/43131930/6680521
Extragorey

Antworten:

52

Definieren Sie dies irgendwo in Ihrer Ansicht / Ihren Helfern

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

Dann benutze :

@Html.TextBox("name", "value", @getHtmlAttributes(Model.ReadOnly, "test"))
BigMike
quelle
Gute Antwort. Am Ende hatte ich dieselbe Logik ausgeführt, außer mit einer Bedingung für eine einzelne Zeile (glaube ich ternär) als Teil des Aufrufs von Html.CheckBox. Ihre Lösung ist viel schöner, danke;)
Musefan
Vielen Dank, für Informationen, ich vermute, dass der bessere Weg darin besteht, eine EditorTemplate für das Modell zu definieren, die Funktion intern zu definieren und auch eine Art clientseitige jQuery-Magie für alles bereitzustellen. Zum Beispiel beim Load-Ereignis eines enthaltenden DIV.
BigMike
1
mmm .. das obige Beispiel funktioniert nicht , wenn ich es mit .TextBoxFor () verwenden (statt .TextBox ()) - Anonymous-Projektions initializer sollte einfacher Name oder Mitglied Zugang Ausdruck sein ...
user1025852
Wie machen Sie dasselbe für das disableoptionale Hinzufügen ?
Juan Carlos Oropeza
Fügen Sie der Funktion einfach einen bool-Parameter hinzu und stellen Sie den Wert von @readonly entsprechend ein. Ich habe den Kontakt zu .NET MVC verloren und weiß daher nicht genau, ob diese Antwort für die neueste Version noch gilt (ich hoffe, sie haben eine bessere Möglichkeit hinzugefügt, dies zu tun).
BigMike
33

Hier ist meine Antwort auf diese ähnliche Frage: https://stackoverflow.com/a/13922813/495000


Ich habe den folgenden Helper erstellt - er benötigt einen booleschen und einen anonymen Objekt. Wenn disabled auf true gesetzt ist, wird das Attribut disabled dem anonymen Objekt (das eigentlich ein Wörterbuch ist) mit dem Wert "disabled" hinzugefügt, andernfalls wird die Eigenschaft überhaupt nicht hinzugefügt.

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

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

   return dictionary;
}

Ein Beispiel dafür in Aktion:

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

Ein großer Vorteil dieses Ansatzes war für mich, dass er mit praktisch allen MVC HtmlHelpers funktioniert, da alle Überladungen haben, die ein RouteValueDictionary anstelle eines anonymen Objekts akzeptieren.

Vorsichtsmaßnahmen :
HtmlHelper.AnonymousObjectToHtmlAttributes()Verwendet einige ausgefallene Code-Ninja-Arbeiten, um Dinge zu erledigen. Ich bin mir nicht ganz sicher, wie performant es ist ... aber es war ausreichend für das, wofür ich es benutze. Ihr Kilometerstand kann variieren.

Ich mag den Namen nicht besonders - aber ich könnte mir nichts Besseres einfallen lassen. Das Umbenennen ist einfach.

Ich mag auch die Verwendungssyntax nicht - aber ich könnte mir auch hier nichts Besseres einfallen lassen. Es sollte nicht schwierig sein, sich zu ändern. Eine Erweiterungsmethode für ein Objekt ist eine Idee ... Sie würden am Ende etwas haben, new { @class = "someClass" }.ConditionalDisable(true)aber wenn Sie nur das Deaktivierungsattribut möchten und nichts zusätzliches hinzufügen möchten, erhalten Sie etwas Grobes wie new {}.ConditionalDisable(true);und Sie erhalten auch eine Erweiterung Methode, die für alle auftaucht object... was wahrscheinlich nicht wünschenswert ist.

Mir
quelle
Eine äußerst elegante Lösung. Dies ist das Beste, was ich gefunden habe. Ich habe vorher über eine Erweiterungsmethode nachgedacht, aber sie wäre einfach viel zu allgemein.
Anon343224user
Dies ist eine großartige Lösung, aber ich kann nicht scheinen, es mit einem Html.ActionLink
Ciaran Gallagher
Gute Lösung, aber es funktioniert nicht mit Html.EditorFor auch
Dark_Knight
12

Wenn Sie eine knappere Syntax wünschen, ohne eine Hilfsfunktion zu benötigen, können Sie eine ternäre Anweisung verwenden, wenn Sie das Wörterbuch definieren, das für die HTML-Attribute des @ HTML.Checkbox-Hilfsprogramms verwendet wird ...

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

In diesem Fall ist Model.ReadOnly false, null wird als Wörterbuch der HTML-Attribute übergeben.

Andy Brudtkuhl
quelle
7
Dies kann sehr ausführlich werden, wenn Sie andere Attribute festlegen.
ScottE
@ScottE Könnten Sie dann nicht einfach eine Eigenschaft in Ihrer Ansicht [Modell] einrichten, die alle umfangreichen Berechnungen für Sie ausführt? Dann ist es in der cshtml so einfach wie im obigen Beispiel.
Ganders
1
Dies funktioniert nicht, wenn Sie die Eigenschaft @class weiterhin auf die falsche Bedingung setzen möchten.
Jaxidian
3

Das Hinzufügen des deaktivierten Attributs clientseitig funktioniert für mich. Beachten Sie, dass Sie überprüfen sollten, welche Felder serverseitig bearbeitet werden dürfen. Dies gilt jedoch auch für den Fall, dass das Attribut disabled auch dekorativ deklariert wird.

In diesem Beispiel habe ich alle Kinder eines Formulars mit jQuery deaktiviert.

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

            $(document).ready(function() {

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

        </script>
    }
gb2d
quelle
2

Was denkst du über meine einfache Lösung? Es funktioniert problemlos mit beiden möglichen HtmlAttributesTypen:

  • Dictionary<string, object>
  • Anonymous Object::

Zuerst fügen Sie die folgenden einfachen extension classzu einem Projekt:

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

Jetzt im Blick:

Beispiel1 ( HtmlAttributesTyp als Anonymous Object)

@{
  var hasDisabled=true; 
}

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

Beispiel 2 ( HtmlAttributesTyp als Dictionary<string, object>)

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

Ändern Sie jetzt einfach den hasDisabledWert in trueoder false!


Beispiel 3 (Mehrere bedingte Eigenschaften)

@{
  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))
.
RAM
quelle
1
Das ist großartig, danke! Ein Kommentar: Ich empfehle die Verwendung HtmlHelper.AnonymousObjectToHtmlAttributesanstelle von, UnderlineToDashInDictionaryKeysda es bereits integriert ist. ;)
Rami A.
1
@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;
    }
}
Alan Singfield
quelle
0

Ich mochte @ gb2d Antwort, hier ist es in JS mit getElementsByClassName von hier getElementByClass (). SetAttribute funktioniert nicht

<script type="text/javascript">
    var list, index;
    list = document.getElementsByClassName("form-control");
    for (index = 0; index < list.length; ++index) {
        list[index].setAttribute('disabled', true);
    }
</script>
sam80e
quelle