为什么CheckBoxFor会渲染一个额外的输入标记,如何使用FormCollection获取值?

时间:2010-05-18 20:34:53

标签: asp.net asp.net-mvc

在我的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集合似乎加入了这两个值,因为它们具有相同的名称。

任何想法?

5 个答案:

答案 0 :(得分:168)

看看这里:

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

  

这不是一个bug,实际上与Ruby相同的方法   使用Rails和MonoRail。

     

当您提交带有复选框的表单时,仅在以下情况下发布该值   选中该复选框。所以,如果你不选中复选框,那么   在很多情况下你都不会将任何内容发送到服务器   希望错误发送。由于隐藏输入具有相同的名称   作为复选框,如果未选中该复选框,您仍然会得到一个   'false'发送到服务器。

     

选中该复选框后,ModelBinder将自动获取   关心从'true,false'中提取'true'

答案 1 :(得分:17)

我和肖恩(上图)有同样的问题。这种方法对于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框架来破坏我的代码。所以我必须确保我有解释这份新合同的测试报道。

答案 2 :(得分:14)

我使用这种替代方法来呈现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 的方法,它正常工作,除了这个不使用基础{{1} }和CheckBoxFor。它基于原始Regex.Replace方法的来源。

答案 3 :(得分:10)

Here is the source code用于附加输入标记。微软非常友好地包含了准确解决这个问题的评论。

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

答案 4 :(得分:7)

我认为最简单的解决方案是直接渲染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语法中,它更容易,因为&#39;检查&#39;属性直接使用&#34;检查&#34;给予“真实”的价值。服务器端值。