asp.net mvc: Warum generiert Html.CheckBox eine zusätzliche versteckte Eingabe?

215

Mir ist gerade aufgefallen, dass Html.CheckBox("foo")2 Eingänge anstelle von einem generiert werden. Weiß jemand, warum das so ist?

<input id="foo" name="foo" type="checkbox" value="true" />
<input name="foo" type="hidden" value="false" /> 
Omu
quelle
1
Die Antwort von ericvg in der möglichen doppelten Frage erklärt auch, was der Modellordner tut, wenn sowohl das Kontrollkästchen als auch ausgeblendete Felder gesendet werden.
Theophilus
1
Ich hasse dieses Ding, das sich mit meiner Frage zusammengetan hat.
Jerry Liang
2
Seltsame Implementierung von mvc. Das Senden beider Werte ist überhaupt nicht sinnvoll. Ich habe Request.From ["myCheckBox"] überprüft und sein Wert war wahr, falsch. WTF. Ich musste das Steuerelement manuell in die Ansicht schreiben.
Nick Masao
Wenn dies wirklich unerwünscht ist, verwenden Sie nicht Html.CheckBox (...) und geben Sie einfach das HTML für ein Kontrollkästchen ein
wnbates

Antworten:

197

Wenn das Kontrollkästchen nicht aktiviert ist, wird das Formularfeld nicht gesendet. Deshalb gibt es im versteckten Feld immer einen falschen Wert. Wenn Sie das Kontrollkästchen nicht aktivieren, hat das Formular weiterhin einen Wert aus dem ausgeblendeten Feld. Auf diese Weise verarbeitet ASP.NET MVC Kontrollkästchenwerte.

Wenn Sie dies bestätigen möchten, aktivieren Sie das Kontrollkästchen nicht mit Html.Hidden, sondern mit <input type="checkbox" name="MyTestCheckboxValue"></input>. Lassen Sie das Kontrollkästchen deaktiviert, senden Sie das Formular und sehen Sie sich die auf der Serverseite veröffentlichten Anforderungswerte an. Sie werden sehen, dass es keinen Kontrollkästchenwert gibt. Wenn Sie ein verstecktes Feld hätten, würde es einen MyTestCheckboxValueEintrag mit falseWert enthalten.

LukLed
quelle
52
Wenn Sie das Kontrollkästchen nicht aktiviert haben, ist die Antwort offensichtlich falsch. Sie müssen den Artikel nicht zurücksenden. Dummes Design meiner Meinung nach.
Der Muffin-Mann
54
@ TheMuffinMan: Dies ist keine dumme Option. Nehmen wir an, Ihr Ansichtsmodell hat eine Eigenschaft namens IsActive, die trueim Konstruktor initiiert wird . Das Kontrollkästchen des Benutzers wird deaktiviert. Da der Wert jedoch nicht an den Server gesendet wird, wird er vom Modellordner nicht übernommen und der Eigenschaftswert wird nicht geändert. Der Modellordner sollte nicht davon ausgehen, dass der Wert, wenn er nicht gesendet wird, auf false gesetzt wurde, da Sie möglicherweise entscheiden, diesen Wert nicht zu senden.
LukLed
21
Es beginnt mit einem harmlosen Kontrollkästchen und bevor Sie es wissen, haben wir den Ansichtsstatus. Dann entwickelt es sich zu ASP.NET MVCForms.
Der Muffin-Mann
4
@LukLed Spät auf Ihren Kommentar antworten ... Ich denke, dass die Logik fehlerhaft ist, denn wenn Ihr Ansichtsmodell einen Eigenschaftstyp int hat und keine Eingabe in das Formular vorhanden ist, ist der Standardwert 0, da kein Wert zum Ändern eingegangen ist. Gleiches gilt für jede andere Eigenschaft. Der Standardwert für eine Zeichenfolge ist null. Wenn Sie keinen Wert senden, ist dieser null. In Ihrem Beispiel, in dem eine Eigenschaft im Konstruktor auf true gesetzt wird, ist dies ehrlich gesagt eine schlechte Entwurfsentscheidung, und sie wird jetzt sichtbar, da Kontrollkästchen in http land funktionieren.
Der Muffin-Mann
8
Diese Shadowing-Logik wird für deaktivierte Kontrollkästchen unterbrochen - sie senden falseWerte, auch wenn sie aktiviert sind, und das ist verwirrend. Deaktivierte Kontrollkästchen sollten überhaupt keinen Wert senden, wenn ASP.NET mit dem Standard-HTTP-Verhalten kompatibel sein soll.
JustAMartin
31

