ASP.NET Core 表单中的标记帮助程序

[删除(380066935@qq.com或微信通知)]

更好的阅读体验请查看原文:https://docs.microsoft.com/zh-cn/aspnet/core/mvc/views/working-with-forms?view=aspnetcore-6.0

作者:Rick AndersonN. Taylor MullenDave PaquetteJerrie Pelser

本文档演示如何使用表单和表单中常用的 HTML 元素。 HTML Form 元素提供 Web 应用用于向服务器回发数据的主要机制。 本文档的大部分内容介绍标记帮助程序及其如何帮助高效创建可靠的 HTML 表单。 建议在阅读本文档前先阅读标记帮助程序简介

在很多情况下,HTML 帮助程序为特定标记帮助程序提供了一种替代方法,但标记帮助程序不会替代 HTML 帮助程序,且并非每个 HTML 帮助程序都有对应的标记帮助程序,认识到这点也很重要。 如果存在 HTML 帮助程序替代项,文中会提到。

表单标记帮助程序

表单标记帮助程序

  • 为 MVC 控制器操作或命名路由生成 HTML <FORM>>action 属性值

  • 生成隐藏的请求验证令牌,防止跨站点请求伪造(在 HTTP Post 操作方法中与 [ValidateAntiForgeryToken] 属性配合使用时)

  • 提供 asp-route-<Parameter Name> 属性,其中 <Parameter Name> 添加到路由值。 Html.BeginFormHtml.BeginRouteFormrouteValues 参数提供类似的功能。

  • 具有 HTML 帮助程序替代项 Html.BeginFormHtml.BeginRouteForm

示例:

<form asp-controller="Demo" asp-action="Register" method="post">
    <!-- Input and Submit elements -->
</form>

上述表单标记帮助程序生成以下 HTML:

<form method="post" action="/Demo/Register">
    <!-- Input and Submit elements -->
    <input name="__RequestVerificationToken" type="hidden" value="<removed for brevity>">
</form>

MVC 运行时通过表单标记帮助程序属性 asp-controllerasp-action 生成 action 属性值。 表单标记帮助程序还会生成隐藏的请求验证令牌,防止跨站点请求伪造(在 HTTP Post 操作方法中与 [ValidateAntiForgeryToken] 属性配合使用时)。 保护纯 HTML 表单免受跨站点请求伪造的影响很难,但表单标记帮助程序可提供此服务。

使用命名路由

asp-route 标记帮助程序属性还可为 HTML action 属性生成标记。 具有名为 register路由的应用可将以下标记用于注册页:

<form asp-route="register" method="post">
    <!-- Input and Submit elements -->
</form>

Views/Account 文件夹中的许多视图(在新建使用个人用户帐户身份验证的 Web 应用时生成)包含 asp-route-returnurl 属性:

<form asp-controller="Account" asp-action="Login"
     asp-route-returnurl="@ViewData["ReturnUrl"]"
     method="post" class="form-horizontal" role="form">

注意

使用内置模板时,returnUrl 仅会在用户尝试访问授权资源,但未验证身份或未获得授权的情况下自动填充。 如果尝试执行未经授权的访问,安全中间件会使用 returnUrl 集将用户重定向至登录页。

窗体操作标记帮助程序

窗体操作标记帮助程序在生成的 <button ...><input type="image" ...> 标记上生成 formaction 属性。 formaction 属性控制窗体在何处提交数据。 它绑定到 image 类型的 <input> 元素以及 <button> 元素。 窗体操作标记帮助程序允许使用多个 AnchorTagHelperasp- 属性来控制为相应元素生成的 formaction 链接。

用于控制 formaction 值的受支持的 AnchorTagHelper 属性:

Attribute 说明
asp-controller 控制器的名称。
asp-action 操作方法的名称。
asp-area 区域名称。
asp-page Razor 页面的名称。
asp-page-handler Razor 页面处理程序的名称。
asp-route 路由的名称。
asp-route-{value} 单个 URL 路由值。 例如,asp-route-id="1234"
asp-all-route-data 所有路由值。
asp-fragment URL 片段。

提交到控制器示例

选中输入或按钮时,下面的标记将窗体提交到 HomeControllerIndex 操作:

