# Building a template-driven form # 构建模板驱动表单 {@a template-driven} This tutorial shows you how to create a template-driven form whose control elements are bound to data properties, with input validation to maintain data integrity and styling to improve the user experience. 本教程将为你演示如何创建一个模板驱动表单,它的控件元素绑定到数据属性,并通过输入验证来保持数据的完整性和样式,以改善用户体验。 Template-driven forms use [two-way data binding](guide/architecture-components#data-binding "Intro to 2-way data binding") to update the data model in the component as changes are made in the template and vice versa. 当在模板中进行更改时,模板驱动表单会使用[双向数据绑定](guide/architecture-components#data-binding "介绍 2 路数据绑定")来更新组件中的数据模型,反之亦然。
Angular supports two design approaches for interactive forms. You can build forms by writing templates using Angular [template syntax and directives](guide/glossary#template "Definition of template terms") with the form-specific directives and techniques described in this tutorial, or you can use a reactive (or model-driven) approach to build forms. Angular 支持两种交互式表单的设计方法。你可以使用 Angular 中的[模板语法和指令,](guide/glossary#template "模板术语的定义")以及本教程中描述的表单专用指令和技巧编写模板来构建表单,或者你可以使用响应式方式(或叫模型驱动方式)来构建表单。 Template-driven forms are suitable for small or simple forms, while reactive forms are more scalable and suitable for complex forms. For a comparison of the two approaches, see [Introduction to Forms](guide/forms-overview "Overview of Angular forms.") 模板驱动表单适用于小型或简单的表单,而响应式表单则更具伸缩性,适用于复杂表单。要比较这两种方法,参阅[“表单简介”](guide/forms-overview "Angular 表单概述")
You can build almost any kind of form with an Angular template—login forms, contact forms, and pretty much any business form. You can lay out the controls creatively and bind them to the data in your object model. You can specify validation rules and display validation errors, conditionally enable or disable specific controls, trigger built-in visual feedback, and much more. 你可以用 Angular 模板来构建各种表单,比如登录表单、联系人表单和几乎所有的业务表单。你可以创造性地对控件进行布局并把它们绑定到对象模型的数据上。你可以指定验证规则并显示验证错误,有条不紊地启用或禁用特定控件,触发内置的视觉反馈等等。 This tutorial shows you how to build a form from scratch, using a simplified sample form like the one from the [Tour of Heroes tutorial](tutorial "Tour of Heroes") to illustrate the techniques. 本教程将向你展示如何通过一个简化的范例表单来从头构建一个表单,就像“[英雄之旅”教程的](tutorial "英雄之旅")中用一个表单来讲解这些技巧一样。
Run or download the example app: . 运行或下载范例应用:
## Objectives ## 目标 This tutorial teaches you how to do the following: 本教程将教你如何执行以下操作: * Build an Angular form with a component and template. 使用组件和模板构建一个 Angular 表单。 * Use `ngModel` to create two-way data bindings for reading and writing input-control values. 使用 `ngModel` 创建双向数据绑定,以便读写输入控件的值。 * Provide visual feedback using special CSS classes that track the state of the controls. 使用跟踪控件状态的特殊 CSS 类来提供视觉反馈。 * Display validation errors to users and enable or disable form controls based on the form status. 向用户显示验证错误,并根据表单状态启用或禁用表单控件。 * Share information across HTML elements using [template reference variables](guide/template-reference-variables). [使用模板引用变量](guide/template-reference-variables)在 HTML 元素之间共享信息。 ## Prerequisites ## 先决条件 Before going further into template-driven forms, you should have a basic understanding of the following. 在进一步研究模板驱动表单之前,你应该对下列内容有一个基本的了解。 * TypeScript and HTML5 programming. TypeScript 和 HTML5 编程。 * Angular app-design fundamentals, as described in [Angular Concepts](guide/architecture "Introduction to Angular concepts."). Angular 的应用设计基础,就像[Angular 概念简介](guide/architecture "Angular 概念简介。")中描述的那样。 * The basics of [Angular template syntax](guide/template-syntax "Template syntax guide"). [Angular 模板语法](guide/template-syntax "模板语法指南")的基础知识。 * The form-design concepts that are presented in [Introduction to Forms](guide/forms-overview "Overview of Angular forms."). [“表单简介”](guide/forms-overview "Angular 表单概述")中提供的表单设计概念。 {@a intro} ## Build a template-driven form ## 构建一个模板驱动表单 Template-driven forms rely on directives defined in the `FormsModule`. 模板驱动表单依赖于 `FormsModule` 定义的指令。 * The `NgModel` directive reconciles value changes in the attached form element with changes in the data model, allowing you to respond to user input with input validation and error handling. `NgModel` 指令会协调其附着在的表单元素中的值变更与数据模型中的变更,以便你通过输入验证和错误处理来响应用户输入。 * The `NgForm` directive creates a top-level `FormGroup` instance and binds it to a `
` element to track aggregated form value and validation status. As soon as you import `FormsModule`, this directive becomes active by default on all `` tags. You don't need to add a special selector. `NgForm` 指令会创建一个顶级的 `FormGroup` 实例,并把它绑定到 `` 元素上,以跟踪它所聚合的那些表单值并验证状态。只要你导入了 `FormsModule`,默认情况下这个指令就会在所有 `` 标签上激活。你不需要添加特殊的选择器。 * The `NgModelGroup` directive creates and binds a `FormGroup` instance to a DOM element. `NgModelGroup` 指令会创建 `FormGroup` 的实例并把它绑定到 DOM 元素中。 ### The sample application ### 范例应用 The sample form in this guide is used by the *Hero Employment Agency* to maintain personal information about heroes. Every hero needs a job. This form helps the agency match the right hero with the right crisis. *英雄雇佣管理局*使用本指南中的范例表单来维护英雄的个人信息。毕竟英雄也要工作啊。这个表单有助于该机构将正确的英雄与正确的危机匹配起来。 The form highlights some design features that make it easier to use. For instance, the two required fields have a green bar on the left to make them easy to spot. These fields have initial values, so the form is valid and the **Submit** button is enabled. 该表单突出了一些易于使用的设计特性。比如,这两个必填字段的左边是绿色条,以便让它们醒目。这些字段都有初始值,所以表单是有效的,并且 **Submit** 按钮也是启用的。 As you work with this form, you will learn how to include validation logic, how to customize the presentation with standard CSS, and how to handle error conditions to ensure valid input. If the user deletes the hero name, for example, the form becomes invalid. The app detects the changed status, and displays a validation error in an attention-grabbing style. In addition, the **Submit** button is disabled, and the "required" bar to the left of the input control changes from green to red. 当你使用这个表单时,你将学习如何包含验证逻辑,如何使用标准 CSS 自定义表达式,以及如何处理错误条件以确保输入的有效性。例如,如果用户删除了英雄的名字,那么表单就会失效。该应用会检测已更改的状态,并以醒目的样式显示验证错误。此外,**Submit** 按钮会被禁用,输入控件左侧的“必填”栏也会从绿色变为红色。 ### Step overview ### 步骤概述 In the course of this tutorial, you bind a sample form to data and handle user input using the following steps. 在本教程中,你将使用以下步骤将一个范例表单绑定到数据并处理用户输入。 1. Build the basic form. 建立基本表单。 * Define a sample data model. 定义一个范例数据模型。 * Include required infrastructure such as the `FormsModule`. 包括必需的基础设施,比如 `FormsModule` 。 2. Bind form controls to data properties using the `ngModel` directive and two-way data-binding syntax. 使用 `ngModel` 指令和双向数据绑定语法把表单控件绑定到数据属性。 * Examine how `ngModel` reports control states using CSS classes. 检查 `ngModel` 如何使用 CSS 类报告控件状态。 * Name controls to make them accessible to `ngModel`. 为控件命名,以便让 `ngModel` 可以访问它们。 3. Track input validity and control status using `ngModel`. 用 `ngModel` 跟踪输入的有效性和控件的状态。 * Add custom CSS to provide visual feedback on the status. 添加自定义 CSS 来根据状态提供可视化反馈。 * Show and hide validation-error messages. 显示和隐藏验证错误信息。 4. Respond to a native HTML button-click event by adding to the model data. 通过添加到模型数据来响应原生 HTML 按钮的单击事件。 5. Handle form submission using the [`ngSubmit`](api/forms/NgForm#properties) output property of the form. 使用表单的 [`ngSubmit`](api/forms/NgForm#properties) 输出属性来处理表单提交。 * Disable the **Submit** button until the form is valid. 在表单生效之前,先禁用 **Submit** 按钮。 * After submit, swap out the finished form for different content on the page. 在提交完成后,把已完成的表单替换成页面上不同的内容。 {@a step1} ## Build the form ## 建立表单 You can recreate the sample application from the code provided here, or you can examine or download the . 你可以根据这里提供的代码从头创建范例应用,也可以查看 。 1. The provided sample application creates the `Hero` class which defines the data model reflected in the form. 这里提供的范例应用会创建一个 `Hero` 类,用于定义表单中所反映的数据模型。 2. The form layout and details are defined in the `HeroFormComponent` class. 该表单的布局和细节是在 `HeroFormComponent` 类中定义的。 The component's `selector` value of "app-hero-form" means you can drop this form in a parent template using the `` tag. 该组件的 `selector` 值为 “app-hero-form”,意味着你可以用 `` 标签把这个表单放到父模板中。 3. The following code creates a new hero instance, so that the initial form can show an example hero. 下面的代码会创建一个新的 hero 实例,以便让初始的表单显示一个范例英雄。 This demo uses dummy data for `model` and `powers`. In a real app, you would inject a data service to get and save real data, or expose these properties as inputs and outputs. 这个演示使用虚拟数据来表达 `model` 和 `powers` 。在真正的应用中,你会注入一个数据服务来获取和保存实际数据,或者把它们作为输入属性和输出属性进行公开。 4. The application enables the Forms feature and registers the created form component. 该应用启用了表单功能,并注册了已创建的表单组件。 5. The form is displayed in the application layout defined by the root component's template. 该表单显示在根组件模板定义的应用布局中。 The initial template defines the layout for a form with two form groups and a submit button. The form groups correspond to two properties of the Hero data model, name and alterEgo. Each group has a label and a box for user input. 初始模板定义了一个带有两个表单组和一个提交按钮的表单布局。表单组对应于 Hero 数据模型的两个属性:name 和 alterEgo。每个组都有一个标签和一个用户输入框。 * The **Name** `` control element has the HTML5 `required` attribute. **Name** `` 控件元素中包含了 HTML5 的 `required` 属性。 * The **Alter Ego** `` control element does not because `alterEgo` is optional. **Alter Ego** `` 没有控件元素,因为 `alterEgo` 是可选的。 The **Submit** button has some classes on it for styling. At this point, the form layout is all plain HTML5, with no bindings or directives. **Submit** 按钮里面有一些用于样式化的类。此时,表单布局全都是纯 HTML5,没有绑定或指令。 6. The sample form uses some style classes from [Twitter Bootstrap](https://getbootstrap.com/css/): `container`, `form-group`, `form-control`, and `btn`. To use these styles, the app's style sheet imports the library. 范例表单使用的是 [Twitter Bootstrap 中的](https://getbootstrap.com/css/)一些样式类: `container` , `form-group` , `form-control` 和 `btn` 。要使用这些样式,就要在该应用的样式表中导入该库。 7. The form makes the hero applicant choose one superpower from a fixed list of agency-approved powers. The predefined list of `powers` is part of the data model, maintained internally in `HeroFormComponent`. The Angular [NgForOf directive](api/common/NgForOf "API reference") iterates over the data values to populate the `` 元素。 If you run the app right now, you see the list of powers in the selection control. The input elements are not yet bound to data values or events, so they are still blank and have no behavior. 如果你现在正在运行该应用,你会看到选择控件中的超能力列表。由于尚未将这些 input 元素绑定到数据值或事件,因此它们仍然是空白的,没有任何行为。 {@a ngModel} ## Bind input controls to data properties ## 把输入控件绑定到数据属性 The next step is to bind the input controls to the corresponding `Hero` properties with two-way data binding, so that they respond to user input by updating the data model, and also respond to programmatic changes in the data by updating the display. 下一步是使用双向数据绑定把输入控件绑定到相应的 `Hero` 属性,这样它们就可以通过更新数据模型来响应用户的输入,并通过更新显示来响应数据中的程序化变更。 The `ngModel` directive declared in the `FormsModule` lets you bind controls in your template-driven form to properties in your data model. When you include the directive using the syntax for two-way data binding, `[(ngModel)]`, Angular can track the value and user interaction of the control and keep the view synced with the model. 该 `ngModel` 指令是由 `FormsModule` 声明的,它能让你把模板驱动表单中的控件绑定到数据模型中的属性。当你使用双向数据绑定的语法 `[(ngModel)]` 引入该指令时,Angular 就可以跟踪控件的值和用户交互,并保持视图与模型的同步。 1. Edit the template file `hero-form.component.html`. 编辑模板 `hero-form.component.html` 。 2. Find the `` tag next to the **Name** label. 找到 **Name** 标签旁边的 `` 标记。 3. Add the `ngModel` directive, using two-way data binding syntax `[(ngModel)]="..."`. 使用双向数据绑定语法 `[(ngModel)]="..."` 添加 `ngModel` 指令。
This example has a temporary diagnostic interpolation after each input tag, `{{model.name}}`, to show the current data value of the corresponding property. The note reminds you to remove the diagnostic lines when you have finished observing the two-way data binding at work. 这个例子中在每个 input 标记后面都有一个临时的诊断插值 `{{model.name}}`,以显示相应属性的当前数据值。本提醒是为了让你在观察完这个双向数据绑定后删除这些诊断行。
{@a ngForm} ### Access the overall form status ### 访问表单的整体状态 When you imported the `FormsModule` in your component, Angular automatically created and attached an [NgForm](api/forms/NgForm "API reference for NgForm") directive to the `` tag in the template (because `NgForm` has the selector `form` that matches `` elements). 当你导入了 `FormsModule` 时,Angular 会自动为模板中的 `` 标签创建并附加一个 [NgForm](api/forms/NgForm "NgForm 的 API 参考") 指令。(因为 `NgForm` 定义了一个能匹配 `` 元素的选择器 `form`)。 To get access to the `NgForm` and the overall form status, declare a [template reference variable](guide/template-reference-variables). 要访问 `NgForm` 和表单的整体状态,[就要声明一个模板引用变量](guide/template-reference-variables)。 1. Edit the template file `hero-form.component.html`. 编辑模板 `hero-form.component.html` 。 2. Update the `` tag with a template reference variable, `#heroForm`, and set its value as follows. 为 `` 标签添加模板引用变量 `#heroForm`,并把它的值设置如下。 The `heroForm` template variable is now a reference to the `NgForm` directive instance that governs the form as a whole. 模板变量 `heroForm` 现在是对 `NgForm` 指令实例的引用,该指令实例管理整个表单。 3. Run the app. 运行该应用。 4. Start typing in the **Name** input box. 开始在 **Name** 输入框中输入。 As you add and delete characters, you can see them appear and disappear from the data model. For example: 在添加和删除字符时,你可以看到它们从数据模型中出现和消失。例如: The diagnostic line that shows interpolated values demonstrates that values are really flowing from the input box to the model and back again. 用来显示插值的诊断行证明了这些值确实从输入框流向了模型,然后再返回。 ### Naming control elements ### 为控件元素命名 When you use `[(ngModel)]` on an element, you must define a `name` attribute for that element. Angular uses the assigned name to register the element with the `NgForm` directive attached to the parent `` element. 在元素上使用 `[(ngModel)]` 时,必须为该元素定义一个 `name` 属性。Angular 会用这个指定的名字来把这个元素注册到父 `` 元素上的 `NgForm` 指令中。 The example added a `name` attribute to the `` element and set it to "name", which makes sense for the hero's name. Any unique value will do, but using a descriptive name is helpful. 这个例子中为 `` 元素添加了一个 `name` 属性,并把它的值设置为 “name”,用来表示英雄的名字。任何唯一的值都可以用,但最好用描述性的名称。 1. Add similar `[(ngModel)]` bindings and `name` attributes to **Alter Ego** and **Hero Power**. 为**Alter Ego**和**Hero Power**添加类似的 `[(ngModel)]` 绑定和 `name` 属性。 2. You can now remove the diagnostic messages that show interpolated values. 你现在可以移除显示插值的诊断消息了。 3. To confirm that two-way data binding works for the entire hero model, add a new binding at the top to the component's `diagnostic` property. 要想确认双向数据绑定是否在整个英雄模型上都有效,就要在该组件的顶部添加一个对 `diagnostic` 属性的新绑定。 After these revisions, the form template should look like the following: 表单模板修改完毕后,应如下所示: * Notice that each `` element has an `id` property. This is used by the `