Sie können einen Helfer schreiben, um das Hinzufügen der versteckten Eingabe zu verhindern:

using System.Web.Mvc;
using System.Web.Mvc.Html;

public static class HelperUI
{
    public static MvcHtmlString CheckBoxSimple(this HtmlHelper htmlHelper, string name, object htmlAttributes)
    {
        string checkBoxWithHidden = htmlHelper.CheckBox(name, htmlAttributes).ToHtmlString().Trim();
        string pureCheckBox = checkBoxWithHidden.Substring(0, checkBoxWithHidden.IndexOf("<input", 1));
        return new MvcHtmlString(pureCheckBox);
    }
}

benutze es:

@Html.CheckBoxSimple("foo", new {value = bar.Id})
Alexander Trofimov
quelle
4
Dies hat mich für eine Minute gestolpert, also nur für den Fall ... Wenn sich Ihr Helfer in einem Namespace befindet, vergessen Sie nicht, @using Your.Name.Spaceoben in Ihrer .cshtml-Rasiermesser-Ansichtsdatei hinzuzufügen .
ooXei1sh
3
@ ooXei1sh Entweder das, oder setzen Sie Ihren Helfer in den Namespace System.Web.Mvc.Html, um in allen Ansichten zugänglich zu sein
Luke
1
Wenn Sie dies für ein Formular-Postback oder für das Posten über Ajax mit den Formularwerten verwenden möchten, müssen Sie den Wert über das Ereignis onchange im Kontrollkästchen auf true oder false setzen. @ Html.CheckBoxSimple (x => Model.Foo, neu {value = Model.Foo, onchange = "toggleCheck (this)"}). Dann in der Javascript-Funktion ToggleCompleted (el) {var Checked = $ (el) .is (': Checked'); $ ('# Foo'). Val (markiert); }
John81
12

Wenn das Kontrollkästchen aktiviert und gesendet ist, führen Sie dies aus

if ($('[name="foo"]:checked').length > 0)
    $('[name="foo"]:hidden').val(true);

Verweisen

Desmond
quelle
8

Der manuelle Ansatz ist folgender:

bool IsDefault = (Request.Form["IsDefault"] != "false");
Valamas
quelle
1
Und erhalten Sie dadurch garantiert den Wert des Kontrollkästchentyps und nicht den Wert des versteckten Typs?
Brian Sweeney
1
das ist irrelevant. Wenn das Kontrollkästchen nicht aktiviert ist, wird es nicht veröffentlicht. Die versteckte Box hat also 'false'. Wenn das Kontrollkästchen aktiviert ist, wird der Wert 'true, true' zurückgegeben (glaube ich), und dies ist nicht gleich 'false'.
Valamas
16
Nein, wenn das Kontrollkästchen aktiviert ist, werden sowohl true als auch false angezeigt, da sowohl das Kontrollkästchen als auch die ausgeblendeten Felder gültige Steuerelemente sind, die zurückgesendet werden sollen. Der Mvc-Ordner prüft dann, ob für einen bestimmten Namen ein wahrer Wert vorhanden ist, und bevorzugt in diesem Fall den wahren Wert. Sie können dies überprüfen, indem Sie die veröffentlichten Daten anzeigen. Es hat sowohl wahre als auch falsche Werte für einen einzelnen Namen.
ZafarYousafi
Dies brachte mich einmal, ich überprüfte, ob der Wert == wahr ist
Terry Kernan
8

Dies ist die stark typisierte Version von Alexander Trofimovs Lösung:

using System.Web.Mvc;
using System.Web.Mvc.Html;

public static class HelperUI
{
    public static MvcHtmlString CheckBoxSimpleFor<TModel>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, bool>> expression, object htmlAttributes)
    {
        string checkBoxWithHidden = htmlHelper.CheckBoxFor(expression, htmlAttributes).ToHtmlString().Trim();
        string pureCheckBox = checkBoxWithHidden.Substring(0, checkBoxWithHidden.IndexOf("<input", 1));
        return new MvcHtmlString(pureCheckBox);
    }
}
Ciro Corvino
quelle
4

Verwenden Sie Enthält, es funktioniert mit den zwei möglichen Post-Werten: "false" oder "true, false".

bool isChecked = Request.Form["foo"].Contains("true");
Dave D.
quelle
1

Die versteckte Eingabe verursachte Probleme mit gestalteten Kontrollkästchen. Also habe ich eine HTML-Hilfserweiterung erstellt, um die versteckte Eingabe außerhalb des Divs mit der CheckBox zu platzieren.

using System;
using System.Linq.Expressions;
using System.Text;
using System.Web.Mvc;
using System.Web.Routing;