<form method="post">
    <button asp-controller="Home" asp-action="Index">Click Me</button>
    <input type="image" src="..." alt="Or Click Me" asp-controller="Home" 
                                asp-action="Index">
</form>

之前的标记将生成以下 HTML:

<form method="post">
    <button formaction="/Home">Click Me</button>
    <input type="image" src="..." alt="Or Click Me" formaction="/Home">
</form>

提交到页示例

以下标记将表单提交到 AboutRazor 页面:

<form method="post">
    <button asp-page="About">Click Me</button>
    <input type="image" src="..." alt="Or Click Me" asp-page="About">
</form>

之前的标记将生成以下 HTML:

<form method="post">
    <button formaction="/About">Click Me</button>
    <input type="image" src="..." alt="Or Click Me" formaction="/About">
</form>

提交到路由示例

请考虑使用 /Home/Test 终结点:

public class HomeController : Controller
{
    [Route("/Home/Test", Name = "Custom")]
    public string Test()
    {
        return "This is the test page";
    }
}

以下标记将窗体提交到 /Home/Test 终结点。

<form method="post">
    <button asp-route="Custom">Click Me</button>
    <input type="image" src="..." alt="Or Click Me" asp-route="Custom">
</form>

之前的标记将生成以下 HTML:

<form method="post">
    <button formaction="/Home/Test">Click Me</button>
    <input type="image" src="..." alt="Or Click Me" formaction="/Home/Test">
</form>

输入标记帮助程序

输入标记帮助程序将 HTML <input> 元素绑定到 Razor 视图中的模型表达式。

语法:

<input asp-for="<Expression Name>">

输入标记帮助程序:

  • asp-for 属性中指定的表达式名称生成 idname HTML 属性。 asp-for="Property1.Property2" 等效于 m => m.Property1.Property2。 表达式的名称用于 asp-for 属性值。 有关其他信息,请参阅表达式名称部分。

  • 根据模型类型和应用于模型属性的数据注释特性设置 HTML type 特性值

  • 如果已经指定,不会覆盖 HTML type 属性值

  • 通过应用于模型属性的数据注释特性生成 HTML5 验证特性

  • 具有与 Html.TextBoxForHtml.EditorFor 重叠的 HTML 帮助程序功能。 有关详细信息,请参阅输入标记帮助程序的 HTML 帮助程序替代项部分。

  • 提供强类型化。 如果属性的名称更改,但未更新标记帮助程序,则会收到类似如下内容的错误:

    An error occurred during the compilation of a resource required to process
    this request. Please review the following specific error details and modify
    your source code appropriately.
    
    Type expected
    'RegisterViewModel' does not contain a definition for 'Email' and no
    extension method 'Email' accepting a first argument of type 'RegisterViewModel'
    could be found (are you missing a using directive or an assembly reference?)
    

Input 标记帮助程序根据 .NET 类型设置 HTML type 属性。 下表列出一些常见的 .NET 类型和生成的 HTML 类型(并未列出每个 .NET 类型)。

.NET 类型 输入类型
Bool type="checkbox"
String type="text"
DateTime type="datetime-local"
Byte type="number"
int type="number"
Single、Double type="number"

下表显示输入标记帮助程序会映射到特定输入类型的一些常见数据注释属性(并未列出每个验证属性):

Attribute 输入类型
[EmailAddress] type="email"
[Url] type="url"
[HiddenInput] type="hidden"
[Phone] type="tel"
[DataType(DataType.Password)] type="password"
[DataType(DataType.Date)] type="date"
[DataType(DataType.Time)] type="time"

示例:

using System.ComponentModel.DataAnnotations;

namespace FormsTagHelper.ViewModels
{
    public class RegisterViewModel
    {
        [Required]
        [EmailAddress]
        [Display(Name = "Email Address")]
        public string Email { get; set; }

        [Required]
        [DataType(DataType.Password)]
        public string Password { get; set; }
    }
}
@model RegisterViewModel

<form asp-controller="Demo" asp-action="RegisterInput" method="post">
    Email:  <input asp-for="Email" /> <br />
    Password: <input asp-for="Password" /><br />
    <button type="submit">Register</button>
</form>

