asp.net mvc: dlaczego Html.CheckBox generuje dodatkowe ukryte dane wejściowe

215

Właśnie zauważyłem, że Html.CheckBox("foo")generuje 2 wejścia zamiast jednego, ktoś wie dlaczego tak jest?

<input id="foo" name="foo" type="checkbox" value="true" />
<input name="foo" type="hidden" value="false" /> 
Omu
źródło
1
Odpowiedź ericvg w możliwym zduplikowanym pytaniu wyjaśnia również, co robi segregator modelu, gdy przesyłane są zarówno pole wyboru, jak i ukryte pola.
Theophilus
1
Nienawidzę tego, co gromadziło się w mojej jquery.
Jerry Liang
2
Dziwna implementacja mvc. Wysłanie obu wartości w ogóle nie ma sensu. Sprawdziłem Request.From [„myCheckBox”] i jego wartość to true, false. WTF. Musiałem napisać kontrolkę ręcznie w widoku.
Nick Masao
Jeśli jest to naprawdę niepożądane, nie używaj Html.CheckBox (...) i po prostu wpisz html dla pola wyboru
wnbates 16.09.16

Odpowiedzi:

197

Jeśli pole wyboru nie jest zaznaczone, pole formularza nie jest przesyłane. Dlatego w ukrytym polu zawsze występuje fałszywa wartość. Jeśli pozostawisz pole wyboru niezaznaczone, formularz nadal będzie miał wartość z ukrytego pola. W ten sposób ASP.NET MVC obsługuje wartości pól wyboru.

Jeśli chcesz to potwierdzić, zaznacz pole wyboru w formularzu nie za pomocą Html.Hidden, ale za pomocą <input type="checkbox" name="MyTestCheckboxValue"></input>. Pozostaw niezaznaczone pole wyboru, prześlij formularz i sprawdź wartości żądań po stronie serwera. Zobaczysz, że nie ma wartości pola wyboru. Gdybyś miał ukryte pole, zawierałoby ono MyTestCheckboxValuewpis z falsewartością.

LukLed
źródło
52
Jeśli nie zaznaczyłeś tego pola, odpowiedź jest oczywiście fałszywa, nie trzeba go odsyłać. Moim zdaniem głupiutki design.
The Muffin Man,
54
@TheMuffinMan: To nie jest głupia opcja. Powiedzmy, że twój model widoku ma właściwość o nazwie IsActive, która jest inicjowana truew konstruktorze. Użytkownik usuwa zaznaczenie pola wyboru, ale ponieważ wartość nie jest wysyłana do serwera, model spoiwa nie odbiera jej, a wartość właściwości nie jest zmieniana. Model segregatorowy nie powinien zakładać, że jeśli wartość nie zostanie wysłana, zostanie ustawiona wartość false, ponieważ może to być Twoja decyzja o nieprzesyłaniu tej wartości.
LukLed
21
Zaczyna się od nieszkodliwego pola wyboru, a następnie, zanim się zorientujesz, mamy stan widoku, przekształca się w ASP.NET MVCForms.
The Muffin Man
4
@LukLed Odpowiadanie na Twój komentarz późno ... Myślę, że logika jest wadliwa, ponieważ jeśli twój model widoku ma typ właściwości int i nie ma danych wejściowych w formularzu, wówczas wartość domyślna to 0, ponieważ nie pojawiła się żadna wartość, aby ją zmienić. To samo dotyczy każdej innej właściwości, domyślna wartość łańcucha to null. Jeśli nie wyślesz wartości, jest ona pusta. W twoim przykładzie ustawiania właściwości w konstruktorze na wartość true, to jest naprawdę zła decyzja projektowa i teraz wychodzi ona na jaw ze względu na to, jak pola wyboru działają w http land.
The Muffin Man
8
Ta logika zacienienia psuje wyłączone pola wyboru - wysyłają falsewartość, nawet jeśli są zaznaczone, i to jest mylące. Wyłączone pola wyboru nie powinny w ogóle wysyłać żadnej wartości, jeśli ASP.NET chce być zgodny z domyślnym zachowaniem HTTP.
JustAMartin,
31

