使用 RadioButtonFor 编码一组单选按钮的正确方法

Posted

技术标签:

【中文标题】使用 RadioButtonFor 编码一组单选按钮的正确方法【英文标题】:Correct Way to Code a Group of Radio Buttons using RadioButtonFor 【发布时间】:2015-02-27 22:45:47 【问题描述】:

根据我的研究,在 MVC 中编写复选框的正确方法如下。

@html.CheckBoxFor(m => m.RememberMe)
@Html.LabelFor(m => m.RememberMe)

这将渲染一个复选框,为该复选框渲染一个标签,并使标签可点击,这意味着点击标签也会切换复选框。

但是对于单个视图模型字段有多个复选框的情况呢? (例如,每个复选框可能代表一个整数值中的一个位。)或者,更好的是,单选按钮呢,总是有多个选项与同一个字段相关联?

在这些情况下,上面的代码似乎存在问题。要么不是所有元素都与同一个字段相关联,要么会呈现具有相同 ID 的多个元素。

我已经研究了一段时间了。我找到了很多解决方案;但是,它们似乎都没有可点击的标签,或者它们需要一堆自定义和/或非标准编码。有些人完全避免使用 RadioButtonFor/LabelFor,而是选择对原始 HTML 进行编码。

编写原始 HTML 代码很好,但 MVC 的设计者没有预料到我们可能需要多个单选按钮用于同一字段吗?而且,如果他们这样做了,他们打算如何对其进行编码?

编辑:

在对此进行了更多研究之后,下面的代码是我发现的对单选按钮进行编码的最佳方式。此代码假定我已为每个单选选项定义了一个枚举。

@Html.RadioButtonFor(m => m.Gender, Gender.Male, new  id = "gender-male" )
@Html.LabelFor(m => m.Gender, Gender.Male.ToString(), new  @for = "gender-male" )
@Html.RadioButtonFor(m => m.Gender, Gender.Female, new  id = "gender-female" )
@Html.LabelFor(m => m.Gender, Gender.Female.ToString(), new  @for = "gender-female" )

也许这是我能预料到的最好的,但它仍然让我有点困扰。

    为什么需要对 ID 等基本内容进行如此多的自定义?再说一次,微软没有预料到我们会想要多个单选按钮与同一个字段相关联吗?

    如果枚举名称与我想要的单选按钮标签的描述不同,这种方法似乎不支持字段属性,例如[Display(Name = "")]

(我猜对多个复选框的正确处理将推迟到另一个问题。)

【问题讨论】:

在我看来,RadioButtonFor 应该有像 DropDownListFor 这样的重载,我可以在其中传递项目列表,并且每个项目的单选按钮都将使用可点击的标签生成。 @th1rdey3:我不确定 DropDownListFor。也许RadioButtonListFor?还有such a component has been written,如果您想使用第三方选项。但我不明白为什么微软认为不需要它。 现在你一个人在谈论......关于你找到的解决方案的问题,我认为这是......同时。 只是指向 AmirHosseinMehrvarzi 删除了他所有的 cmets。就是这样。 @JonathanWood 顺便说一句,你在 SO 1 月 1 日 2:56 做了什么?你为什么不庆祝新年? :) 【参考方案1】:

好的,让我们做一些研究。首先,您不需要唯一的 id 来通过单击标签来切换复选框/收音机。实际上你根本不需要 id !您所要做的就是将您的输入包装在<label> 标签中:

<label>
    <input type="radio" name="radio" value="1" /> Radio 1   
</label>

到目前为止一切顺利,ASP.NET MVC 为您提供了编写自己的 html 帮助程序的能力,让我们为Enum 模型字段编写一个!

想象一下,我们有一些注册模型:

public class RegisterViewModel

    [Required]
    [EmailAddress]
    [Display(Name = "Email")]
    public string Email  get; set; 

    [Required]
    [StringLength(100, ErrorMessage = "The 0 must be at least 2 characters long.", MinimumLength = 6)]
    [DataType(DataType.Password)]
    [Display(Name = "Password")]
    public string Password  get; set; 

    [DataType(DataType.Password)]
    [Display(Name = "Confirm password")]
    [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
    public string ConfirmPassword  get; set;  

该死!我刚刚复制粘贴了教程模型o_O。我们现在想要的 - 是获取用户的性别,所以我们添加一个 Gender 枚举:

public enum Gender

    Iamparanoic = 0,

    Male = 1,

    Female = 2,

并将Gender 类型的Gender 字段添加到我们的模型中(希望c# 有一个Gender 访问修饰符!)

public Gender Gender  get; set; 

现在,是时候使用一些 asp.net 魔法并编写我们的 html 助手了:

public static MvcHtmlString RadioButtonsListForEnum<TModel, TEnum>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TEnum>> expression)

    var sb = new StringBuilder();
    sb.Append("<ul>"); //we want it to look cool, dont' we?
    foreach (var value in Enum.GetValues(typeof(TEnum)))
    
        var radio = htmlHelper.RadioButtonFor(expression, value).ToHtmlString(); //you can generage any id and pass it to helper, or use a tagbuilder

        sb.AppendFormat(
            "<li><label>0 1</label></li>",
            radio,
            ((Enum)value).GetEnumName() //instead of it you can grab e.g. Display/Description attribute value or w/e else
        );
    
    sb.Append("</ul");
    return MvcHtmlString.Create(sb.ToString());

它的用法如下:

@Html.RadioButtonsListForEnum(m => m.Gender)

很漂亮,不是吗?当然你可以添加很多自定义,比如渲染具有唯一 mvc 样式 id 的单选按钮,各种 html 属性等,但这只是一个示例,正确的方式。

接下来,我们还要渲染 checkboxlist!

public static MvcHtmlString CheckBoxListForEnum<TModel, TEnum>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TEnum>> expression, IDictionary<string, object> htmlAttributes = null)

    var metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
    var enumValue = Convert.ToInt32(metadata.Model);

    var sb = new StringBuilder();
    foreach (var value in Enum.GetValues(typeof (TEnum)))
    
        var val = Convert.ToInt32(value);
        var checkbox = new TagBuilder("input");

        checkbox.MergeAttribute("type", "checkbox");
        checkbox.MergeAttribute("value", value.ToString());
        checkbox.MergeAttribute("name", htmlHelper.NameFor(expression).ToString());

        if ((enumValue & val) == val)
            checkbox.MergeAttribute("checked", "checked");
        if (htmlAttributes != null)
            checkbox.MergeAttributes(htmlAttributes);

        var label = new TagBuilder("label")  InnerHtml = checkbox + ((Enum)value).GetEnumName() ;

        sb.Append(label);
        sb.Append("<br/>");
    
    return new MvcHtmlString(sb.ToString());