上述代码生成以下 HTML:

  <form method="post" action="/Demo/RegisterInput">
      Email:
      <input type="email" data-val="true"
             data-val-email="The Email Address field is not a valid email address."
             data-val-required="The Email Address field is required."
             id="Email" name="Email" value=""><br>
      Password:
      <input type="password" data-val="true"
             data-val-required="The Password field is required."
             id="Password" name="Password"><br>
      <button type="submit">Register</button>
      <input name="__RequestVerificationToken" type="hidden" value="<removed for brevity>">
   </form>

应用于 EmailPassword 属性的数据注释在模型中生成元数据。 输入标记帮助程序使用模型元数据并生成 HTML5data-val-* 属性(请参阅模型验证)。 这些属性描述要附加到输入字段的验证程序。 这样可以提供非介入式 HTML5 和 jQuery 验证。 非干扰属性具有格式data-val-rule="Error Message",其中规则是验证规则的名称 ((如data-val-requireddata-val-emaildata-val-maxlength等等)) 如果属性中提供了错误消息,则会将其显示为属性的值data-val-rule。 还有表单 data-val-ruleName-argumentName="argumentValue" 的属性,这些属性提供有关规则的其他详细信息,例如,data-val-maxlength-max="1024"

将多个 input 控件绑定到同一属性时,生成的控件共享相同的 id控件,这使得生成的标记无效。 若要防止重复,请显式指定 id 每个控件的属性。

复选框隐藏输入呈现

HTML5 中的复选框在未选中时不会提交值。 为了使未选中的复选框能够发送默认值,输入标记帮助程序会为复选框生成一个额外的隐藏输入。

以下列 Razor 标记为例,该标记将输入标记帮助程序用于布尔模型属性 IsChecked

<form method="post">
    <input asp-for="@Model.IsChecked" />
    <button type="submit">Submit</button>
</form>

上述 Razor 标记生成类似于以下内容的 HTML 标记:

<form method="post">
    <input name="IsChecked" type="checkbox" value="true" />
    <button type="submit">Submit</button>

    <input name="IsChecked" type="hidden" value="false" /> 
</form>

上述 HTML 标记显示一个额外的隐藏输入,其名称为 IsChecked,值为 false。 默认情况下,此隐藏输入在表单末尾呈现。 提交表单时:

  • 如果选中 IsChecked 复选框输入,truefalse 都作为值提交。
  • 如果未选中 IsChecked 复选框输入,则仅提交隐藏输入值 false

ASP.NET Core 模型绑定进程在绑定到 bool 值时仅读取第一个值,这导致选中的复选框为 true,未选中的复选框为 false

若要配置隐藏输入呈现的行为,请设置 MvcViewOptions.HtmlHelperOptions 上的 CheckBoxHiddenInputRenderMode 属性。 例如:

services.Configure<MvcViewOptions>(options =>
    options.HtmlHelperOptions.CheckBoxHiddenInputRenderMode =
        CheckBoxHiddenInputRenderMode.None);

上述代码通过将 CheckBoxHiddenInputRenderMode 设置为 CheckBoxHiddenInputRenderMode.None 来禁用复选框的隐藏输入呈现。 有关所有可用的呈现模式,请参阅 CheckBoxHiddenInputRenderMode 枚举。

输入标记帮助程序的 HTML 帮助程序替代项

Html.TextBoxHtml.TextBoxForHtml.EditorHtml.EditorFor 与输入标记帮助程序的功能存在重叠。 输入标记帮助程序会自动设置 type 属性;而 Html.TextBoxHtml.TextBoxFor 不会。 Html.EditorHtml.EditorFor 处理集合、复杂对象和模板;而输入标记帮助程序不会。 输入标记帮助程序、Html.EditorForHtml.TextBoxFor 是强类型(使用 Lambda 表达式);而 Html.TextBoxHtml.Editor 不是(使用表达式名称)。

HtmlAttributes

@Html.Editor()@Html.EditorFor() 在执行其默认模板时使用名为 htmlAttributes 的特殊 ViewDataDictionary 条目。 此行为可选择使用 additionalViewData 参数增强。 键“htmlAttributes”区分大小写。 键“htmlAttributes”的处理方式与传递到输入帮助程序的 htmlAttributes 对象(例如 @Html.TextBox())的处理方式类似。