Możesz napisać pomocnika, aby zapobiec dodawaniu ukrytych danych wejściowych:

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

Użyj tego:

@Html.CheckBoxSimple("foo", new {value = bar.Id})
Alexander Trofimov
źródło
4
To mnie potknęło na minutę, więc na wszelki wypadek ... Jeśli twój pomocnik znajduje się w przestrzeni nazw, nie zapomnij dodać @using Your.Name.Spacena górze pliku .cshtml razor.
ooXei1sh
3
@ ooXei1sh Albo to, albo umieść swojego pomocnika w przestrzeni nazw, System.Web.Mvc.Htmlaby był dostępny we wszystkich widokach
Luke
1
Jeśli chcesz użyć tego do zwrotnego formularza lub do wysyłania za pośrednictwem ajax z wartościami formularza, musisz obsłużyć ustawienie wartości na true lub false za pomocą zdarzenia onchange na polu wyboru. @ Html.CheckBoxSimple (x => Model.Foo, new {value = Model.Foo, onchange = "toggleCheck (this)"}). Następnie w funkcji javascript ToggleCompleted (el) {var Sprawdzone = $ (el) .is (': sprawdzone'); $ („# Foo”). Val (zaznaczone); }
John81,
12

po zaznaczeniu i przesłaniu pola wyboru wykonaj to

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

Odnosić się

Desmond
źródło
8

Podejście ręczne jest następujące:

bool IsDefault = (Request.Form["IsDefault"] != "false");
Valamas
źródło
1
Czy to gwarantuje uzyskanie wartości typu pola wyboru, a nie wartości typu ukrytego?
Brian Sweeney,
1
to nie ma znaczenia. Gdy pole wyboru nie jest zaznaczone, nie jest publikowane. Tak więc ukryty box będzie miał wartość „false”. Jeśli pole jest zaznaczone, zwracana jest wartość „prawda, prawda” (myślę), a to nie równa się „fałsz”.
Valamas
16
Nie, gdy pole wyboru jest zaznaczone, zarówno prawda, jak i fałsz są wysyłane, ponieważ zarówno pole wyboru, jak i ukryte pola są prawidłowymi elementami sterującymi, które należy odesłać. Spoiwo Mvc sprawdza następnie, czy dla danej nazwy istnieje wartość true, a jeśli tak, to preferuje wartość true. Możesz to sprawdzić, przeglądając opublikowane dane. Będzie miał zarówno wartości prawda, jak i fałsz względem pojedynczej nazwy.
ZafarYousafi
To mnie raz przywiodło, sprawdzałem, czy wartość == true
Terry Kernan
8

To mocno typowana wersja rozwiązania Aleksandra Trofimowa:

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
źródło
4

Użyj Zawiera, będzie działać z dwiema możliwymi wartościami post: „false” lub „true, false”.

bool isChecked = Request.Form["foo"].Contains("true");
Dave D.
źródło
1

Ukryte dane wejściowe powodowały problemy ze stylowymi polami wyboru. Więc stworzyłem rozszerzenie pomocnika HTML, aby umieścić ukryte dane wejściowe poza div zawierającym CheckBox.

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

Wynik

<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
źródło
0

To nie jest błąd! Dodaje możliwość posiadania zawsze wartości, po wysłaniu formularza na serwer. Jeśli chcesz poradzić sobie z polami wejściowymi pola wyboru za pomocą jQuery, użyj metody prop (przekaż właściwość „zaznaczone” jako parametr). Przykład:$('#id').prop('checked')

BlueKore
źródło
0

Możesz spróbować zainicjować konstruktor swojego modelu w następujący sposób:

public MemberFormModel() {
    foo = true;
}

i twoim zdaniem:

@html.Checkbox(...)
@html.Hidden(...)
Saïd Mezhoud
źródło
0

Odkryłem, że to naprawdę spowodowało problemy, gdy miałem WebGrid. Łącza sortujące w sieci Web zamieniłyby się przez podwojone kwerendy lub x = true i x = false na x = true, false i spowodowałyby błąd analizy w polu wyboru dla.

Skończyło się na użyciu jQuery do usunięcia ukrytych pól po stronie klienta:

    <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
źródło