将 Html.RadioButtonFor 和 Html.LabelFor 用于相同的模型但不同的值

Posted

技术标签:

【中文标题】将 Html.RadioButtonFor 和 Html.LabelFor 用于相同的模型但不同的值【英文标题】:Use Html.RadioButtonFor and Html.LabelFor for the same Model but different values 【发布时间】:2012-01-09 08:21:25 【问题描述】:

我有这个 Razor 模板

<table>
<tr>
    <td>@html.RadioButtonFor(i => i.Value, "1")</td>
    <td>@Html.LabelFor(i => i.Value, "true")</td>
</tr>
<tr>
    <td>@Html.RadioButtonFor(i => i.Value, "0")</td>
    <td>@Html.LabelFor(i => i.Value, "false")</td>
</tr>
</table>

这给了我这个 HTML

<table>
<tr>
    <td><input id="Items_1__Value" name="Items[1].Value" type="radio" value="1" /></td>
    <td><label for="Items_1__Value">true</label></td>
</tr>
<tr>
    <td><input checked="checked" id="Items_1__Value" name="Items[1].Value" type="radio" value="0" /></td>
    <td><label for="Items_1__Value">false</label></td>
</tr>
</table>

所以我有两次 ID Items_1__Value,这当然是不好的,并且在我点击第二个标签“false”时无法在浏览器中工作,第一个收音机将被激活。

我知道我可以在 RadioButtonFor 添加一个自己的 Id 并用我的标签引用它,但这不是很好,是吗?特别是因为我处于一个循环中并且不能只使用名​​称“值”和一个附加数字,这最终也会在我的最终 HTML 标记中出现多个 Dom Id。

不应该是一个好的解决方案吗?

【问题讨论】:

我认为你最好的选择是创建你自己的自定义 HtmlHelper,它可以输出一个单选按钮和一个标签,考虑到你传入的默认值。 我选择了简单的选项并使用 Html.Raw 来编写标签 html,它只需要做这么多。使用 ViewData.TemplateInfo.GetFullHtmlFieldId 生成单选按钮的 id,以便在标签中使用。 【参考方案1】:

不要为此过度设计解决方案。您想要完成的只是让单选按钮响应对文本的点击。保持简单,只需将单选按钮包装在标签标签中:

<table>
<tr>
    <td><label>@Html.RadioButtonFor(i => i.Value, "1")True</label></td>
</tr>
<tr>
    <td><label>@Html.RadioButtonFor(i => i.Value, "0")False</label></td>
</tr>
</table>

LabelFor html 帮助器通常用于从视图模型的 Display 属性中引入 Name(例如“[Display(Name = "Enter your Name")])。

对于单选按钮,名称并不是特别有用,因为每个单选按钮都有不同的文本行,这意味着无论如何您都无法将文本硬编码到视图中。

【讨论】:

+1 你一针见血:“不要为此过度设计解决方案。” 让我开心:“不要过度设计”! 一般来说,不要过度设计是很好的建议。如果可以的话,我会给你不止一票。 对于那些想知道这是否是 HTML 中的好习惯并且没有任何其他副作用的人,请参阅***.com/questions/774054/… 这是个好主意,但我认为在这种情况下您不能为 :selected 之类的选择器设置标签样式?【参考方案2】:

我一直想知道 MVC 如何确定“嵌套”字段名称和 ID。对 MVC 源代码进行了一些研究才弄清楚,但我想我有一个很好的解决方案。

EditorTemplates 和 DisplayTemplates 如何确定字段名称和 ID

随着 EditorTemplates 和 DisplayTemplates 的引入,MVC 框架添加了ViewData.TemplateInfo,其中包含当前的“字段前缀”,例如"Items[1]."。嵌套模板使用它来创建唯一的名称和 ID。

创建我们自己的唯一 ID:

TemplateInfo 类包含一个有趣的方法,GetFullHtmlFieldId。我们可以使用它来创建我们自己的唯一 ID,如下所示:

@string id = ViewData.TemplateInfo.GetFullHtmlFieldId("fieldName");
@* This will result in something like "Items_1__fieldName" *@

为了胜利

以下是为您的示例实现正确行为的方法:

<table>
<tr>
    @string id = ViewData.TemplateInfo.GetFullHtmlFieldId("radioTrue");
    <td>@Html.RadioButtonFor(i => i.Value, "1", newid)</td>
    <td>@Html.LabelFor(i => i.Value, "true", new@for=id)</td>
</tr>
<tr>
    @id = ViewData.TemplateInfo.GetFullHtmlFieldId("radioFalse");
    <td>@Html.RadioButtonFor(i => i.Value, "0", newid)</td>
    <td>@Html.LabelFor(i => i.Value, "false", new@for=id)</td>
</tr>
</table>

这将为您提供以下 HTML:

<table>
<tr>
    <td><input id="Items_1__radioTrue" name="Items[1].Value" type="radio" value="1" /></td>
    <td><label for="Items_1__radioTrue">true</label></td>
</tr>
<tr>
    <td><input checked="checked" id="Items_1__radioFalse" name="Items[1].Value" type="radio" value="0" /></td>
    <td><label for="Items_1__radioFalse">false</label></td>
</tr>
</table>

免责声明

我的 Razor 语法不完善,所以如果这段代码有语法错误,请告诉我。

物有所值

很遗憾,RadioButtonFor 没有内置此功能。所有呈现的单选按钮都应该有一个 ID 是其namevalue 的组合,这似乎是合乎逻辑的,但事实并非如此——可能是因为这与所有其他 Html 帮助程序不同。 为此功能创建自己的扩展方法似乎也是一个合乎逻辑的选择。但是,使用“表达式语法”可能会变得棘手......所以我建议重载.RadioButton(name, value, ...) 而不是RadioButtonFor(expression, ...)。您可能还希望 .Label(name, value) 重载。 我希望这一切都说得通,因为那段中有很多“填空”。