再一次,简单的用法:

@Html.CheckBoxListForEnum(m => m.Gender)

顺便说一句,它只对标有Flags 属性的枚举有意义,当然你会遇到模型绑定的问题——它需要自定义绑定器。但这是另一个问题,我希望Jon Skeet 可以回答它:)

【讨论】:

Plus 1 用于讨论在 &lt;label&gt; 元素中包含复选框,这不需要唯一的 ID。我熟悉这种方法,但不推荐阅读它。然而,现在研究它,似乎这两种方法都同样有效。关于自定义 HtmlHelper,我已经编写了一个非常复杂的 HtmlHelper,正如我在此处的讨论中所指出的那样。如果有可用的枚举值,我的还将使用任何 [Description] 属性。我的复选框版本支持最初选择的值列表。 我仍然完全不知道为什么 MVC 中没有对此提供一些基本支持。事实上,即使使用将@Html.CheckBoxFor() 包装在&lt;Labe&gt; 元素中的技术也是无效的,因为它会生成重复的ID。即使这样有效,它也是一种糟糕的形式。 @JonathanWood 实际上我注意到它只是一个样本。希望您能弄清楚如何使用模型元数据和当前枚举值生成唯一 ID?此外,您可以根据需要自定义帮助程序,使用 htmlattributes,包装/不包装到ul 为什么在 MVC 中没有对它的基本支持?因为大多数时候人们不需要它?如果你真的这样做了 - 你可以自己编码,就像自定义下拉列表、特殊标签等等......【参考方案2】:

在我看来,我认为微软方面没有什么可以处理你所说的情况。

从Html代码开始,在html中,多单选按钮会如何呈现?

<form action="">
<input type="radio" name="sex" value="male">Male<br>
<input type="radio" name="sex" value="female">Female
</form>

以上是我从W3cSchools带来的样品,

如果你想通过点击标签来切换单选按钮,这可以在 html 中通过两种方式完成

第一种方法是在标签中使用for标签

<input type="radio" id="myId" name="MyName" value="MyValue" />
<label for="myId">Display Name</label>

第二种方法是将无线电输入放在标签标签内

<label>
   <input type="radio" id="myId" name="MyName" value="MyValue"/> 
   Display Name
</label>

如您所见,当您单击标签时,它的标签组合可让单选按钮切换,我认为微软让开发人员可以简单灵活地选择要做什么以及以他喜欢的方式。

现在,要在 Mvc 中获得相同的结果,您可以使用上述两种方法之一对每个单选按钮进行硬编码,或者为了更简单,编写自己的 Mvc Html 扩展,我推荐第二种方法。

希望对你有帮助

【讨论】:

有了这种心态,MVC 的CheckboxForLabelFor 或其他HTML 助手就没有理由了。微软觉得它们很值得,很多人都在使用它们。显然,它们很有用。在这里,只是有点缺乏支持。 我同意你的观点,如果我们概括这个想法,我肯定会得到与你写的相同的结果,但在你的情况下,有两个想法是另一个程序想要的结果与你不同想要,他不想在用户单击标签时切换复选框,而您希望在用户单击标签时切换复选框,为简单起见,如您所知,这是一种 mvc 框架,Microsoft在我看来,将提供基本的东西,这并不缺乏 我同意@HadiHassan 的观点,即微软提供了基本控件,在 winforms 应用程序中,我们开始使用不支持我们所有需求的控件,因此,我们不得不购买一个第三方工具。

以上是关于使用 RadioButtonFor 编码一组单选按钮的正确方法的主要内容,如果未能解决你的问题,请参考以下文章

如何在我的程序中保存一组单选按钮的状态?

jQuery使用数据目标更改带有onClick侦听器的一组单选按钮标签的innerHtml

如何在 Flutter 中只选择一组单选按钮的值?

在 WinForms 中对一组单选按钮进行数据绑定的最佳方法

如何居中对齐测验标题中心下方的一组单选按钮并保持响应?

制作一组单选按钮 [VB.NET]