为什么 CheckBoxFor 呈现一个额外的输入标记,以及如何使用 FormCollection 获取该值?

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

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

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

FormValues["ReceiveRSVPNotifications"]

我得到了值“ true,false”:

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

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

有什么想法吗?

46445 次浏览

看看这里:

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

这不是一个 bug,实际上这是与 Ruby on 相同的方法 使用铁路及单轨铁路。

当提交带有复选框的表单时,只有在下列情况下才会提交该值 选中复选框。因此,如果您没有选中该复选框,那么 在许多情况下,没有任何东西会被发送到服务器 因为隐藏的输入具有相同的名称 作为复选框,那么如果未选中该复选框,您仍然会得到一个 发送到服务器的“ false”。

当选中该复选框时,ModelBinder 将自动采用 小心地从“真,假”中提取“真”

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

下面是附加输入标记的源代码 。微软非常友好地在评论中明确指出了这一点。

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

我认为最简单的解决方案是直接呈现 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”服务器端值时,“ check”属性直接呈现为一个“ check”值。

我使用这种替代方法来呈现 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方法的源代码。