Подтвердить что ты не робот

Почему CheckBoxFor создает дополнительный тег ввода и как я могу получить значение с помощью FormCollection?

В моем приложении ASP.NET MVC я создаю флажок, используя следующий код:

<%= Html.CheckBoxFor(i=>i.ReceiveRSVPNotifications) %>

Теперь я вижу, что это отображает как тег ввода флажка, так и скрытый тег ввода. Проблема, с которой я столкнулась, - это когда я пытаюсь получить значение из флажка с помощью формы:

FormValues["ReceiveRSVPNotifications"]

Я получаю значение "true, false". При просмотре отображаемого HTML я вижу следующее:

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

Таким образом, коллекция FormValues, похоже, объединяет эти два значения, так как они имеют одинаковое имя.

Любые идеи?

4b9b3361

Ответ 1

Посмотрите здесь:

http://forums.asp.net/t/1314753.aspx

Это не ошибка, и на самом деле тот же подход, что и Ruby on Rails и MonoRail.

Когда вы отправляете форму с помощью флажка, значение публикуется только в том случае, если флажок установлен. Итак, если вы не отметили флажок, ничто не будет отправлено на сервер, если во многих ситуациях вы вместо этого нужно отправить false. Поскольку скрытый ввод имеет одно и то же имя как флажок, тогда, если флажок снят, вы все равно получите 'false' отправлено на сервер.

Когда флажок установлен, ModelBinder автоматически примет забота об извлечении "истины" из "истинного, ложного"

Ответ 2

У меня была та же проблема, что и Шон (выше). Этот подход может быть отличным для POST, но он действительно отстой для GET. Поэтому я реализовал простое расширение Html, которое просто выдает скрытое поле.

public static MvcHtmlString BasicCheckBoxFor<T>(this HtmlHelper<T> html, 
                                                Expression<Func<T, bool>> expression,
                                                object htmlAttributes = null)
{
    var result = html.CheckBoxFor(expression).ToString();
    const string pattern = @"<input name=""[^""]+"" type=""hidden"" value=""false"" />";
    var single = Regex.Replace(result, pattern, "");
    return MvcHtmlString.Create(single);
}

Проблема, которую я сейчас имею, заключается в том, что я не хочу, чтобы изменения в структуре MVC нарушили мой код. Поэтому я должен убедиться, что у меня есть тестовое покрытие, объясняющее этот новый контракт.

Ответ 3

Я использую этот альтернативный метод для отображения флажков для форм GET:

/// <summary>
/// Renders checkbox as one input (normal Html.CheckBoxFor renders two inputs: checkbox and hidden)
/// </summary>
public static MvcHtmlString BasicCheckBoxFor<T>(this HtmlHelper<T> html, Expression<Func<T, bool>> expression, object htmlAttributes = null)
{
    var tag = new TagBuilder("input");

    tag.Attributes["type"] = "checkbox";
    tag.Attributes["id"] = html.IdFor(expression).ToString();
    tag.Attributes["name"] = html.NameFor(expression).ToString();
    tag.Attributes["value"] = "true";

    // set the "checked" attribute if true
    ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData);
    if (metadata.Model != null)
    {
        bool modelChecked;
        if (Boolean.TryParse(metadata.Model.ToString(), out modelChecked))
        {
            if (modelChecked)
            {
                tag.Attributes["checked"] = "checked";
            }
        }
    }

    // merge custom attributes
    tag.MergeAttributes(HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));

    var tagString = tag.ToString(TagRenderMode.SelfClosing);
    return MvcHtmlString.Create(tagString);
}

Он похож на метод Криса Кемпа, который отлично работает, кроме этого не использует базовые CheckBoxFor и Regex.Replace. Он основан на источнике исходного метода Html.CheckBoxFor.

Ответ 4

Вот исходный код для дополнительного тега ввода. Microsoft была достаточно любезна, чтобы включать комментарии, которые точно соответствуют этому.

if (inputType == InputType.CheckBox)
{
    // Render an additional <input type="hidden".../> for checkboxes. This
    // addresses scenarios where unchecked checkboxes are not sent in the request.
    // Sending a hidden input makes it possible to know that the checkbox was present
    // on the page when the request was submitted.
    StringBuilder inputItemBuilder = new StringBuilder();
    inputItemBuilder.Append(tagBuilder.ToString(TagRenderMode.SelfClosing));

    TagBuilder hiddenInput = new TagBuilder("input");
    hiddenInput.MergeAttribute("type", HtmlHelper.GetInputTypeString(InputType.Hidden));
    hiddenInput.MergeAttribute("name", fullName);
    hiddenInput.MergeAttribute("value", "false");
    inputItemBuilder.Append(hiddenInput.ToString(TagRenderMode.SelfClosing));
    return MvcHtmlString.Create(inputItemBuilder.ToString());
}

Ответ 5

Я думаю, что самым простым решением является визуализация элемента INPUT следующим образом:

<input type="checkbox" 
       id="<%=Html.IdFor(i => i.ReceiveRSVPNotifications)%>"
       name="<%=Html.NameFor(i => i.ReceiveRSVPNotifications)%>"
       value="true"
       checked="<%=Model.ReceiveRSVPNotifications ? "checked" : String.Empty %>" />

В синтаксисе Razor это еще проще, потому что атрибут "checked" непосредственно отображается с "проверенным" значением при задании значения "true" на стороне сервера.