@Html.EditorFor(model => model.YourProperty, 
  new { htmlAttributes = new { @class="myCssClass", style="Width:100px" } })

表达式名称

asp-for 属性值是 ModelExpression,并且是 lambda 表达式的右侧。 因此,asp-for="Property1" 在生成的代码中变成 m => m.Property1,这也是无需使用 Model 前缀的原因。 可使用“@”字符作为内联表达式的开头并移到 m. 之前:

@{
  var joe = "Joe";
}

<input asp-for="@joe">

生成以下 HTML:

<input type="text" id="joe" name="joe" value="Joe">

使用集合属性时,asp-for="CollectionProperty[23].Member"i 具有值 23 时生成与 asp-for="CollectionProperty[i].Member" 相同的名称。

在 ASP.NET Core MVC 计算 ModelExpression 的值时,它会检查多个源,包括 ModelState。 以 <input type="text" asp-for="Name"> 为例。 计算出的 value 属性是第一个非 null 值,属于:

  • 带有“Name”键的 ModelState 条目。
  • Model.Name 表达式的结果。

还可使用视图模型的属性路径导航到子属性。 设想一个包含子 Address 属性的更复杂的模型类。

public class AddressViewModel
{
    public string AddressLine1 { get; set; }
}
public class RegisterAddressViewModel
{
    public string Email { get; set; }

    [DataType(DataType.Password)]
    public string Password { get; set; }

    public AddressViewModel Address { get; set; }
}

在视图中,绑定到 Address.AddressLine1

@model RegisterAddressViewModel

<form asp-controller="Demo" asp-action="RegisterAddress" method="post">
    Email:  <input asp-for="Email" /> <br />
    Password: <input asp-for="Password" /><br />
    Address: <input asp-for="Address.AddressLine1" /><br />
    <button type="submit">Register</button>
</form>

Address.AddressLine1 生成以下 HTML:

<input type="text" id="Address_AddressLine1" name="Address.AddressLine1" value="">

表达式名称和集合

包含 Colors 数组的模型示例:

public class Person
{
    public List<string> Colors { get; set; }

    public int Age { get; set; }
}

操作方法:

public IActionResult Edit(int id, int colorIndex)
{
    ViewData["Index"] = colorIndex;
    return View(GetPerson(id));
}

以下 Razor 演示如何访问特定 Color 元素:

@model Person
@{
    var index = (int)ViewData["index"];
}

<form asp-controller="ToDo" asp-action="Edit" method="post">
    @Html.EditorFor(m => m.Colors[index])
    <label asp-for="Age"></label>
    <input asp-for="Age" /><br />
    <button type="submit">Post</button>
</form>

模板 Views/Shared/EditorTemplates/String.cshtml

@model string

<label asp-for="@Model"></label>
<input asp-for="@Model" /> <br />

使用 List<T> 的示例:

public class ToDoItem
{
    public string Name { get; set; }

    public bool IsDone { get; set; }
}

以下 Razor 演示如何循环访问集合:

@model List<ToDoItem>

<form asp-controller="ToDo" asp-action="Edit" method="post">
    <table>
        <tr> <th>Name</th> <th>Is Done</th> </tr>

        @for (int i = 0; i < Model.Count; i++)
        {
            <tr>
                @Html.EditorFor(model => model[i])
            </tr>
        }

    </table>
    <button type="submit">Save</button>
</form>

模板 Views/Shared/EditorTemplates/ToDoItem.cshtml

@model ToDoItem

<td>
    <label asp-for="@Model.Name"></label>
    @Html.DisplayFor(model => model.Name)
</td>
<td>
    <input asp-for="@Model.IsDone" />
</td>

@*
    This template replaces the following Razor which evaluates the indexer three times.
    <td>
         <label asp-for="@Model[i].Name"></label>
         @Html.DisplayFor(model => model[i].Name)
     </td>
     <td>
         <input asp-for="@Model[i].IsDone" />
     </td>
*@

