ASP.NET 5 MVC 6中的单选按钮标记帮助程序
本文关键字:单选按钮 帮助程序 NET MVC ASP | 更新日期: 2023-09-27 18:28:42
在ASP.NET 5 MVC 6中,我没有看到任何用于单选按钮的标记帮助程序。在需要使用单选按钮的地方,处理表单元素的正确方法是什么?
所有输入类型都有一个TagHelper,其中也包括单选按钮类型。假设你有一个像这样的视图模型
public class CreateUserVm
{
public string UserName { set; get; }
public IEnumerable<RoleVm> Roles { set; get; }
public int SelectedRole { set; get; }
}
public class RoleVm
{
public int Id { set; get; }
public string RoleName { set; get; }
}
在你的GET行动中,
public IActionResult Index()
{
var vm = new CreateUserVm
{
Roles = new List<RoleVm>
{
new RoleVm {Id = 1, RoleName = "Admin"},
new RoleVm {Id = 2, RoleName = "Editor"},
new RoleVm {Id = 3, RoleName = "Reader"}
}
};
return View(vm);
}
在视图中,您可以简单地对输入标记使用标记。
@model YourNamespaceHere.CreateUserVm
<form asp-action="Index" asp-controller="Home">
<label class="label">User name</label>
<div class="col-md-10">
<input type="text" asp-for="UserName" />
</div>
<label class="label">Select a Role</label>
<div class="col-md-10">
@foreach (var item in Model.Roles)
{
<input asp-for="SelectedRole" type="radio" value="@item.Id" /> @item.RoleName
}
</div>
<input type="submit" />
</form>
当您发布表单时,所选角色的Rold Id将位于SelectedRole
属性中
请记住,上面的剃刀代码将为循环生成的每个输入生成具有相同Id
属性值和name
属性值的输入元素。在上面的示例中,它将生成3个输入元素(单选按钮类型),其中Id
和name
属性值设置为SelectedRole
。模型绑定将在name
属性值与视图模型中的属性名称(SelectedRole
)匹配时工作,但重复的Id属性值可能会给客户端代码带来麻烦(文档中重复的Id无效)
虽然有使用asp-for="SomeField"
的解决方案,但我发现最简单的解决方案是将视图模型字段与单选按钮的name
字段匹配。
视图模型:
public class MyViewModel
{
public string MyRadioField { get; set; }
}
形式(为清晰起见,无标签):
@model MyViewModel
<form asp-action="SomeAction" asp-controller="SomeController">
<input type="radio" name="MyRadioField" value="option1" checked />
<input type="radio" name="MyRadioField" value="option2" />
<input type="radio" name="MyRadioField" value="option3" />
<input type="submit" />
</form>
提交表单时,MyRadioField
将填充选中单选按钮的值。
我已经编写了自己的标记助手来实现这一点。它将input
标签替换为每个枚举变体的标签和单选按钮:
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DigitScpi.Web.Helpers;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Razor.TagHelpers;
namespace TagHelpers
{
/// Generates the radio buttons for an enum. Syntax: `<input radio asp-for="MyMappedEnumField"/>`.
[HtmlTargetElement("input", Attributes = RadioAttributeName)]
public class RadioButtonTagHelper : TagHelper
{
private const string RadioAttributeName = "radio";
private const string ForAttributeName = "asp-for";
[HtmlAttributeNotBound] [ViewContext] public ViewContext ViewContext { get; set; }
private readonly IHtmlGenerator _generator;
[HtmlAttributeName(ForAttributeName)] public ModelExpression For { get; set; }
[HtmlAttributeName(RadioAttributeName)] public bool RadioMarker { get; set; }
public RadioButtonTagHelper(IHtmlGenerator generator)
{
_generator = generator;
}
/// <inheritdoc />
public override void Process(TagHelperContext context, TagHelperOutput output)
{
output.SuppressOutput();
foreach (var enumItem in For.Metadata.EnumNamesAndValues)
{
var id = VariantId(enumItem);
var name = For.Metadata.EnumGroupedDisplayNamesAndValues.FirstOrDefault(v => v.Value == enumItem.Value).Key.Name;
var radio = _generator.GenerateRadioButton(ViewContext, For.ModelExplorer, For.Name, enumItem.Key, false, new {id});
var label = _generator.GenerateLabel(ViewContext, For.ModelExplorer, For.Name, name, new {@for = id});
output.PreElement.AppendHtml(radio);
output.PreElement.AppendHtml(label);
}
}
/// Computes the variant to be unique for each radiobutton.
private string VariantId(KeyValuePair<string, string> enumItem) =>
new StringBuilder()
.Append(ViewContext.CreateUniqueId(_generator.IdAttributeDotReplacement, For.Name))
.Append(_generator.IdAttributeDotReplacement)
.Append(enumItem.Key)
.ToString();
}
}
这里有一个扩展方法,它在给定SelectList的情况下呈现单选按钮列表。你使用它就像使用Html.DropDownListFor 一样
public static HtmlString RadioButtonListFor<TModel, TResult>(this IHtmlHelper<TModel> helper, Expression<Func<TModel, TResult>> expression, SelectList selectList, string className = "radio-group")
{
//render a list of radio buttons to bind to the specified value
MemberExpression member = expression.Body as MemberExpression;
string propertyName = member.Member.Name;
Func<TModel, TResult> method = expression.Compile();
TResult selectedValue = method(helper.ViewData.Model);
var output = new StringBuilder();
if (!string.IsNullOrEmpty(className))
output.AppendLine($"<div class='{className}'>");
else
output.AppendLine("<div>");
foreach (SelectListItem item in selectList)
{
bool selected = Convert.ToString(selectedValue) == item.Value;
string html = $"<label> <input type='radio' name='{propertyName}' value='{item.Value}' {(selected ? "checked" : "")} /> {item.Text} </label>";
output.AppendLine(html);
}
output.AppendLine("</div>");
return new HtmlString(output.ToString());
}
你在剃刀文件中这样称呼它:
@Html.RadioButtonListFor(m => m.ModelProperty, selectList)