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

Web*_*ere 128 asp.net asp.net-mvc

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

<%= Html.CheckBoxFor(i=>i.ReceiveRSVPNotifications) %>
Run Code Online (Sandbox Code Playgroud)

现在,我看,这使得双方的复选框输入标签和一个隐藏的输入标签.我遇到的问题是当我尝试使用FormCollection从复选框中检索值时:

FormValues["ReceiveRSVPNotifications"]
Run Code Online (Sandbox Code Playgroud)

我得到的值是"真,假".查看呈现的HTML时,我可以看到以下内容:

 <input id="ReceiveRSVPNotifications" name="ReceiveRSVPNotifications" value="true" type="checkbox">
 <input name="ReceiveRSVPNotifications" value="false" type="hidden">
Run Code Online (Sandbox Code Playgroud)

所以FormValues集合似乎加入了这两个值,因为它们具有相同的名称.

有任何想法吗?

Rob*_*vey 169

看看这里:

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

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

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

选中该复选框后,ModelBinder将自动负责从'true,false'中提取'true'

  • 我不喜欢这个....我有一个使用GET的搜索页面,现在我的网址充满了真/假参数... (13认同)
  • 这是一个很好的解释,但在某些情况下,您可能希望在未选中ckecbox时在查询字符串中获得"无" - 所以默认行为.这可能是使用Html帮助器或我只需使用`<input>`标签. (9认同)

Chr*_*emp 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);
}
Run Code Online (Sandbox Code Playgroud)

我现在遇到的问题是我不想改变MVC框架来破坏我的代码.所以我必须确保我有测试报道解释这个新合同.

  • 只是一个小小的观察.您的实现不会呈现任何传递的自定义属性.参数"htmlAttributes"未传递给原始的"CheckBoxFor"方法. (10认同)
  • 使用正则表达式来匹配/删除隐藏的输入而不是仅仅构建_only_一个复选框输入,你不觉得有点脏吗?:) (5认同)
  • 不,我这样做是因为我正在修改现有的行为,而不是重新实现我自己的行为.这样,我的更改将与MS推出的任何更改保持同步. (2认同)

Tom*_*rek 15

我使用这种替代方法来呈现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);
}
Run Code Online (Sandbox Code Playgroud)

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


Rya*_*wan 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());
}
Run Code Online (Sandbox Code Playgroud)


小智 8

我认为最简单的解决方案是直接渲染INPUT元素,如下所示:

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

在Razor语法中,它更容易,因为当给定"真实"服务器端值时,'checked'属性直接使用"checked"值呈现.