应尽量在 asp-forHtml.DisplayFor 等效上下文中使用值 foreach。 一般情况下,for 优于 foreach(如果情况允许使用的话),因为它不需要分配枚举器,但在 LINQ 表达式中评估索引器的成本高昂,应最大限度地减少使用它。

注意

上述带有注释的示例代码演示如何将 lambda 表达式替换为 @ 运算符来访问列表中的每个 ToDoItem

文本区标记帮助程序

Textarea Tag Helper 标记帮助程序类似于输入标记帮助程序。

  • 通过模型为 <textarea> 元素生成 idname 属性以及数据验证属性。

  • 提供强类型化。

  • HTML 帮助程序替代项:Html.TextAreaFor

示例:

using System.ComponentModel.DataAnnotations;

namespace FormsTagHelper.ViewModels
{
    public class DescriptionViewModel
    {
        [MinLength(5)]
        [MaxLength(1024)]
        public string Description { get; set; }
    }
}
@model DescriptionViewModel

<form asp-controller="Demo" asp-action="RegisterTextArea" method="post">
    <textarea asp-for="Description"></textarea>
    <button type="submit">Test</button>
</form>

生成以下 HTML:

<form method="post" action="/Demo/RegisterTextArea">
  <textarea data-val="true"
   data-val-maxlength="The field Description must be a string or array type with a maximum length of &#x27;1024&#x27;."
   data-val-maxlength-max="1024"
   data-val-minlength="The field Description must be a string or array type with a minimum length of &#x27;5&#x27;."
   data-val-minlength-min="5"
   id="Description" name="Description">
  </textarea>
  <button type="submit">Test</button>
  <input name="__RequestVerificationToken" type="hidden" value="<removed for brevity>">
</form>

标签标记帮助程序

  • <label> 元素中为表达式名称生成标签描述和 for 属性

  • HTML 帮助程序替代项:Html.LabelFor

Label Tag Helper 通过纯 HTML 标签元素提供如下优势:

  • 可自动从 Display 属性中获取描述性标签值。 预期的显示名称可能会随时间变化,Display 属性和标签标记帮助程序的组合会在其被使用的所有位置应用 Display

  • 源代码中的标记更少

  • 模型属性的强类型化。

示例:

using System.ComponentModel.DataAnnotations;

namespace FormsTagHelper.ViewModels
{
    public class SimpleViewModel
    {
        [Required]
        [EmailAddress]
        [Display(Name = "Email Address")]
        public string Email { get; set; }
    }
}

@model SimpleViewModel

<form asp-controller="Demo" asp-action="RegisterLabel" method="post">
    <label asp-for="Email"></label>
    <input asp-for="Email" /> <br />
</form>

<label> 元素生成以下 HTML:

<label for="Email">Email Address</label>

标签标记帮助程序生成“Email”的 for 属性值,即与 <input> 元素关联的 ID。 标记帮助程序生成一致的 idfor 元素,方便将其正确关联。 本示例中的描述来自 Display 属性。 如果模型不包含 Display 特性,描述将为表达式的属性名称。 若要替代默认标题,请在标签标记中添加标题。

验证标记帮助程序

有两个验证标记帮助程序。 Validation Message Tag Helper(为模型中的单个属性显示验证消息)和 Validation Summary Tag Helper(显示验证错误的摘要)。 Input Tag Helper 根据模型类的数据注释属性将 HTML5 客户端验证属性添加到输入元素中。 同时在服务器上执行验证。 验证标记帮助程序会在发生验证错误时显示这些错误消息。

验证消息标记帮助程序

  • HTML5data-valmsg-for="property" 属性添加到 span 元素中,该元素会附加指定模型属性的输入字段中的验证错误消息。 jQuery 会在发生客户端验证错误时在 <span> 元素中显示错误消息。

  • 还会在服务器上执行验证。 客户端可能已禁用 JavaScript,一些验证仅可在服务器端执行。

  • HTML 帮助程序替代项:Html.ValidationMessageFor

Validation Message Tag Helper 与 HTML span 元素中的 asp-validation-for 属性配合使用。

<span asp-validation-for="Email"></span>

验证消息标记帮助程序会生成以下 HTML:

<span class="field-validation-valid"
  data-valmsg-for="Email"
  data-valmsg-replace="true"></span>