【讨论】:

编译器可能会抱怨上面代码new for = id 中声明了匿名类型。这是因为for 是保留关键字。在这种情况下,只需大写属性名称,如new For = id 。谢谢斯科特。 我没有注意到。我通常使用new @for = id, @class = "cssClass" 之类的语法,但我从未想过使用大写属性。你确认输出是小写的吗? 是的,生成的属性的名称是小写的,但是我已经切换到@for 语法,因为它似乎是惯例。干杯 您可以改用字典重载:new Dictionary&lt;string, object&gt;() "for", id, "class", "cssClass" @ScottRippey 怎么可能只使用new id ?引擎如何知道将该值映射到标签的id 属性?【参考方案3】:

@Scott Rippey 几乎拥有它,但我想他必须使用不同版本的 MVC3,因为对我来说 @Html.LabelFor 没有需要 3 个参数的重载。我发现使用普通的@Html.Label 效果很好:

@string id = ViewData.TemplateInfo.GetFullHtmlFieldId("radioButton_True");
@Html.Label(id, "True:")
@Html.RadioButtonFor(m => m.radioButton, true, new  id )

@id = ViewData.TemplateInfo.GetFullHtmlFieldId("radioButton_False");
@Html.Label(id, "False:")
@Html.RadioButtonFor(m => m.radioButton, false, new  id )

这允许您单击标签并按预期选择相关的单选按钮。

【讨论】:

老实说,我没有测试我的代码。但是,我确实看过the documentation for Html.LabelFor(expression, labelText, htmlAttributes),看起来应该可以正常工作。 @ScottRippey 那是针对 MVC4 的,因为问题是关于 MVC3,所以这应该是公认的答案。 赞成,这让我走上了正确的道路,可以解决我在点击标签时没有激活 IE6 中的单选按钮的问题。上述答案中未包含的是 id 和 for 属性。【参考方案4】:

虽然不完美,但可以工作,

<table>
<tr>
    <td>@ReplaceName(Html.RadioButtonFor(i => i.Value, "1"))</td>
    <td>@Html.LabelFor(i => i.Value[0], "true")</td>
</tr>
<tr>
    <td>@ReplaceName(Html.RadioButtonFor(i => i.Value, "0"))</td>
    <td>@Html.LabelFor(i => i.Value[1], "false")</td>
</tr>
</table>


@functions 
    int counter = 0;
    MvcHtmlString ReplaceName(MvcHtmlString html)
        return MvcHtmlString.Create(html.ToString().Replace("__Value", "__Value_" + counter++ +"_"));
    

【讨论】:

【参考方案5】:

这是一个您可以使用的 HtmlHelper,尽管您可以自定义它。只是勉强测试,所以 YMMV。

     public static MvcHtmlString RadioButtonWithLabelFor<TModel, TProperty>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TProperty>> expression, object value, object htmlAttributes = null)
     
        var name = ExpressionHelper.GetExpressionText(expression);
        var id = helper.ViewData.TemplateInfo.GetFullHtmlFieldId(name + "_" + value);

        var viewData = new ViewDataDictionary(helper.ViewData) "id", id;

        if (htmlAttributes != null)
        
            var viewDataDictionary = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes);
            foreach (var keyValuePair in viewDataDictionary)
            
                viewData[keyValuePair.Key] = keyValuePair.Value;
            
        

        var radioButton = helper.RadioButtonFor(expression, value, viewData);

        var tagBuilder = new TagBuilder("label");
        tagBuilder.MergeAttribute("for", id);
        tagBuilder.InnerHtml = value.ToString();

        return new MvcHtmlString(radioButton.ToHtmlString() + tagBuilder.ToString());
    

【讨论】:

我把它分成 2 个助手,一个用于标签,一个用于单选按钮。此外,我正在更改标签以解决内部 HTML 需要与值不同的情况,例如,如果值是 2 个单词。【参考方案6】:

您可以添加带有标签的文本

<td>@Html.RadioButtonFor(i => i.Value, true) <text>True</text></td>
<td>@Html.RadioButtonFor(i => i.Value, false) <text>False</text></td>

【讨论】:

【参考方案7】:

显然有一个很好的解决方案

    <td>@Html.RadioButtonFor(i => i.Value, true)</td>
    <td>@Html.RadioButtonFor(i => i.Value, false)</td>

【讨论】:

对不起,但这不是一个好的解决方案——使用该代码,我将只得到单选按钮,但没有标签。我想要有标签,用户可以单击以激活单选按钮。只是收音机对用户不友好。 是的,我有点误读了你的问题。查看答案here,它使用比 bool 更复杂的值,这将允许有工作标签 我不明白 - 这对我的情况有何帮助?在另一个答案中使用了不同的属性,我只有一个具有两个不同值的属性。您能否按照您认为适合我的方式更新您的答案? 不不,你是对的 - 我在考虑我自己的代码,我有一个特殊的编辑器,用于为枚举创建 id,发布时我没有意识到它不是自动的。见here

以上是关于将 Html.RadioButtonFor 和 Html.LabelFor 用于相同的模型但不同的值的主要内容,如果未能解决你的问题,请参考以下文章

Html.RadioButtonFor 单选按钮不在 Safari 或 Chrome 中呈现

Razor- 为 radioButtonFor 赋予“属性”

MVC RadioButton控件

foreach 中的 mvc 单选按钮

工作总结 @Html 辅助方法 为 生成的 标签设置元素属性 htmlAttributes 一个对象,其中包含要为该元素设置的 HTML 特性。

单选按钮值第二次没有改变