原文:Working with Forms
作者:Rick AndersonDave PaquetteJerrie Pelser
翻译:姚阿勇(Dr.Yao)
校对:孟帅洋(书缘)

这篇文章演示了如何使用表单以及表单中常用的 HTML 元素。HTML 的 Form 元素提供了 Web 应用向服务器回发数据的主要机制。本文的大部分在描述 Tag Helpers 以及它们如何能帮你有效地构建健壮的表单。在阅读本文之前,我们建议你阅读一下 Tag Helpers 。

在很多情况下,HTML Helpers 都提供了对某个 Tag Helper 的替代方法,但重要的是必须意识到 Tag Helper 不是要取代 HTML Helper,而且也并不是每个 HTML Helper 都有对应的 Tag Helper。当一个 HTML Helper 作为替代方案存在时,是有意为之的。

章节:

 

表单 Form 的 Tag Helper:

  • 为 MVC 控制器 Action 或已命名的路由生成 HTML
    的 action 属性值。
  • 生成一个隐藏的 请求验证标记 来防止跨站请求伪装(当在 HTTP Post 操作方法上应用了 [ValidateAntiForgeryToken] 特性时)。
  • 提供 asp-route-<参数名> 属性, <参数名> 是路由里面添加过的值。 Html.BeginForm 和 Html.BeginRouteForm 的 routeValues 参数提供了类似的功能。
  • 有 HTML Helper 替代方法 Html.BeginForm 和 Html.BeginRouteForm

示例:

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

上面的 Form Tag Helper 生成如下的 HTML :

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

MVC 运行时(runtime)根据 Form Tag Helper 的属性 asp-controller 和 asp-action 生成 action属性值。Form Tag Helper 也会生成一个隐藏的 请求验证标记 来防止跨站请求伪装(当在HTTP Post 方法上应用了 [ValidateAntiForgeryToken] 特性时)。要保护纯 HTML 避免跨站请求伪装是非常困难的,Form Tag Helper 为你提供了这个服务。

 

使用命名路由

Tag Helper 属性 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 的设置将你重定向到登录页面。

 

Input Tag Helper将 HTML <input> 元素绑定到 Razor 视图中的模型表达式上。

语法:

复制代码
<input asp-for="<Expression Name>" />
 

Input Tag Helper:

  • 为 asp-for 属性中指定的表达式名称生成 id 和 name HTML 属性。 asp-for="Property1.Property2" 等价于 m => m.Property1.Property2 ,就是说属性值实际上是表达式的一部分。 asp-for 属性值所使用的就是表达式的名称。
  • 基于模型类型和应用在模型属性上的 数据注释 特性来设置 HTML type 的属性值。
  • 如果 HTML type 属性已被指定,则不会覆盖它。
  • 根据应用在模型属性上的 数据注释 特性生成 HTML5 验证属性。
  • 与 HTML Helper Html.TextBoxFor and Html.EditorFor 功能重叠。详情可参见 Input Tag Helper 的 HTML Helper 替代方法 一节。
复制代码
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 Tag Helper基于 .NET 类型来设置 HTML type 属性。下表列出了一些常见的 .NET 类型和生成出的 HTML 类型(并非所有 .NET 类型都在列)。

.NET 类型 Input 类型
Bool type="checkbox"
String type="text"
DateTime type="datetime"
Byte type="number"
Int type="number"
Single, Double type="number"

下表列出了 Input Tag Helper会将其映射到指定 Input 类型的一些常见 数据注释 特性(并非所有特性都在列)。

Attribute Input Type
[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 e-mail 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>
 

Email 和 Password 属性上应用的数据注释在该模型上生成元数据。Input Tag Helper读取模型元数据并生成 HTML5 data-val-* 属性(详见 Model Validation)。这些属性对验证器进行描述使其附加到 Input 字段上。这提供了 unobtrusive 的 HTML5 和 jQuery 验证。

 

替代 Input Tag Helper 的 Html Helper

Html.TextBoxHtml.TextBoxForHtml.Editor 和 Html.EditorFor 有着与 Input Tag Helper 重复的功能。Input Tag Helper 会自动设置 type 属性;Html.TextBox 和 Html.TextBoxFor 则不会。Html.Editor 和 Html.EditorFor 会处理集合、复杂对象以及模版;Input Tag Helper 则不会。Input Tag Helper 、Html.EditorFor 和 Html.TextBoxFor 是强类型的(它们使用 lambda 表达式);Html.TextBox 和 Html.Editor 则不是(它们使用表达式名称)。

 

表达式名称

asp-for 属性值是一个 ModelExpression 同时也是 lambda 表达式右边的部分。因此,你不需要使用 Model 前缀,因为 asp-for="Property1" 在生成的代码中会变成 m => m.Property1 。

复制代码
@{
    var joe = "Joe";
}
<input asp-for="@joe" />
 

生成以下代码:

复制代码
<input type="text" id="joe" name="joe" value="Joe" />
 
 

定位子属性

你还可以通过视图模型的属性路径定位到子属性。考虑这个更复杂的模型,它包含了一个 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>
 

以下 HTML 是根据 Address.AddressLine1 生成的:

复制代码
<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; }
 }
 

Action 方法:

复制代码
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>
 
复制代码
@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>
*@
 

