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" /> 
Ома
источник
3
возможный дубликат метода Razor ViewEngine HTML.Checkbox создает скрытый ввод. Зачем?
Мэтт
1
Ответ ericvg на возможный повторяющийся вопрос также объясняет, что делает связыватель модели, когда отправляются оба флажка и скрытые поля.
Феофил
1
Ненавижу эту штуку, которая накапливалась с моей jquery.
Джерри Лян
2
Странная реализация от mvc. Отправка обоих значений не имеет смысла вообще. Я проверил Request.From ["myCheckBox"] и его значение было true, false. WTF. Мне пришлось написать элемент управления вручную в представлении.
Ник Масао
Если это действительно нежелательно, не используйте Html.CheckBox (...), а просто
введите

Ответы:

198

Если флажок не установлен, поле формы не отправляется. Вот почему всегда есть ложное значение в скрытом поле. Если вы оставите флажок не установленным, форма все равно будет иметь значение из скрытого поля. Именно так ASP.NET MVC обрабатывает значения флажков.

Если вы хотите это подтвердить, поставьте флажок в форме не с Html.Hidden, а с <input type="checkbox" name="MyTestCheckboxValue"></input>. Оставьте флажок снятым, отправьте форму и посмотрите значения отправленных запросов на стороне сервера. Вы увидите, что значение флажка отсутствует. Если у вас есть скрытое поле, оно будет содержать MyTestCheckboxValueзапись со falseзначением.

LukLed
источник
52
Если вы не установили флажок, то, очевидно, ответ ложный, нет необходимости отправлять товар обратно. Глупый дизайн на мой взгляд.
Человек
54
@TheMuffinMan: Это не глупый вариант. Допустим, у вашей модели представления есть свойство с именем IsActive, которое инициируется trueв конструкторе. Пользователь снимает флажок, но поскольку значение не отправляется на сервер, связыватель модели не получает его, и значение свойства не изменяется. Связыватель модели не должен предполагать, что если значение не отправлено, для него установлено значение false, поскольку это может быть вашим решением не отправлять это значение.
LukLed
21
Он начинается с безобидного флажка, а затем, прежде чем вы узнаете его, у нас есть состояние просмотра, он превращается в ASP.NET MVCForms.
Маффин Ман
4
@LukLed Отвечая на ваш комментарий поздно ... Я думаю, что логика ошибочна, потому что, если ваша модель представления имеет тип свойства int и нет ввода в форме, то значение по умолчанию равно 0, потому что не было получено никакого значения для его изменения. То же самое касается любого другого свойства, значение по умолчанию для строки равно нулю. Если вы не отправите значение, оно будет нулевым. В вашем примере установки свойства в конструкторе значение true, это, честно говоря, плохое проектное решение, и теперь оно выясняется из-за того, как флажки работают в http land.
Человек
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 со значениями формы, вам нужно будет установить значение true или false через событие onchange в флажке. @ Html.CheckBoxSimple (x => Model.Foo, new {value = Model.Foo, onchange = "toggleCheck (this)"}). Затем в JavaScript функция ToggleCompleted (el) {var checked = $ (el) .is (': checked'); $ ( '# Foo') Вал (проверено). }
Джон81,
13

когда флажок установлен и отправлен, выполните это

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

обращаться

Десмонд
источник
8

Ручной подход заключается в следующем:

bool IsDefault = (Request.Form["IsDefault"] != "false");
Valamas
источник
1
И гарантированно ли это даст вам значение типа флажка, а не значение скрытого типа?
Брайан Суини
1
это не имеет значения. Когда флажок не установлен, он не публикуется. Таким образом, скрытая коробка будет иметь значение «ложь». Если флажок установлен, возвращается значение «истина, истина» (я думаю), и это не равно «ложь».
Валамас
16
Нет, когда флажок установлен, и истина, и ложь публикуются, потому что оба флажка и скрытые поля являются допустимыми элементами управления для отправки обратно. Mvc binder затем проверяет, существует ли истинное значение для данного namve, и если да, то оно предпочитает истинное значение. Вы можете проверить это, просмотрев опубликованные данные. Он будет иметь значения true и false для одного имени.
ZafarYousafi
Это дало мне один раз, я проверял, если значение == true
Терри Кернан
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 для размещения скрытого ввода вне div, содержащего 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">
VDWWD
источник
0

Это не ошибка! Это добавляет возможность иметь всегда значение после отправки формы на сервер. Если вы хотите иметь дело с полями ввода флажков с помощью jQuery, используйте метод prop (передайте свойство 'checked' в качестве параметра). Пример:$('#id').prop('checked')

BlueKore
источник
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>
Мэтью Лок
источник