namespace YourNameSpace
{
    public static class HtmlHelperExtensions
    {
        public static MvcHtmlString CustomCheckBoxFor<TModel, TValue>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TValue>> expression, string labelText)
        {
            //get the data from the model binding
            var fieldName = ExpressionHelper.GetExpressionText(expression);
            var fullBindingName = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(fieldName);
            var fieldId = TagBuilder.CreateSanitizedId(fullBindingName);
            var metaData = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
            var modelValue = metaData.Model;

            //create the checkbox
            TagBuilder checkbox = new TagBuilder("input");
            checkbox.MergeAttribute("type", "checkbox");
            checkbox.MergeAttribute("value", "true"); //the visible checkbox must always have true
            checkbox.MergeAttribute("name", fullBindingName);
            checkbox.MergeAttribute("id", fieldId);

            //is the checkbox checked
            bool isChecked = false;
            if (modelValue != null)
            {
                bool.TryParse(modelValue.ToString(), out isChecked);
            }
            if (isChecked)
            {
                checkbox.MergeAttribute("checked", "checked");
            }

            //add the validation
            checkbox.MergeAttributes(htmlHelper.GetUnobtrusiveValidationAttributes(fieldId, metaData));

            //create the outer div
            var outerDiv = new TagBuilder("div");
            outerDiv.AddCssClass("checkbox-container");

            //create the label in the outer div
            var label = new TagBuilder("label");
            label.MergeAttribute("for", fieldId);
            label.AddCssClass("checkbox");

            //render the control
            StringBuilder sb = new StringBuilder();
            sb.AppendLine(outerDiv.ToString(TagRenderMode.StartTag));
            sb.AppendLine(checkbox.ToString(TagRenderMode.SelfClosing));
            sb.AppendLine(label.ToString(TagRenderMode.StartTag));
            sb.AppendLine(labelText); //the label
            sb.AppendLine("<svg width=\"10\" height=\"10\" class=\"icon-check\"><use xlink:href=\"/icons.svg#check\"></use></svg>"); //optional icon
            sb.AppendLine(label.ToString(TagRenderMode.EndTag));
            sb.AppendLine(outerDiv.ToString(TagRenderMode.EndTag));

            //create the extra hidden input needed by MVC outside the div
            TagBuilder hiddenCheckbox = new TagBuilder("input");
            hiddenCheckbox.MergeAttribute("type", HtmlHelper.GetInputTypeString(InputType.Hidden));
            hiddenCheckbox.MergeAttribute("name", fullBindingName);
            hiddenCheckbox.MergeAttribute("value", "false");
            sb.Append(hiddenCheckbox.ToString(TagRenderMode.SelfClosing));

            //return the custom checkbox
            return MvcHtmlString.Create(sb.ToString());
        }

Ergebnis

<div class="checkbox-container">
    <input checked="checked" id="Model_myCheckBox" name="Model.myCheckBox" type="checkbox" value="true">
    <label class="checkbox" for="Model_myCheckBox">
        The checkbox label
        <svg width="10" height="10" class="icon-check"><use xlink:href="/icons.svg#check"></use></svg>
    </label>
</div>
<input name="Model.myCheckBox" type="hidden" value="false">
VDWWD
quelle
0

Dies ist kein Fehler! Es wird die Möglichkeit hinzugefügt, nach dem Posten des Formulars auf dem Server immer einen Wert zu haben. Wenn Sie mit jQuery Eingabefelder für Kontrollkästchen bearbeiten möchten, verwenden Sie die Methode prop (übergeben Sie die Eigenschaft 'checked' als Parameter). Beispiel:$('#id').prop('checked')

BlueKore
quelle
0

Sie können versuchen, den Konstruktor Ihres Modells folgendermaßen zu initialisieren:

public MemberFormModel() {
    foo = true;
}

und aus Ihrer Sicht:

@html.Checkbox(...)
@html.Hidden(...)
Saïd Mezhoud
quelle
0

Ich fand, dass dies wirklich Probleme verursachte, als ich ein WebGrid hatte. Die Sortierlinks im WebGrid werden durch den verdoppelten Querystring oder x = true & x = false in x = true, false umgewandelt und verursachen einen Analysefehler im Kontrollkästchen für.

Am Ende habe ich jQuery verwendet, um die versteckten Felder auf der Clientseite zu löschen:

    <script type="text/javascript">
    $(function () {
        // delete extra hidden fields created by checkboxes as the grid links mess this up by doubling the querystring parameters
        $("input[type='hidden'][name='x']").remove();
    });
    </script>
Matthew Lock
quelle