注意
应始终使用 for (而 不是 foreach )遍历列表。在 LINQ 表达式中执行索引器会产生开销应当尽量减少。


注意
上面示例中被注释的代码演示了应当如何使用 @ 操作符代替 lambda 表达式去访问列表中的每一个 ToDoItem 。

 

Textarea Tag Helper 与 Input Tag Helper类似。

  • 为 <textarea> 元素生成 id 和 name 属性,以及数据验证属性。
  • 提供强类型。
  • HTML Helper 替代选项: 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>
 

生成以下代码:

复制代码
<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 Helper 替代选项: Html.LabelFor 。

Label Tag Helper 相对于纯 HTML label 元素具有以下优势:

  • 可从 Display 特性自动获得描述性的 Label 值。随着时间推移,预期的显示名称可能会变化,而结合使用 Display 特性与 Label Tag Helper将会在所有使用它的地方应用 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>
 

Label Tag Helper生成了 "Email" 的 for 属性值,也就是与 <input> 元素关联的 ID 。Tag Helper生成一致的 id 和 for 元素,因此它们可以正确地关联起来。本例中的标签文本来自于 Display 特性。如果模型没有 Display 特性,标签文本则会是表达式的属性名称。

 

有两种验证Tag Helper。Validation Message Tag Helper(用来显示模型上单个属性的验证信息),和Validation Summary Tag Helper (用来显示验证错误汇总)。Input Tag Helper 根据模型类的数据注释给 input 元素添加 HTML5 客户端验证属性。验证也在服务端执行。Validation Tag Helper会在验证发生错误的时候显示这些错误信息。

 

Validaton Message Tag Helper

  • 添加 HTML5 data-valmsg-for="property" 属性到 span 元素,使验证错误信息附加到指定模型属性的 input 字段上。当客户端验证发生错误,jQuery 会在 <span> 元素里显示错误信息。
  • 验证也发生在服务端。客户端可能会禁用 JavaScript 那么验证就只能在服务端完成。
  • HTML Helper 替代选项: Html.ValidationMessageFor

Validaton Message Tag Helper 与 HTML span 元素上的 asp-validation-for 属性一起使用。

复制代码
<span asp-validation-for="Email"></span>
 

Validation Message Tag Helper将生成以下 HTML :

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

通常在模型属性相同的 Input Tag Helper后面使用 Validation Message Tag Helper 。这样可以在发生验证错误的 input 旁边显示错误信息。

注意
必须有一个正确引用了 JavaScript 和 jQuery 脚本的视图进行客户端验证。详见: Model Validation 。

当服务端验证发生了错误(比如你有自定义的服务端验证或者客户端验证被禁用),MVC 会把错误信息放在 <span> 元素的正文中。

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

验证摘要Tag Helper

  • 选取带有 asp-validation-summary 属性的 <div> 元素。
  • HTML Helper 替代选项:@Html.ValidationSummary 。

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

asp-validation-summary Validation messages displayed
ValidationSummary.All Property and model level
ValidationSummary.ModelOnly Model
ValidationSummary.None None
 

示例

在以下示例中,数据模型装饰了 DataAnnotation 特性,用以在 <input> 元素上生成验证错误信息。当发生验证错误的时候, Validation Tag Helper显示错误信息:

复制代码
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="ValidationSummary.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 e-mail 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 元素。

Select Tag Helper 的 asp-for 为 select 元素指定模型的属性名称,而 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>
 

注意
我们不推荐将 ViewBag 或 ViewData 用于 Select Tag Helper 。视图模型在提供 MVC 元数据方面更加健壮并且通常来说问题更少。

asp-for 属性值是一个特例,不需要 Model 前缀,而其他的 Tag Helper 属性则需要(比如 asp-items )。

复制代码
 <select asp-for="Country" asp-items="Model.Countries"></select> 
 
 

枚举绑定

将 enum 属性用于 <select> 并根据 enum 的值生成 `SelectListItemselectlistitem] 元素通常是很方便的。

示例:

复制代码
 public class CountryEnumViewModel
 {
     public CountryEnum EnumCountry { get; set; }
 }
 
复制代码
using System.ComponentModel.DataAnnotations;

namespace FormsTagHelper.ViewModels
{
    {
        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> 元素。

CountryViewModelGroup 把 SelectListItem 元素分到 "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; }
 }
 

下面展示了这两个分组:

如何使用表单
    

如何使用表单

生成的 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 类型, Select Tag Helper 将会自动生成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>
 
 

无选择

想要允许无选择,可添加一个 “未选择” 项到选择列表。如果该模型属性是一个 值类型,则需要使其为可空值 nullable 。

复制代码
@model CountryViewModel

<form asp-controller="Home" asp-action="IndexEmpty" method="post">
    <select asp-for="Country" asp-items="Model.Countries">
        <option value="">&lt;none&gt;</option>
    </select> 
    <br /><button type="submit">Register</button>
</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 <option> 元素并不局限于 无选择 的情况。比如,下面的视图和 Action 方法会生成和上面类似的 HTML :

复制代码
  public IActionResult IndexOption(int id)
  {
      var model = new CountryViewModel();
      model.Country = "CA";
      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>
 

<option> 元素将会根据当前的 Country 值被正确选中(加上 selected="selected" 属性)。

复制代码
 <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>
 
 

返回目录

相关文章: