首页 > 代码库 > 如何使用表单
如何使用表单
如何使用表单
原文:Working with Forms
作者:Rick Anderson、Dave Paquette、Jerrie 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
- Input Tag Helper
- Textarea Tag Helper
- Textarea Tag Helper
- Label Tag Helper
- 验证Tag Helper
- Select Tag Helper
- 其他资源
Form Tag Helper
表单 Form 的 Tag Helper:
- 为 MVC 控制器 Action 或已命名的路由生成 HTML
- 生成一个隐藏的 请求验证标记 来防止跨站请求伪装(当在 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=http://www.mamicode.com/"<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
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
andHtml.EditorFor
功能重叠。详情可参见 Input Tag Helper 的 HTML Helper 替代方法 一节。
An error occurred during the compilation of a resource required to processthis request. Please review the following specific error details and modifyyour 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=http://www.mamicode.com/"" /> <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=http://www.mamicode.com/"<removed for brevity>" /></form>
Email
和 Password
属性上应用的数据注释在该模型上生成元数据。Input Tag Helper读取模型元数据并生成 HTML5 data-val-*
属性(详见 Model Validation)。这些属性对验证器进行描述使其附加到 Input 字段上。这提供了 unobtrusive 的 HTML5 和 jQuery 验证。
替代 Input Tag Helper 的 Html Helper
Html.TextBox
、Html.TextBoxFor
、Html.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=http://www.mamicode.com/"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=http://www.mamicode.com/"" />
表达式名称与集合
示例,包含一个 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
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 '1024'." data-val-maxlength-max="1024" data-val-minlength="The field Description must be a string or array type with a minimum length of '5'." data-val-minlength-min="5" id="Description" name="Description"> </textarea> <button type="submit">Test</button> <input name="__RequestVerificationToken" type="hidden" value=http://www.mamicode.com/"<removed for brevity>" /></form>
Label Tag Helper
- 根据表达式名称在
<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
有两种验证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=http://www.mamicode.com/"" 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=http://www.mamicode.com/"<removed for brevity>" /></