asp.net mvc: чому Html.CheckBox генерує додатковий прихований вхід


215

Я щойно помітив, що Html.CheckBox("foo")генерує 2 введення замість одного, хтось знає, чому це так?

<input id="foo" name="foo" type="checkbox" value="true" />
<input name="foo" type="hidden" value="false" /> 


1
Відповідь ericvg у можливому повторному запитанні також пояснює, що робить палітурка моделі, коли надсилаються як прапорці, так і приховані поля.
Теофіл

1
Я ненавиджу цю річ, вона згуртовувала мій jquery.
Джеррі Лян

2
Дивна реалізація mvc. Надсилання обох значень взагалі не має сенсу. Я перевірив Request.From ["myCheckBox"], і його значення було правдивим, хибним. WTF. Мені довелося записати керування вручну у поданому вигляді.
Нік Масао

Якщо це дійсно небажано, тоді не використовуйте Html.CheckBox (...), а просто введіть html для прапорця
wnbates

Відповіді:


198

Якщо прапорець не встановлений, поле форми не надсилається. Ось чому в прихованому полі завжди є хибне значення. Якщо ви не залишите прапорець прапорець, форма все одно матиме значення з прихованого поля. Ось як ASP.NET MVC обробляє значення прапорець.

Якщо ви хочете підтвердити це, поставте прапорець у формі не з Html.Hidden, а з <input type="checkbox" name="MyTestCheckboxValue"></input>. Залиште прапорець не встановленим, надсилайте форму та перегляньте розміщені запити на стороні сервера. Ви побачите, що значення прапорця немає. Якщо у вас було приховане поле, воно містило б MyTestCheckboxValueзапис із falseзначенням.


52
Якщо ви не встановили прапорець, то, очевидно, відповідь неправдива, немає необхідності повертати товар назад. Дурний дизайн на мій погляд.
The Muffin Man

54
@TheMuffinMan: Це не дурний варіант. Скажімо, ваша модель перегляду має властивість IsActive, яка викликається trueв конструктор. Користувач знімає прапорець, але оскільки значення не надсилається серверу, палітурка моделі не приймає його, а значення властивості не змінюється. В’яжуча модель не повинна вважати, що якщо значення не надсилається, воно було встановлено на помилкове значення, оскільки це може бути ваше рішення не надсилати це значення.
LukLed

21
Він починається з нешкідливого прапорця, а потім, перш ніж ви дізнаєтесь про це, ми переглядаємо стан, а потім перетворюється на ASP.NET MVCForms.
The Muffin Man

4
@LukLed Пізно відповідаючи на ваш коментар ... Я думаю, що логіка помилкова, тому що якщо у вашій моделі перегляду є тип властивості int, а у формі немає введення, то значення за замовчуванням дорівнює 0, оскільки для зміни його не з’явилось значення. Те ж саме стосується будь-якого іншого властивості, значення за замовчуванням для рядка є нульовим. Якщо ви не надсилаєте значення, воно є нульовим. У вашому прикладі встановлення властивості true в конструкторі, це, чесно кажучи, погано дизайнерське рішення, і тепер воно з’являється через те, як працюють прапорці в http land.
The Muffin Man

8
Ця логіка розтікання порушується для відключених прапорців - вони надсилають falseзначення, навіть якщо вони встановлені, і це заплутано. Відключені прапорці взагалі не повинні надсилати значення, якщо ASP.NET хоче бути сумісним з поведінкою HTTP за замовчуванням.
JustAMartin

31

Ви можете написати помічник, щоб не додати прихований вхід:

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

використай це:

@Html.CheckBoxSimple("foo", new {value = bar.Id})

4
Це спонукало мене на хвилину, на всякий випадок ... Якщо ваш помічник знаходиться в просторі імен, не забудьте додати @using Your.Name.Spaceвгорі файл .cshtml з переглядом бритви.
ooXei1sh

3
@ ooXei1sh Або це, або помістіть свого помічника в простір імен, System.Web.Mvc.Htmlщоб він був доступний для всіх видів
Лука

1
Якщо ви хочете використовувати це для зворотного зв'язку форми або для публікації через ajax зі значеннями форми, тоді вам потрібно буде обробити встановлення значення на істинне або хибне через подію onchange у полі. @ Html.CheckBoxSimple (x => Model.Foo, новий {value = Model.Foo, onchange = "toggleCheck (this)"}). Потім у функції javascript ToggleCompleted (el) {var verified = $ (el) .is (': перевірено'); $ ('# Foo'). Val (перевірено); }
John81


8

Ручний підхід такий:

bool IsDefault = (Request.Form["IsDefault"] != "false");

1
І чи гарантовано це, що ви отримаєте значення типу галочки, а не значення прихованого типу?
Брайан Свіні

1
це не має значення. Якщо прапорець не встановлений, він не публікується. Тож у скрипті буде "помилково". Якщо прапорець встановлено, значення "true, true" повертається (я думаю), і це не дорівнює "false".
Валамас

16
Ні, коли прапорець встановлено, істинний і хибний розміщуються, оскільки і прапорець, і приховані поля є дійсними елементами управління, які потрібно повернути назад. Mvc binder потім перевіряє, чи існує справжнє значення для даної намви, і якщо так, то воно віддає перевагу справжньому значенню. Ви можете перевірити це, переглянувши опубліковані дані. Він буде мати істинні, і хибні значення проти одного імені.
ZafarYousafi

Це отримало мене один раз, я перевіряв, чи значення == вірно
Террі Кернан

8

Це сильно типізована версія рішення Олександра Трофімова:

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

4

Використовуючи Contains, він буде працювати з двома можливими значеннями повідомлення: "false" або "true, false".

bool isChecked = Request.Form["foo"].Contains("true");

1

Прихований вклад викликав проблеми зі стильовими прапорцями. Тому я створив Html Helper Extension, щоб розмістити прихований вхід поза ділом, що містить 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());
        }

Результат

<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">

0

Це не помилка! Це додає можливість мати завжди значення після публікації форми на сервері. Якщо ви хочете опрацювати поля введення прапорець за допомогою jQuery, використовуйте метод опори (передайте властивість "прапорець" як параметр). Приклад:$('#id').prop('checked')


0

Ви можете спробувати ініціалізувати конструктор своєї моделі так:

public MemberFormModel() {
    foo = true;
}

і на ваш погляд:

@html.Checkbox(...)
@html.Hidden(...)

0

Я виявив, що це справді викликало проблеми, коли у мене була WebGrid. Посилання сортування на WebGrid перетворяться подвоєним запитом на рядок запитів або x = true & x = false у x = true, false і спричинять помилку розбору в прапорці для.

Я видав jQuery для видалення прихованих полів на стороні клієнта:

    <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>
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.