为什么CheckBoxFor呈现附加的输入标签,以及如何使用FormCollection获取值?


130

在我的ASP.NET MVC应用程序中,我使用以下代码呈现一个复选框:

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

现在,我看到这将同时呈现复选框输入标签和隐藏的输入标签。我遇到的问题是,当我尝试使用FormCollection从复选框中检索值时:

FormValues["ReceiveRSVPNotifications"]

我得到的值是“ true,false”。查看呈现的HTML时,可以看到以下内容:

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

因此,FormValues集合似乎将这两个值连接在一起,因为它们具有相同的名称。

有任何想法吗?

Answers:


168

在这里看看:

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

这不是一个错误,实际上是Ruby on Rails和MonoRail都使用的相同方法。

当您提交带有复选框的表单时,仅在选中复选框后才发布该值。因此,如果未选中该复选框,则在许多情况下您希望发送false时,将不会将任何内容发送到服务器。由于隐藏的输入与复选框具有相同的名称,因此,如果未选中该复选框,则仍然会向服务器发送“ false”。

选中此复选框后,ModelBinder将自动处理从“ true,false”中提取“ true”


10
这是一个很好的解释,但是在某些情况下,当未选中ckecbox时,您可能想在查询字符串中什么都没有-因此是默认行为。是否可以使用Html帮助程序,还是我必须简单地使用<input>标记。
Maksymilian Majer 2010年

13
我不喜欢...。我有一个使用GET的搜索页面,现在我的URL充满了对/错参数……
Shawn Mclean

1
哇,真是出乎意料。这是否意味着HTML复选框实际上是“损坏”的?
Isantipov 2014年

1
@Isantipov:不,这仅意味着这些Web框架不依赖缺少发布值来表示的复选框false。请看下面RyanJMcGowan的回答:“发送隐藏的输入可以使提交请求时知道该复选框出现在页面上。”
罗伯特·哈维

1
@Robert,这对我不起作用。如果未选中我的chbox MyCheckbox,那么我将得到false,而选中MyCheckbox时,我将得到一个[true,false]数组作为其值。我正在序列化整个表单,并将其通过ajax传递给控制器​​动作,例如。AddToOrder(MyModel model)我在哪里获得model.MyCheckbox = false而不是true
nickornotto

18

我遇到了与肖恩相同的问题(上)。这种方法对于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框架来破坏我的代码。因此,我必须确保我具有解释该新合同的测试范围。


5
你不觉得有点脏,你使用正则表达式匹配/删除隐藏的输入,而不是只建立只有一个复选框输入?:)
蒂姆·霍布斯

2
不,我这样做是为了修改现有行为,而不是重新实现自己的行为。这样,我的更改将与MS推出的任何更改保持同步。
克里斯·坎普

10
只是一个小的观察。您的实现不会呈现任何传递的自定义属性。参数“ htmlAttributes”不会传递给原始的“ CheckBoxFor”方法。
埃米尔·隆丁

16

我使用这种替代方法来呈现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);
}

类似于Chris Kemp的方法,该方法工作正常,但该方法不使用底层的CheckBoxForRegex.Replace。它基于原始Html.CheckBoxFor方法的来源。


这绝对是解决此问题的最佳方法,应该成为MVC框架的一部分……它可以完美地工作。我不确定为什么您的答案没有得到更多关注...谢谢!
user2315985 2015年

@ user2315985:我把它发布得太晚了;)这就是原因。
TomPažourek'15

您如何处理集合属性?默认的模型绑定器在绑定索引中出现间隙时将停止绑定值,因此似乎在使用扩展名,例如,如果检查了第一个和最后一个值,您将永远不知道应该接收的第二个值。还是我错过了什么?
Tieson T. 16/12/11

@TiesonT。除非您要编写自己的模型联编程序,否则唯一的解决方案是防止出现间隙。此方法不会为未选中的复选框发送false,因此您可能要使用执行此操作的原始Html.CheckBoxFor方法。
汤姆·帕索里克(TomPažourek),2013年

10

这是附加输入标签的源代码。微软很友好地提出了可以解决这个问题的评论。

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

9

我认为最简单的解决方案是按如下所示直接呈现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语法中,这甚至更加容易,因为当给定“ true”服务器端值时,“ checked”属性将直接使用“ checked”值呈现。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.