对于同一属性,通常在 Input 标记帮助程序后使用 Validation Message Tag Helper。 这样做可在导致错误的输入附近显示所有验证错误消息。

注意

必须拥有包含正确的 JavaScript 和 jQuery 脚本引用的视图才能执行客户端验证。 有关详细信息,请参阅模型验证

发生服务器端验证错误时(例如,禁用自定义服务器端验证或客户端验证时),MVC 会将该错误消息作为 <span> 元素的主体。

<span class="field-validation-error" data-valmsg-for="Email"
            data-valmsg-replace="true">
   The Email Address field is required.
</span>

验证摘要标记帮助程序

  • 针对具有 asp-validation-summary 属性的 <div> 元素

  • HTML 帮助程序替代项:@Html.ValidationSummary

Validation Summary Tag Helper 用于显示验证消息的摘要。 asp-validation-summary 属性值可以是以下任意值:

asp-validation-summary 显示的验证消息
All 属性和模型级别
ModelOnly 建模
None

示例

在以下示例中,数据模型具有 DataAnnotation 属性,在 <input> 元素中生成验证错误消息。 验证标记帮助程序会在发生验证错误时显示错误消息:

using System.ComponentModel.DataAnnotations;

namespace FormsTagHelper.ViewModels
{
    public class RegisterViewModel
    {
        [Required]
        [EmailAddress]
        [Display(Name = "Email Address")]
        public string Email { get; set; }

        [Required]
        [DataType(DataType.Password)]
        public string Password { get; set; }
    }
}
@model RegisterViewModel

<form asp-controller="Demo" asp-action="RegisterValidation" method="post">
    <div asp-validation-summary="ModelOnly"></div>
    Email:  <input asp-for="Email" /> <br />
    <span asp-validation-for="Email"></span><br />
    Password: <input asp-for="Password" /><br />
    <span asp-validation-for="Password"></span><br />
    <button type="submit">Register</button>
</form>

生成的 HTML(如果模型有效):

<form action="/DemoReg/Register" method="post">
  <div class="validation-summary-valid" data-valmsg-summary="true">
  <ul><li style="display:none"></li></ul></div>
  Email:  <input name="Email" id="Email" type="email" value=""
   data-val-required="The Email field is required."
   data-val-email="The Email field is not a valid email address."
   data-val="true"><br>
  <span class="field-validation-valid" data-valmsg-replace="true"
   data-valmsg-for="Email"></span><br>
  Password: <input name="Password" id="Password" type="password"
   data-val-required="The Password field is required." data-val="true"><br>
  <span class="field-validation-valid" data-valmsg-replace="true"
   data-valmsg-for="Password"></span><br>
  <button type="submit">Register</button>
  <input name="__RequestVerificationToken" type="hidden" value="<removed for brevity>">
</form>

选择标记帮助程序

  • 为模型属性生成 select 元素和关联的 option 元素。

  • 具有 HTML 帮助程序替代项 Html.DropDownListForHtml.ListBoxFor

Select Tag Helperasp-forselect 元素指定模型属性名称,asp-items 指定 option 元素。 例如:

<select asp-for="Country" asp-items="Model.Countries"></select> 

示例:

using Microsoft.AspNetCore.Mvc.Rendering;
using System.Collections.Generic;

namespace FormsTagHelper.ViewModels
{
    public class CountryViewModel
    {
        public string Country { get; set; }

        public List<SelectListItem> Countries { get; } = new List<SelectListItem>
        {
            new SelectListItem { Value = "MX", Text = "Mexico" },
            new SelectListItem { Value = "CA", Text = "Canada" },
            new SelectListItem { Value = "US", Text = "USA"  },
        };
    }
}

Index 方法初始化 CountryViewModel,设置选定的国家/地区并将其传递到 Index 视图。

public IActionResult Index()
{
    var model = new CountryViewModel();
    model.Country = "CA";
    return View(model);
}

HTTP POST Index 方法显示选定内容:

[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Index(CountryViewModel model)
{
    if (ModelState.IsValid)
    {
        var msg = model.Country + " selected";
        return RedirectToAction("IndexSuccess", new { message = msg });
    }

    // If we got this far, something failed; redisplay form.
    return View(model);
}

Index 视图:

@model CountryViewModel

<form asp-controller="Home" asp-action="Index" method="post">
    <select asp-for="Country" asp-items="Model.Countries"></select> 
    <br /><button type="submit">Register</button>
</form>

生成以下 HTML(选择“CA”时):

<form method="post" action="/">
     <select id="Country" name="Country">
       <option value="MX">Mexico</option>
       <option selected="selected" value="CA">Canada</option>
       <option value="US">USA</option>
     </select>
       <br /><button type="submit">Register</button>
     <input name="__RequestVerificationToken" type="hidden" value="<removed for brevity>">
   </form>

注意

不建议将 ViewBagViewData 与选择标记帮助程序配合使用。 视图模型在提供 MVC 元数据方面更可靠且通常更不容易出现问题。

asp-for 属性值是特殊情况,它不要求提供 Model 前缀,但其他标记帮助程序属性需要该前缀(例如 asp-items

<select asp-for="Country" asp-items="Model.Countries"></select> 

枚举绑定

通常可方便地将 <select>enum 属性配合使用并通过 enum 值生成 SelectListItem 元素。

示例:

    public class CountryEnumViewModel
    {
        public CountryEnum EnumCountry { get; set; }
    }
}
using System.ComponentModel.DataAnnotations;

namespace FormsTagHelper.ViewModels
{
    public enum CountryEnum
    {
        [Display(Name = "United Mexican States")]
        Mexico,
        [Display(Name = "United States of America")]
        USA,
        Canada,
        France,
        Germany,
        Spain
    }
}

GetEnumSelectList 方法为枚举生成 SelectList 对象。

@model CountryEnumViewModel

<form asp-controller="Home" asp-action="IndexEnum" method="post">
    <select asp-for="EnumCountry" 
            asp-items="Html.GetEnumSelectList<CountryEnum>()">
    </select> 
    <br /><button type="submit">Register</button>
</form>

可使用 Display 属性标记枚举器列表,以获取更丰富的 UI:

using System.ComponentModel.DataAnnotations;

namespace FormsTagHelper.ViewModels
{
    public enum CountryEnum
    {
        [Display(Name = "United Mexican States")]
        Mexico,
        [Display(Name = "United States of America")]
        USA,
        Canada,
        France,
        Germany,
        Spain
    }
}

生成以下 HTML:

  <form method="post" action="/Home/IndexEnum">
         <select data-val="true" data-val-required="The EnumCountry field is required."
                 id="EnumCountry" name="EnumCountry">
             <option value="0">United Mexican States</option>
             <option value="1">United States of America</option>
             <option value="2">Canada</option>
             <option value="3">France</option>
             <option value="4">Germany</option>
             <option selected="selected" value="5">Spain</option>
         </select>
         <br /><button type="submit">Register</button>
         <input name="__RequestVerificationToken" type="hidden" value="<removed for brevity>">
    </form>

选项组

如果视图模型包含一个或多个 SelectListGroup 对象,则会生成 HTML <optgroup> 元素。

CountryViewModelGroupSelectListItem 元素分组为“North America”组和“Europe”组:

public class CountryViewModelGroup
{
    public CountryViewModelGroup()
    {
        var NorthAmericaGroup = new SelectListGroup { Name = "North America" };
        var EuropeGroup = new SelectListGroup { Name = "Europe" };

        Countries = new List<SelectListItem>
        {
            new SelectListItem
            {
                Value = "MEX",
                Text = "Mexico",
                Group = NorthAmericaGroup
            },
            new SelectListItem
            {
                Value = "CAN",
                Text = "Canada",
                Group = NorthAmericaGroup
            },
            new SelectListItem
            {
                Value = "US",
                Text = "USA",
                Group = NorthAmericaGroup
            },
            new SelectListItem
            {
                Value = "FR",
                Text = "France",
                Group = EuropeGroup
            },
            new SelectListItem
            {
                Value = "ES",
                Text = "Spain",
                Group = EuropeGroup
            },
            new SelectListItem
            {
                Value = "DE",
                Text = "Germany",
                Group = EuropeGroup
            }
      };
    }

    public string Country { get; set; }

    public List<SelectListItem> Countries { get; }

两个组如下所示:

option group example

生成的 HTML:

 <form method="post" action="/Home/IndexGroup">
      <select id="Country" name="Country">
          <optgroup label="North America">
              <option value="MEX">Mexico</option>
              <option value="CAN">Canada</option>
              <option value="US">USA</option>
          </optgroup>
          <optgroup label="Europe">
              <option value="FR">France</option>
              <option value="ES">Spain</option>
              <option value="DE">Germany</option>
          </optgroup>
      </select>
      <br /><button type="submit">Register</button>
      <input name="__RequestVerificationToken" type="hidden" value="<removed for brevity>">
 </form>

多重选择

如果 asp-for 特性中指定的属性为 IEnumerable,选择标记帮助程序会自动生成 multiple = "multiple" 特性。 例如,如果给定以下模型:

using Microsoft.AspNetCore.Mvc.Rendering;
using System.Collections.Generic;

namespace FormsTagHelper.ViewModels
{
    public class CountryViewModelIEnumerable
    {
        public IEnumerable<string> CountryCodes { get; set; }

        public List<SelectListItem> Countries { get; } = new List<SelectListItem>
        {
            new SelectListItem { Value = "MX", Text = "Mexico" },
            new SelectListItem { Value = "CA", Text = "Canada" },
            new SelectListItem { Value = "US", Text = "USA"    },
            new SelectListItem { Value = "FR", Text = "France" },
            new SelectListItem { Value = "ES", Text = "Spain"  },
            new SelectListItem { Value = "DE", Text = "Germany"}
         };
    }
}

及以下视图:

@model CountryViewModelIEnumerable

<form asp-controller="Home" asp-action="IndexMultiSelect" method="post">
    <select asp-for="CountryCodes" asp-items="Model.Countries"></select> 
    <br /><button type="submit">Register</button>
</form>

则会生成以下 HTML:

<form method="post" action="/Home/IndexMultiSelect">
    <select id="CountryCodes"
    multiple="multiple"
    name="CountryCodes"><option value="MX">Mexico</option>
<option value="CA">Canada</option>
<option value="US">USA</option>
<option value="FR">France</option>
<option value="ES">Spain</option>
<option value="DE">Germany</option>
</select>
    <br /><button type="submit">Register</button>
  <input name="__RequestVerificationToken" type="hidden" value="<removed for brevity>">
</form>

无选定内容

如果发现自己在多个页面中使用“未指定”选项,可创建模板用于消除重复的 HTML:

@model CountryViewModel

<form asp-controller="Home" asp-action="IndexEmpty" method="post">
    @Html.EditorForModel()
    <br /><button type="submit">Register</button>
</form>

模板 Views/Shared/EditorTemplates/CountryViewModel.cshtml

@model CountryViewModel

<select asp-for="Country" asp-items="Model.Countries">
    <option value="">--none--</option>
</select>

添加 HTML <选项> 元素不限于 “无选择 ”大小写。 例如,以下视图和操作方法会生成与上述代码类似的 HTML:

public IActionResult IndexNone()
{
    var model = new CountryViewModel();
    model.Insert(0, new SelectListItem("<none>", ""));
    return View(model);
}
@model CountryViewModel

<form asp-controller="Home" asp-action="IndexEmpty" method="post">
    <select asp-for="Country">
        <option value="">&lt;none&gt;</option>
        <option value="MX">Mexico</option>
        <option value="CA">Canada</option>
        <option value="US">USA</option>
    </select> 
    <br /><button type="submit">Register</button>
</form>

根据当前的 Country 值选择正确的 <option> 元素(包含 selected="selected" 属性)。

public IActionResult IndexOption(int id)
{
    var model = new CountryViewModel();
    model.Country = "CA";
    return View(model);
}
 <form method="post" action="/Home/IndexEmpty">
      <select id="Country" name="Country">
          <option value="">&lt;none&gt;</option>
          <option value="MX">Mexico</option>
          <option value="CA" selected="selected">Canada</option>
          <option value="US">USA</option>
      </select>
      <br /><button type="submit">Register</button>
   <input name="__RequestVerificationToken" type="hidden" value="<removed for brevity>">
 </form>

其他资源