| 
									
										
										
										
											2016-06-01 11:54:30 -07:00
										 |  |  |  | include ../_util-fns | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  | a#top | 
					
						
							| 
									
										
										
										
											2016-06-01 11:54:30 -07:00
										 |  |  |  | :marked | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |   We can improve overall data quality by validating user input for accuracy and completeness. | 
					
						
							| 
									
										
										
										
											2016-08-26 21:02:32 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-18 22:40:53 +01:00
										 |  |  |  |   我们可以通过验证用户输入的准确性和完整性,来增强整体数据质量。 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |   In this cookbook we show how to validate user input in the UI and display useful validation messages  | 
					
						
							|  |  |  |  |   using first the template-driven forms and then the reactive forms approach. | 
					
						
							| 
									
										
										
										
											2016-10-18 22:40:53 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   在本烹饪书中,我们展示在界面中如何验证用户输入,并显示有用的验证信息,先使用模板驱动表单方式,再使用响应式表单方式。 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  | .l-sub-section | 
					
						
							|  |  |  |  |   :marked | 
					
						
							|  |  |  |  |     Learn more about these choices in the [Forms chapter.](../guide/forms.html) | 
					
						
							| 
									
										
										
										
											2016-06-01 11:54:30 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-18 22:40:53 +01:00
										 |  |  |  |     参见[表单章节](../guide/forms.html)了解关于这些选择的更多知识。 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  | a#toc | 
					
						
							| 
									
										
										
										
											2016-06-01 11:54:30 -07:00
										 |  |  |  | :marked | 
					
						
							| 
									
										
										
										
											2016-09-12 09:19:37 -07:00
										 |  |  |  |   ## Table of Contents | 
					
						
							| 
									
										
										
										
											2016-06-01 11:54:30 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-18 22:40:53 +01:00
										 |  |  |  |   ## 目录 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |     [Simple Template-Driven Forms](#template1) | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-18 22:40:53 +01:00
										 |  |  |  |     [简单的模板驱动表单](#template1) | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |     [Template-Driven Forms with validation messages in code](#template2) | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-18 22:40:53 +01:00
										 |  |  |  |     [代码中拥有验证信息的模板驱动表单](#template2) | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |     [Reactive Forms with validation in code](#reactive) | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-18 22:40:53 +01:00
										 |  |  |  |     [代码中拥有验证的响应式表单](#reactive) | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |     [Custom validation](#custom-validation) | 
					
						
							| 
									
										
										
										
											2016-06-01 11:54:30 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-18 22:40:53 +01:00
										 |  |  |  |     [自定义验证](#custom-validation) | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |     [Testing](#testing) | 
					
						
							| 
									
										
										
										
											2016-06-01 11:54:30 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-18 22:40:53 +01:00
										 |  |  |  |     [测试](#testing) | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  | a#live-example | 
					
						
							|  |  |  |  | :marked | 
					
						
							|  |  |  |  |   **Try the live example to see and download the full cookbook source code** | 
					
						
							| 
									
										
										
										
											2016-10-18 22:40:53 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   **查看在线例子,并下载整个烹饪书的源代码** | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | live-example(name="cb-form-validation" embedded img="cookbooks/form-validation/plunker.png") 在线例子 | 
					
						
							| 
									
										
										
										
											2016-06-01 11:54:30 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | .l-main-section | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  | a#template1 | 
					
						
							| 
									
										
										
										
											2016-06-01 11:54:30 -07:00
										 |  |  |  | :marked | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |    ## Simple Template-Driven Forms | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-18 22:40:53 +01:00
										 |  |  |  |    ## 简单的模板驱动表单 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |    In the template-driven approach, you arrange  | 
					
						
							|  |  |  |  |    [form elements](https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Forms_in_HTML) in the component's template. | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-18 22:40:53 +01:00
										 |  |  |  |    在模板驱动表单方法中,你在组件的模板中组织[表单元素](https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Forms_in_HTML)。 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |    You add Angular form directives (mostly directives beginning `ng...`) to help | 
					
						
							|  |  |  |  |    Angular construct a corresponding internal control model that implements form functionality. | 
					
						
							|  |  |  |  |    We say that the control model is _implicit_ in the template. | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-18 22:40:53 +01:00
										 |  |  |  |    你可以添加Angular表单指令(通常为以`ng`开头的指令)来帮助Angular构建对应的内部控制模型,以实现表单功能。 | 
					
						
							|  |  |  |  |    控制模型在模板中是**隐式**的。 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |    To validate user input, you add [HTML validation attributes](https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/HTML5/Constraint_validation)  | 
					
						
							|  |  |  |  |    to the elements. Angular interprets those as well, adding validator functions to the control model. | 
					
						
							| 
									
										
										
										
											2016-06-01 11:54:30 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-18 22:40:53 +01:00
										 |  |  |  |    要验证用户输入,你添加[HTML验证属性](https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/HTML5/Constraint_validation)到元素中。 | 
					
						
							| 
									
										
										
										
											2016-10-19 20:52:28 +01:00
										 |  |  |  |    Angular拦截这些元素,添加验证器函数到控制模型中。 | 
					
						
							| 
									
										
										
										
											2016-10-18 22:40:53 +01:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |    Angular exposes information about the state of the controls including  | 
					
						
							|  |  |  |  |    whether the user has "touched" the control or made changes and if the control values are valid. | 
					
						
							| 
									
										
										
										
											2016-06-01 11:54:30 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-18 22:40:53 +01:00
										 |  |  |  |    Angular暴露关于控制状态的信息,包括用户是否已经“触摸“了控制器,或者用户已经作了更新和控制器的值是否还有效。 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |    In the first template validation example,  | 
					
						
							|  |  |  |  |    we add more HTML to read that control state and update the display appropriately. | 
					
						
							| 
									
										
										
										
											2016-08-26 21:02:32 -07:00
										 |  |  |  |    Here's an excerpt from the template html for a single input box control bound to the hero name: | 
					
						
							| 
									
										
										
										
											2016-10-18 22:40:53 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |    在第一个模板验证例子中,我们添加了更多HTML,来读取控制器状态并适当更新显示。 | 
					
						
							|  |  |  |  |    下面是模板HTML中提取的,一个绑定到英雄名字的输入框控制器: | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  | +makeExample('cb-form-validation/ts/app/template/hero-form-template1.component.html','name-with-error-msg','template/hero-form-template1.component.html (Hero name)')(format='.') | 
					
						
							| 
									
										
										
										
											2016-06-01 11:54:30 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | :marked | 
					
						
							| 
									
										
										
										
											2016-08-26 21:02:32 -07:00
										 |  |  |  |   Note the following: | 
					
						
							| 
									
										
										
										
											2016-10-19 17:01:51 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   请注意一下几点: | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |   - The `<input>` element carries the HTML validation attributes: `required`, `minlength`, and `maxlength`. | 
					
						
							| 
									
										
										
										
											2016-06-01 11:54:30 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-19 17:01:51 +01:00
										 |  |  |  |   - `<input>`元素具有HTML验证属性:`required`、`minlength`、和 `maxlength`。 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |   - We set the `name` attribute of the input box to `"name"` so Angular can track this input element and associate it | 
					
						
							|  |  |  |  |   with an Angular form control called `name` in its internal control model. | 
					
						
							| 
									
										
										
										
											2016-08-26 21:02:32 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-19 17:01:51 +01:00
										 |  |  |  |   - 我们设置输入框的`name`属性为`"name"`,这样Angular可以跟踪这个输入元素,并将其内部控制器模型的一个名为`name`的Angular表单控制关联起来。 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |   - We use the `[(ngModel)]` directive to two-way data bind the input box to the `hero.name` property. | 
					
						
							| 
									
										
										
										
											2016-10-19 17:01:51 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   - 我们使用`[(ngModel)]`指令,将输入框双向数据绑定到`hero.name`属性。 | 
					
						
							| 
									
										
										
										
											2016-08-26 21:02:32 -07:00
										 |  |  |  |    | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |   - We set a template variable (`#name`) to the value `"ngModel"` (always `ngModel`). | 
					
						
							|  |  |  |  |   This gives us a reference to the Angular `NgModel` directive  | 
					
						
							|  |  |  |  |   associated with this control that we can use _in the template_ | 
					
						
							|  |  |  |  |   to check for control states such as `valid` and `dirty`. | 
					
						
							| 
									
										
										
										
											2016-08-26 21:02:32 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-19 17:01:51 +01:00
										 |  |  |  |   - 我们将模板变量(`#name`)赋值为`"ngModel"` (总是 `ngModel`)。 | 
					
						
							|  |  |  |  |   它为我们提供了与这个控制器关联的Angular `NgModel`指令的引用,我们在模板中使用它,以检查控制器状态,比如`valid`和`dirty`。 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |   - The `*ngIf` on `<div>` element reveals a set of nested message `divs` but only if there are "name" errors and | 
					
						
							| 
									
										
										
										
											2016-08-26 21:02:32 -07:00
										 |  |  |  |   the control is either `dirty` or `touched`. | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-19 17:01:51 +01:00
										 |  |  |  |   - `<div>`元素的`*ngIf`揭露了一套嵌套消息`divs`,但是只在有“name”错误和控制器为`dirty`或者`touched`。 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |   - Each nested `<div>` can present a custom message for one of the possible validation errors. | 
					
						
							|  |  |  |  |   We've prepared messages for `required`, `minlength`, and `maxlength`. | 
					
						
							| 
									
										
										
										
											2016-08-26 21:02:32 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-19 17:01:51 +01:00
										 |  |  |  |   - 每个嵌套的`<div>`为其中一个可能出现的验证错误显示一条自定义消息。我们已经为`required`、`minlength`、和 `maxlength`准备了消息。 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-26 21:02:32 -07:00
										 |  |  |  |   The full template repeats this kind of layout for each data entry control on the form. | 
					
						
							| 
									
										
										
										
											2016-10-19 17:01:51 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   整个模板为表单上的每种数据输入控制器重复这种布局。 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-01 11:54:30 -07:00
										 |  |  |  | .l-sub-section | 
					
						
							|  |  |  |  |   :marked | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |     #### Why check _dirty_ and _touched_? | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-04 12:41:10 +00:00
										 |  |  |  |     #### 为何检查**dirty**和**touched**? | 
					
						
							| 
									
										
										
										
											2016-10-19 17:01:51 +01:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-26 21:02:32 -07:00
										 |  |  |  |     We shouldn't show errors for a new hero before the user has had a chance to edit the value. | 
					
						
							|  |  |  |  |     The checks for `dirty` and `touched` prevent premature display of errors. | 
					
						
							| 
									
										
										
										
											2016-06-01 11:54:30 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-19 17:01:51 +01:00
										 |  |  |  |     当用户创建一个新英雄时,在还没有机会输入之前,我们不应该显示任何错误。 | 
					
						
							|  |  |  |  |     检查`dirty`和`touched`防止了这种过早的错误显示。 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-01 11:54:30 -07:00
										 |  |  |  |     Learn about `dirty` and `touched` in the [Forms](../guide/forms.html) chapter. | 
					
						
							| 
									
										
										
										
											2016-10-19 17:01:51 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     参见[表单](../guide/forms.html)章,学习关于`dirty`和`touched`的知识。 | 
					
						
							| 
									
										
										
										
											2016-06-01 11:54:30 -07:00
										 |  |  |  | :marked | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |   The component class manages the hero model used in the data binding | 
					
						
							|  |  |  |  |   as well as other code to support the view. | 
					
						
							| 
									
										
										
										
											2016-06-01 11:54:30 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-19 17:01:51 +01:00
										 |  |  |  |   组件类管理用于数据绑定的英雄模型,它还有其他支持视图的代码。 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  | +makeExample('cb-form-validation/ts/app/template/hero-form-template1.component.ts','class','template/hero-form-template1.component.ts (class)') | 
					
						
							| 
									
										
										
										
											2016-06-01 11:54:30 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | :marked | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |    Use this template-driven validation technique when working with static forms with simple, standard validation rules. | 
					
						
							| 
									
										
										
										
											2016-06-01 11:54:30 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-19 17:01:51 +01:00
										 |  |  |  |    在处理简单的、拥有标准验证规则的静态表单时,使用这种模板驱动验证方法。 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |    Here are the complete files for the first version of `HeroFormTemplateCompononent` in the template-driven approach: | 
					
						
							| 
									
										
										
										
											2016-06-01 11:54:30 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-19 17:01:51 +01:00
										 |  |  |  |    下面是第一个版本的使用模板驱动方法的`HeroFormTemplateComponent`: | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  | +makeTabs( | 
					
						
							|  |  |  |  |   `cb-form-validation/ts/app/template/hero-form-template1.component.html, | 
					
						
							|  |  |  |  |    cb-form-validation/ts/app/template/hero-form-template1.component.ts`, | 
					
						
							| 
									
										
										
										
											2016-06-01 11:54:30 -07:00
										 |  |  |  |   '',  | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |   `template/hero-form-template1.component.html, | 
					
						
							|  |  |  |  |    template/hero-form-template1.component.ts`) | 
					
						
							| 
									
										
										
										
											2016-06-01 11:54:30 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | .l-main-section | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  | a#template2 | 
					
						
							|  |  |  |  | :marked | 
					
						
							|  |  |  |  |   ## Template-Driven Forms with validation messages in code | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-19 17:01:51 +01:00
										 |  |  |  |   ## 验证消息在代码中的模板驱动表单 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |   While the layout is straightforward,  | 
					
						
							|  |  |  |  |   there are obvious shortcomings with the way we handle validation messages: | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-19 17:01:51 +01:00
										 |  |  |  |   虽然布局很直观,但是我们处理验证消息的方法有明显的缺陷: | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |   * It takes a lot of HTML to represent all possible error conditions.  | 
					
						
							|  |  |  |  |   This gets out of hand when there are many controls and many validation rules. | 
					
						
							| 
									
										
										
										
											2016-10-19 17:01:51 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   * 它使用了很多HTML来表现所有可能出现的错误情况。 | 
					
						
							|  |  |  |  |   如果有太多控制器和太多验证规则,我们就失去了控制。 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |    | 
					
						
							|  |  |  |  |   * We're not fond of so much JavaScript logic in HTML. | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-19 17:01:51 +01:00
										 |  |  |  |   * 我们不喜欢在HTML里面插入这么多JavaScript。 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |   * The messages are static strings, hard-coded into the template.  | 
					
						
							|  |  |  |  |   We often require dynamic messages that we should shape in code. | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-19 17:01:51 +01:00
										 |  |  |  |   * 这些消息是静态的字符串,被硬编码到模板中。我们通常要求在代码中可以塑造的动态消息。 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |   We can move the logic and the messages into the component with a few changes to  | 
					
						
							|  |  |  |  |   the template and component. | 
					
						
							| 
									
										
										
										
											2016-10-19 17:01:51 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   只需要对模板和组件做出一些修改,我们可以将逻辑和消息移到组件中。 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |    | 
					
						
							|  |  |  |  |   Here's the hero name again, excerpted from the revised template ("Template 2"), next to the original version: | 
					
						
							| 
									
										
										
										
											2016-10-19 17:01:51 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   下面也是关于英雄名字的控制器,从修改后的模板("Template 2")中抽取出来,与原来的版本相比: | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  | +makeTabs( | 
					
						
							|  |  |  |  |   `cb-form-validation/ts/app/template/hero-form-template2.component.html,  | 
					
						
							|  |  |  |  |    cb-form-validation/ts/app/template/hero-form-template1.component.html`, | 
					
						
							|  |  |  |  |   'name-with-error-msg, name-with-error-msg', | 
					
						
							|  |  |  |  |   `hero-form-template2.component.html (name #2), | 
					
						
							|  |  |  |  |    hero-form-template1.component.html (name #1)`) | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | :marked | 
					
						
							|  |  |  |  |   The `<input>` element HTML is almost the same. There are noteworthy differences: | 
					
						
							| 
									
										
										
										
											2016-10-19 17:01:51 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   `<input>`元素的HTML几乎一样。但是下列有值得注意的区别: | 
					
						
							|  |  |  |  |    | 
					
						
							|  |  |  |  |   - The hard-code error message `<div>` are gone. | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   - 硬编码的错误消息`<div>`消失了。 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   - There's a new attribute, `forbiddenName`, that is actually a custom validation directive. | 
					
						
							|  |  |  |  |   It invalidates the control if the user enters "bob" anywhere in the name ([try it](#live-example)). | 
					
						
							|  |  |  |  |   We discuss [custom validation directives](#custom-validation) later in this cookbook. | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-19 17:01:51 +01:00
										 |  |  |  |   - 添加了一个新属性`forbiddenName`,它实际上是一个自定义验证指令。 | 
					
						
							|  |  |  |  |   如果用户名字中的任何地方输入“bob”,该指令变将控制器标记为无效([试试](#live-example))。 | 
					
						
							|  |  |  |  |   我们在本烹饪书后面介绍了[自定义验证指令](#custom-validation)。 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |   - The `#name` template variable is gone because we no longer refer to the Angular control for this element. | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-19 17:01:51 +01:00
										 |  |  |  |   - 模板变量`#name`消失了,因为我们不再需要为这个元素引用Angular控制器。 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |   -  Binding to the new `formErrors.name` property is sufficent to display all name validation error messages. | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-19 17:01:51 +01:00
										 |  |  |  |   - 绑定到新的`formErrors.name`属性,就可以处理所有名字验证错误信息了。 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |   #### Component class | 
					
						
							| 
									
										
										
										
											2016-10-19 17:01:51 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   #### 组件类 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |   The original component code stays the same. | 
					
						
							|  |  |  |  |   We _added_ new code to acquire the Angular form control and compose error messages. | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-19 17:01:51 +01:00
										 |  |  |  |   原来的组件代码还是一样。我们**添加**了新的代码,来获取Angular表单控制器和撰写错误信息。 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |   The first step is to acquire the form control that Angular created from the template by querying for it. | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-19 17:01:51 +01:00
										 |  |  |  |   第一步是获取Angular通过查询模板而生成的表单控制器。 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |   Look back at the top of the component template where we set the  | 
					
						
							|  |  |  |  |   `#heroForm` template variable in the `<form>` element: | 
					
						
							| 
									
										
										
										
											2016-10-19 17:01:51 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   回头看组件模板顶部,我们在`<form>`元素中设置`#heroForm`模板变量: | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  | +makeExample('cb-form-validation/ts/app/template/hero-form-template1.component.html','form-tag','template/hero-form-template1.component.html (form tag)')(format='.') | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | :marked | 
					
						
							|  |  |  |  |   The `heroForm` variable is a reference to the control model that Angular derived from the template. | 
					
						
							|  |  |  |  |   We tell Angular to inject that model into the component class's `currentForm` property using a `@ViewChild` query: | 
					
						
							| 
									
										
										
										
											2016-10-19 17:01:51 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   `heroFrom`变量是Angular从模板衍生出来的控制模型的引用。 | 
					
						
							|  |  |  |  |   我们利用`@ViewChild`来告诉Angular注入这个模型到组件类的`currentForm`属性: | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  | +makeExample('cb-form-validation/ts/app/template/hero-form-template2.component.ts','view-child','template/hero-form-template2.component.ts (heroForm)')(format='.') | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | :marked | 
					
						
							|  |  |  |  |   Some observations: | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-19 17:01:51 +01:00
										 |  |  |  |   一些细节: | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |   - Angular `@ViewChild` queries for a template variable when you pass it  | 
					
						
							|  |  |  |  |   the name of that variable as a string (`'heroForm'` in this case). | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-19 17:01:51 +01:00
										 |  |  |  |   - Angular的`@ViewChild`使用传入的模板变量的字符串名字(这里是`'heroForm'`),来查询对应的模板变量。 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |   - The `heroForm` object changes several times during the life of the component, most notably when we add a new hero. | 
					
						
							|  |  |  |  |   We'll have to re-inspect it periodically. | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-19 17:01:51 +01:00
										 |  |  |  |   - `heroForm`对象在组件的生命周期内变化了好几次,最值得注意的是当我们添加一个新英雄时的变化。我们必须定期重新检测它。 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |   - Angular calls the `ngAfterViewChecked` [lifecycle hook method](../guide/lifecycle-hooks.html#afterview)  | 
					
						
							|  |  |  |  |   when anything changes in the view. | 
					
						
							|  |  |  |  |   That's the right time to see if there's a new `heroForm` object. | 
					
						
							| 
									
										
										
										
											2016-10-19 17:01:51 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   - 当视图有任何变化时,Angular调用`ngAfterViewChecked`[生命周期钩子方法](../guide/lifecycle-hooks.html#afterview)。这是查看是否有新`heroForm`对象的最佳时机。  | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |    | 
					
						
							|  |  |  |  |   - When there _is_ a new `heroForm` model, we subscribe to its `valueChanged` _Observable_ property. | 
					
						
							| 
									
										
										
										
											2016-10-19 17:01:51 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   - 当出现新`heroForm`模型时,我们订阅它的`valueChanged`**可观察**属性。 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |   The `onValueChanged` handler looks for validation errors after every user keystroke.   | 
					
						
							| 
									
										
										
										
											2016-10-19 17:01:51 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   `onValueChanged`处理器在每次用户键入后查找验证错误。 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  | +makeExample('cb-form-validation/ts/app/template/hero-form-template2.component.ts','handler','template/hero-form-template2.component.ts (handler)')(format='.') | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-01 11:54:30 -07:00
										 |  |  |  | :marked | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |   The `onValueChanged` handler interprets user data entry.  | 
					
						
							|  |  |  |  |   The `data` object passed into the handler contains the current element values. | 
					
						
							|  |  |  |  |   The handler ignores them. Instead, it iterates over the fields of the component's `formErrors` object. | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-19 17:01:51 +01:00
										 |  |  |  |   `onValueChanged`处理器拦截用户数据输入。 | 
					
						
							|  |  |  |  |   包含当前元素值得`data`对象被传入处理器。 | 
					
						
							|  |  |  |  |   处理器忽略它们。相反,它迭代组件的`formErrors`对象。 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |   The `formErrors` is a dictionary of the hero fields that have validation rules and their current error messages. | 
					
						
							|  |  |  |  |   Only two hero properties have validation rules, `name` and `power`. | 
					
						
							|  |  |  |  |   The messages are empty strings when the hero data are valid. | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-19 17:01:51 +01:00
										 |  |  |  |   `formErrors`是一个词典,包含了拥有验证规则和当前错误消息的英雄控件。 | 
					
						
							|  |  |  |  |   只有两个英雄属性有验证规则,`name`和`power`。 | 
					
						
							|  |  |  |  |   当英雄数据有效时,这些消息的值为空字符串。 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |   For each field, the handler | 
					
						
							| 
									
										
										
										
											2016-10-19 17:01:51 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   对于每个控件,这个处理器: | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |     - clears the prior error message if any | 
					
						
							| 
									
										
										
										
											2016-10-19 17:01:51 +01:00
										 |  |  |  |      | 
					
						
							|  |  |  |  |     - 如果有之前的错误信息,清楚它们 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     - acquires the field's corresponding Angular form control | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     - 获取控件对应的Angular表单控制器 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |     - if such a control exists _and_ its been changed ("dirty") _and_ its invalid ... | 
					
						
							| 
									
										
										
										
											2016-10-19 17:01:51 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     - 如果这样的控制器存在,并且它被更新(“dirty”)**以及**它无效... | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |     - the handler composes a consolidated error message for all of the control's errors. | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-19 17:01:51 +01:00
										 |  |  |  |     - 处理器便为所有控制器的错误合成一条错误消息。 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |   We'll need some error messages of course, a set for each validated property, one message per validation rule: | 
					
						
							| 
									
										
										
										
											2016-10-19 17:01:51 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   很显然,我们需要一些错误消息,每个验证的属性都需要一套,每个验证规则需要一条消息: | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  | +makeExample('cb-form-validation/ts/app/template/hero-form-template2.component.ts','messages','template/hero-form-template2.component.ts (messages)')(format='.') | 
					
						
							|  |  |  |  | :marked | 
					
						
							|  |  |  |  |   Now every time the user makes a change, the `onValueChanged` handler checks for validation errors and produces messages accordingly. | 
					
						
							| 
									
										
										
										
											2016-10-19 17:01:51 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   现在,每次用户作出变化时,`onValueChanged`处理器检查验证错误并按情况发出错误消息。 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |    | 
					
						
							|  |  |  |  |   ### Is this an improvement? | 
					
						
							| 
									
										
										
										
											2016-10-19 17:01:51 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   ### 这是增强吗? | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |    | 
					
						
							|  |  |  |  |   Clearly the template got substantially smaller while the component code got substantially larger.  | 
					
						
							|  |  |  |  |   It's not easy to see the benefit when there are just three fields and only two of them have validation rules. | 
					
						
							| 
									
										
										
										
											2016-06-01 11:54:30 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-19 17:01:51 +01:00
										 |  |  |  |   很显然,模板变得小多了,组件代码变得大多了。当只有三个控件并且其中只有两个有验证规则时,我们很难看出好处。 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |   Consider what happens as we increase the number of validated fields and rules. | 
					
						
							|  |  |  |  |   In general, HTML is harder to read and maintain than code.  | 
					
						
							|  |  |  |  |   The initial template was already large and threatening to get rapidly worse as we add more validation message `<divs>`. | 
					
						
							| 
									
										
										
										
											2016-10-19 17:01:51 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   假设增加需要验证的控件和规则后会怎么样。 | 
					
						
							|  |  |  |  |   通常,HTML比代码更难阅读和维护。 | 
					
						
							|  |  |  |  |   初始的模板已经很大了,如果我们添加更多验证消息`<div>`,它会迅速变得更大。 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |    | 
					
						
							|  |  |  |  |   After moving the validation messaging to the component,  | 
					
						
							|  |  |  |  |   the template grows more slowly and proportionally. | 
					
						
							|  |  |  |  |   Each field has approximately the same number of lines no matter its number of validation rules. | 
					
						
							|  |  |  |  |   The component also grows proportionally, at the rate of one line per validated field | 
					
						
							|  |  |  |  |   and one line per validation message. | 
					
						
							| 
									
										
										
										
											2016-10-19 17:01:51 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   将验证消息移到组件后,模板的增长变得更加缓慢,幅度也小一些。 | 
					
						
							|  |  |  |  |   不管有多少个验证规则,每个控件的行数是差不多的。 | 
					
						
							|  |  |  |  |   组件也按比例增长,每增加一个控件增加一行,每个验证消息一行。 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |    | 
					
						
							|  |  |  |  |   Both trends are manageable. | 
					
						
							| 
									
										
										
										
											2016-06-01 11:54:30 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-19 17:01:51 +01:00
										 |  |  |  |   两条线容易维护。 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |   Now that the messages are in code, we have more flexibility. We can compose messages more intelligently.  | 
					
						
							|  |  |  |  |   We can refactor the messages out of the component, perhaps to a service class that retrieves them from the server. | 
					
						
							|  |  |  |  |   In short, there are more opportunities to improve message handling now that text and logic have moved from template to code. | 
					
						
							| 
									
										
										
										
											2016-06-01 11:54:30 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-19 17:01:51 +01:00
										 |  |  |  |   现在消息在代码中,我们有更多的灵活度。我们更加智能的撰写消息。 | 
					
						
							|  |  |  |  |   我们可以将消息重构出组件,比如到一个服务类,从服务端获取消息。 | 
					
						
							|  |  |  |  |   简而言之,有很多机会增强消息处理,因为文本和逻辑都已经从模板移到代码中。 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |   ### _FormModule_ and template-driven forms | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-19 17:01:51 +01:00
										 |  |  |  |   ### _FormModule_ 和模板驱动表单 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |   Angular has two different forms modules — `FormsModule` and `ReactiveFormsModule` —  | 
					
						
							|  |  |  |  |   that correspond with the two approaches to form development. | 
					
						
							|  |  |  |  |   Both modules come from the same `@angular/forms` library package. | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-19 17:01:51 +01:00
										 |  |  |  |   Angular有两种不同的表单模块 - `FormsModule`和`ReactiveFormsModule` - 它们与表单开发的两种方法对应。 | 
					
						
							|  |  |  |  |   两种模块都从同一个`@angular/forms`库。 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |   We've been reviewing the "Template-driven" approach which requires the `FormsModule` | 
					
						
							|  |  |  |  |   Here's how we imported it in the `HeroFormTemplateModule`. | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-19 17:01:51 +01:00
										 |  |  |  |   我们一直在探讨**模板驱动**方法,它需要`FormsModule`。下面是如何在`HeroFormTemplateModule`中导入它: | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  | +makeExample('cb-form-validation/ts/app/template/hero-form-template.module.ts','','template/hero-form-template.module.ts')(format='.') | 
					
						
							|  |  |  |  | .l-sub-section | 
					
						
							| 
									
										
										
										
											2016-06-01 11:54:30 -07:00
										 |  |  |  |   :marked | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |     We haven't talked about the `SharedModule` or its `SubmittedComponent` which appears at the bottom of every | 
					
						
							|  |  |  |  |     form template in this cookbook.   | 
					
						
							| 
									
										
										
										
											2016-10-19 17:01:51 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     我们还没有讲`SharedModule`或者它的`SubmittedComponent`,它们出现在本烹饪书的每一个表单模板中。 | 
					
						
							| 
									
										
										
										
											2016-06-01 11:54:30 -07:00
										 |  |  |  |      | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |     They're not germane to the validation story. Look at the [live example](#live-example) if you're interested. | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-19 17:01:51 +01:00
										 |  |  |  |     它们与表单验证没有紧密的关系。如果你感兴趣,参见[在线例子](#live-example)。 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  | .l-main-section | 
					
						
							|  |  |  |  | a#reactive | 
					
						
							|  |  |  |  | :marked | 
					
						
							|  |  |  |  |   ## Reactive Forms | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-19 17:01:51 +01:00
										 |  |  |  |   ## 响应式表单 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |   In the template-driven approach, you markup the template with form elements, validation attributes,  | 
					
						
							|  |  |  |  |   and `ng...` directives from the Angular `FormsModule`. | 
					
						
							|  |  |  |  |   At runtime, Angular interprets the template and derives its _form control model_. | 
					
						
							| 
									
										
										
										
											2016-10-19 17:01:51 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   在模板驱动方法中,你在模板中标出表单元素、验证属性和Angular`FormsModule`中的`ng...`指令。 | 
					
						
							|  |  |  |  |   在运行时间,Angular解释模板并从**表单控制器模型**衍生它。 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |    | 
					
						
							|  |  |  |  |   **Reactive Forms** takes a different approach.  | 
					
						
							|  |  |  |  |   You create the form control model in code. You write the template with form elements | 
					
						
							|  |  |  |  |   and`form...` directives from the Angular `ReactiveFormsModule`. | 
					
						
							|  |  |  |  |   At runtime, Angular binds the template elements to your control model based on your instructions. | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-19 17:01:51 +01:00
										 |  |  |  |   **响应式表单**采用不同的方法。 | 
					
						
							|  |  |  |  |   你在代码中创建表单控制器模型,并用表单元素和来自Angular `ReactiveFormsModule`中的`form...`指令来编写模板。 | 
					
						
							|  |  |  |  |   在运行时间,Angular根据你的指示绑定模板元素到你的控制器模型。 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |   This approach requires a bit more effort. *You have to write the control model and manage it*. | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-19 17:01:51 +01:00
										 |  |  |  |   这个方法需要做一些额外的工作。*你必须编写并管理控制器模型** | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |   In return, you can | 
					
						
							| 
									
										
										
										
											2016-10-19 17:01:51 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   作为回报,你可以: | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |   * add, change, and remove validation functions on the fly | 
					
						
							| 
									
										
										
										
											2016-10-19 17:01:51 +01:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-19 20:52:28 +01:00
										 |  |  |  |   * 随时添加、修改和删除验证函数 | 
					
						
							| 
									
										
										
										
											2016-10-19 17:01:51 +01:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |   * manipulate the control model dynamically from within the component | 
					
						
							| 
									
										
										
										
											2016-10-19 17:01:51 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   * 在组件内动态操纵控制器模型 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |   * [test](#testing) validation and control logic with isolated unit tests. | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-19 17:01:51 +01:00
										 |  |  |  |   * 使用孤立单元测试来[测试](#testing)验证和控制器逻辑 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |   The third cookbook sample re-writes the hero form in _reactive forms_ style. | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-19 17:01:51 +01:00
										 |  |  |  |   第三个烹饪书例子用**响应式表单**风格重新编写英雄表格。 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |   ### Switch to the _ReactiveFormsModule_ | 
					
						
							| 
									
										
										
										
											2016-10-19 17:01:51 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   ### 切换到_ReactiveFormsModule_ | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |   The reactive forms classes and directives come from the Angular `ReactiveFormsModule`, not the `FormsModule`. | 
					
						
							|  |  |  |  |   The application module for the "Reactive Forms" feature in this sample looks like this: | 
					
						
							| 
									
										
										
										
											2016-10-19 17:01:51 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   响应式表单类和指令来自于Angular的`ReactiveFormsModule`,不是`FormsModule`。 | 
					
						
							|  |  |  |  |   本例中,应用模块的“响应式表单”特性是这样的: | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  | +makeExample('cb-form-validation/ts/app/reactive/hero-form-reactive.module.ts','','app/reactive/hero-form-reactive.module.ts')(format='.') | 
					
						
							|  |  |  |  | :marked | 
					
						
							|  |  |  |  |   The "Reactive Forms" feature module and component are in the `app/reactive` folder.  | 
					
						
							|  |  |  |  |   Let's focus on the `HeroFormReactiveComponent` there, starting with its template. | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-19 17:01:51 +01:00
										 |  |  |  |   “响应式表单”特性模块和组件在`app/reactive`目录。 | 
					
						
							|  |  |  |  |   让我们关注那里的`HeroFormReactiveComponent`,先看它的模板。 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |   ### Component template | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-04 12:41:10 +00:00
										 |  |  |  |   ### 组件模板 | 
					
						
							| 
									
										
										
										
											2016-10-19 17:01:51 +01:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |   We begin by changing the `<form>` tag so that it binds the Angular `formGroup` directive in the template | 
					
						
							|  |  |  |  |   to the `heroForm` property in the component class.  | 
					
						
							|  |  |  |  |   The `heroForm` is the control model that the component class builds and maintains. | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-19 17:01:51 +01:00
										 |  |  |  |   我们先修改`<form>`标签,让Angular的`formGroup`指令绑定到组件类的`heroForm`属性。 | 
					
						
							|  |  |  |  |   `heroForm`是组件类创建和维护的控制器模型。 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  | +makeExample('cb-form-validation/ts/app/reactive/hero-form-reactive.component.html','form-tag')(format='.') | 
					
						
							|  |  |  |  | :marked | 
					
						
							|  |  |  |  |   Then we modify the template HTML elements to match the _reactive forms_ style. | 
					
						
							|  |  |  |  |   Here is the "name" portion of the template again, revised for reactive forms and compared with the template-driven version: | 
					
						
							| 
									
										
										
										
											2016-10-19 20:52:28 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   接下来,我们修改模板HTML元素,来匹配**响应式表单**样式。 | 
					
						
							|  |  |  |  |   下面又是“name”部分的模板,响应式表单修改版本和模板驱动版本的比较: | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  | +makeTabs( | 
					
						
							|  |  |  |  |   `cb-form-validation/ts/app/reactive/hero-form-reactive.component.html,  | 
					
						
							|  |  |  |  |    cb-form-validation/ts/app/template/hero-form-template2.component.html`, | 
					
						
							|  |  |  |  |   'name-with-error-msg, name-with-error-msg', | 
					
						
							|  |  |  |  |   `hero-form-reactive.component.html (name #3), | 
					
						
							|  |  |  |  |    hero-form-template1.component.html (name #2)`) | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | :marked | 
					
						
							|  |  |  |  |   Key changes: | 
					
						
							| 
									
										
										
										
											2016-10-19 20:52:28 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   关键变化: | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |   - the validation attributes are gone (except `required`) because we'll be validating in code. | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-19 20:52:28 +01:00
										 |  |  |  |   - 验证属性没有了(除了`required`),因为我们将在代码中验证。 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |   - `required` remains, not for validation purposes (we'll cover that in the code),  | 
					
						
							|  |  |  |  |   but rather for css styling and accessibility. | 
					
						
							| 
									
										
										
										
											2016-06-01 11:54:30 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-19 20:52:28 +01:00
										 |  |  |  |   - 保留了`required`,不是为了验证目的(我们将在代码中再行解释),而是为了CSS样式和可访问性。 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-01 11:54:30 -07:00
										 |  |  |  | .l-sub-section | 
					
						
							|  |  |  |  |   :marked | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |     A future version of reactive forms will add the `required` HTML validation attribute to the DOM element | 
					
						
							| 
									
										
										
										
											2016-10-19 20:52:28 +01:00
										 |  |  |  |     (and perhaps the `aria-required` attribute) when the control has the `required` validator function. | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     未来版本的响应式表单将会在控制器有`required`验证器函数时,添加`required` HTML验证属性到DOM元素(也可能添加`aria-required`属性)。  | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     Until then, apply the `required` attribute _and_ add the `Validator.required` function | 
					
						
							|  |  |  |  |     to the control model, as we'll do below. | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-19 20:52:28 +01:00
										 |  |  |  |     在此之前,添加`required`属性**以及**添加`Validator.required`函数到控制器模型,像我们下面这样做: | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-01 11:54:30 -07:00
										 |  |  |  | :marked | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |   - the `formControlName` replaces the `name` attribute; it serves the same | 
					
						
							|  |  |  |  |   purpose of correlating the input box with the Angular form control. | 
					
						
							| 
									
										
										
										
											2016-06-01 11:54:30 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-19 20:52:28 +01:00
										 |  |  |  |   - `formControlName`替换了`name`属性;它起到了关联输入框和Angular表单控制器的同样作用。 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |   - the two-way `[(ngModel)]` binding is gone.  | 
					
						
							|  |  |  |  |   The reactive approach does not use data binding to move data into and out of the form controls. | 
					
						
							|  |  |  |  |   We do that in code. | 
					
						
							| 
									
										
										
										
											2016-06-01 11:54:30 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-19 20:52:28 +01:00
										 |  |  |  |   - 双向`[(ngModel)]`绑定消失了。 | 
					
						
							|  |  |  |  |   响应式表单方法不使用数据绑定从表单控制器移入和移出数据。我们在代码中做这些。 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  | .l-sub-section | 
					
						
							|  |  |  |  |   :marked | 
					
						
							|  |  |  |  |     The retreat from data binding is a principle of the reactive paradigm rather than a technical limitation. | 
					
						
							| 
									
										
										
										
											2016-10-19 20:52:28 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     不适用表单数据绑定是响应式模式的原则,而非技术局限。 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  | :marked | 
					
						
							|  |  |  |  |   ### Component class | 
					
						
							| 
									
										
										
										
											2016-06-01 11:54:30 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-19 20:52:28 +01:00
										 |  |  |  |   ### 组件类 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   The component class is now responsible for defining and managing the form control model. | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   组件类现在负责定义和管理表单控制器模型。  | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |    | 
					
						
							|  |  |  |  |   Angular no longer derives the control model from the template so we can no longer query for it. | 
					
						
							|  |  |  |  |   We create the Angular form control model explicitly with the help of the `FormBuilder`. | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-19 20:52:28 +01:00
										 |  |  |  |   Angular不再从模板衍生控制器模型,所以我们不能再查询它。 | 
					
						
							|  |  |  |  |   我们利用`FormBuilder`来显式创建Angular表单控制器模型。 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |   Here's the section of code devoted to that process, paired with the template-driven code it replaces: | 
					
						
							| 
									
										
										
										
											2016-10-19 20:52:28 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   下面是负责该进程的代码部分,与被它取代的模板驱动代码相比: | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  | +makeTabs( | 
					
						
							|  |  |  |  |   `cb-form-validation/ts/app/reactive/hero-form-reactive.component.ts, | 
					
						
							|  |  |  |  |    cb-form-validation/ts/app/template/hero-form-template2.component.ts`, | 
					
						
							|  |  |  |  |   'form-builder, view-child', | 
					
						
							|  |  |  |  |   `reactive/hero-form-reactive.component.ts (FormBuilder), | 
					
						
							|  |  |  |  |    template/hero-form-template2.component.ts (ViewChild)`) | 
					
						
							|  |  |  |  | :marked | 
					
						
							|  |  |  |  |   - we inject the `FormBuilder` in a constructor. | 
					
						
							| 
									
										
										
										
											2016-06-01 11:54:30 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-19 20:52:28 +01:00
										 |  |  |  |   - 我们注入`FormBuilder`到构造函数中。 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |   - we call a `buildForm` method in the `ngOnInit` [lifecycle hook method](../guide/lifecycle-hooks.html#hooks-overview) | 
					
						
							|  |  |  |  |   because that's when we'll have the hero data. We'll call it again in the `addHero` method. | 
					
						
							| 
									
										
										
										
											2016-10-19 20:52:28 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   - 我们在`ngOnInit`[生命周期钩子方法](../guide/lifecycle-hooks.html#hooks-overview)中调用`buildForm`方法, | 
					
						
							|  |  |  |  |   因为这正是我们拥有英雄数据的时刻。我们将在`addHero`方法中再次调用它。 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  | .l-sub-section | 
					
						
							|  |  |  |  |   :marked | 
					
						
							|  |  |  |  |     A real app would retrieve the hero asynchronously from a data service, a task best performed in the `ngOnInit` hook. | 
					
						
							| 
									
										
										
										
											2016-10-19 20:52:28 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     真实的应用很可能从数据服务异步获取英雄,这个任务最好在`ngOnInit`生命周期钩子中进行。 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  | :marked | 
					
						
							|  |  |  |  |   - the `buildForm` method uses the `FormBuilder` (`fb`) to declare the form control model. | 
					
						
							| 
									
										
										
										
											2016-08-31 18:15:14 -07:00
										 |  |  |  |   Then it attaches the same `onValueChanged` handler (there's a one line difference)  | 
					
						
							|  |  |  |  |   to the form's `valueChanged` event and calls it immediately  | 
					
						
							|  |  |  |  |   to set error messages for the new control model. | 
					
						
							| 
									
										
										
										
											2016-06-01 11:54:30 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-19 20:52:28 +01:00
										 |  |  |  |   - `buildForm`方法使用`FormBuilder`(`fb`)来声明表单控制器模型。 | 
					
						
							|  |  |  |  |   然后它将相同的`onValueChanged`(有一行代码不一样)处理器附加到表单的`valueChanged`事件, | 
					
						
							|  |  |  |  |   并立刻为新的控制器模型设置错误消息。 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-01 11:54:30 -07:00
										 |  |  |  | :marked | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |   #### _FormBuilder_ declaration | 
					
						
							| 
									
										
										
										
											2016-10-19 20:52:28 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   #### _FormBuilder_声明 | 
					
						
							|  |  |  |  |   | 
					
						
							|  |  |  |  |   The `FormBuilder` declaration object specifies the three controls of the sample's hero form. | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   `FormBuilder`声明对象指定了本例英雄表单的三个控制器。  | 
					
						
							| 
									
										
										
										
											2016-06-01 11:54:30 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |   Each control spec is a control name with an array value.  | 
					
						
							|  |  |  |  |   The first array element is the current value of the corresponding hero field. | 
					
						
							|  |  |  |  |   The (optional) second value is a validator function or an array of validator functions. | 
					
						
							| 
									
										
										
										
											2016-10-19 20:52:28 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   每个控制器的设置都是控制器名字和数组值。 | 
					
						
							|  |  |  |  |   第一个数组元素是英雄控件对应的当前值。 | 
					
						
							|  |  |  |  |   第二个值(可选)是验证器函数或者验证器函数数组。 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |    | 
					
						
							|  |  |  |  |   Most of the validator functions are stock validators provided by Angular as static methods of the `Validators` class. | 
					
						
							|  |  |  |  |   Angular has stock validators that correspond to the standard HTML validation attributes. | 
					
						
							| 
									
										
										
										
											2016-06-01 11:54:30 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-19 20:52:28 +01:00
										 |  |  |  |   大多数验证器函数是Angular以`Validators`类的静态方法的形式提供的原装验证器。 | 
					
						
							|  |  |  |  |   Angular有一些原装验证器,与标准HTML验证属性一一对应。 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |   The `forbiddenNames` validator on the `"name"` control is a custom validator,  | 
					
						
							|  |  |  |  |   discussed in a separate [section below](#custom-validation). | 
					
						
							| 
									
										
										
										
											2016-06-01 11:54:30 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-19 20:52:28 +01:00
										 |  |  |  |   `"name"`控制器上的`forbiddenNames`验证器是自定义验证器,在下面单独的[小结](#custom-validation)有所讨论。 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  | .l-sub-section   | 
					
						
							|  |  |  |  |   :marked | 
					
						
							|  |  |  |  |     Learn more about `FormBuilder` in a _forthcoming_ chapter on reactive forms.  | 
					
						
							| 
									
										
										
										
											2016-06-01 11:54:30 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-19 20:52:28 +01:00
										 |  |  |  |     到**即将到来**的响应式表单章,学习更多关于`FormBuilder`的知识。 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  | :marked | 
					
						
							|  |  |  |  |   #### Committing hero value changes | 
					
						
							| 
									
										
										
										
											2016-10-19 20:52:28 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   #### 提交英雄值的更新 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |    | 
					
						
							|  |  |  |  |   In two-way data binding, the user's changes flow automatically from the controls back to the data model properties. | 
					
						
							|  |  |  |  |   Reactive forms do not use data binding to update data model properties.  | 
					
						
							|  |  |  |  |   The developer decides _when and how_ to update the data model from control values. | 
					
						
							| 
									
										
										
										
											2016-06-01 11:54:30 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-19 20:52:28 +01:00
										 |  |  |  |   在双向数据绑定时,用户的修改自动从控制器流向数据模型属性。 | 
					
						
							|  |  |  |  |   响应式表单不适用数据绑定来更新数据模型属性。 | 
					
						
							|  |  |  |  |   开发者决定**何时**与**如何**从控制器的值更新数据模型。 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |   This sample updates the model twice: | 
					
						
							| 
									
										
										
										
											2016-10-19 20:52:28 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   本例更新模型两次: | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |   1. when the user submits the form | 
					
						
							| 
									
										
										
										
											2016-10-19 20:52:28 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   1. 当用户提交标单时 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |   1. when the user chooses to add a new hero | 
					
						
							| 
									
										
										
										
											2016-06-01 11:54:30 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-19 20:52:28 +01:00
										 |  |  |  |   1. 当用户选择添加新英雄 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |   The `onSubmit` method simply replaces the `hero` object with the combined values of the form: | 
					
						
							| 
									
										
										
										
											2016-10-19 20:52:28 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   `onSubmit`方法直接使用表单的值得合集来替换`hero`对象: | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  | +makeExample('cb-form-validation/ts/app/reactive/hero-form-reactive.component.ts','on-submit')(format='.') | 
					
						
							|  |  |  |  | .l-sub-section | 
					
						
							|  |  |  |  |   :marked | 
					
						
							|  |  |  |  |     This example is "lucky" in that the `heroForm.value` properties _just happen_ to | 
					
						
							|  |  |  |  |     correspond _exactly_ to the hero data object properties. | 
					
						
							| 
									
										
										
										
											2016-10-19 20:52:28 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     本例非常“幸运”,因为`heroForm.value`属性**正好**与英雄数据对象属性对应。 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  | :marked | 
					
						
							|  |  |  |  |   The `addHero` method discards pending changes and creates a brand new `hero` model object. | 
					
						
							| 
									
										
										
										
											2016-10-19 20:52:28 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   `addHero`方法放弃未处理的变化,并创建一个崭新的`hero`模型对象。 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  | +makeExample('cb-form-validation/ts/app/reactive/hero-form-reactive.component.ts','add-hero')(format='.') | 
					
						
							|  |  |  |  | :marked | 
					
						
							|  |  |  |  |   Then it calls `buildForm` again which replaces the previous `heroForm` control model with a new one. | 
					
						
							|  |  |  |  |   The `<form>` tag's `[formGroup]` binding refreshes the page with the new control model. | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-19 20:52:28 +01:00
										 |  |  |  |   然后它再次调用`buildForm`,用一个新对象替换了之前的`heroForm`控制器模型。 | 
					
						
							|  |  |  |  |   `<form>`标签的`[formGroup]`绑定使用这个新的控制器模型更新页面。 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |   Here's the complete reactive component file, compared to the two template-driven component files. | 
					
						
							| 
									
										
										
										
											2016-10-19 20:52:28 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   下面是完整的响应式表单的组件文件,与两个模板驱动组件文件对比: | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  | +makeTabs( | 
					
						
							|  |  |  |  |   `cb-form-validation/ts/app/reactive/hero-form-reactive.component.ts, | 
					
						
							|  |  |  |  |    cb-form-validation/ts/app/template/hero-form-template2.component.ts,  | 
					
						
							|  |  |  |  |    cb-form-validation/ts/app/template/hero-form-template1.component.ts`, | 
					
						
							|  |  |  |  |   '', | 
					
						
							|  |  |  |  |   `reactive/hero-form-reactive.component.ts (#3), | 
					
						
							|  |  |  |  |    template/hero-form-template2.component.ts (#2), | 
					
						
							|  |  |  |  |    template/hero-form-template1.component.ts (#1)`) | 
					
						
							| 
									
										
										
										
											2016-06-01 11:54:30 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  | .l-sub-section | 
					
						
							|  |  |  |  |   :marked | 
					
						
							|  |  |  |  |     Run the [live example](#live-example) to see how the reactive form behaves | 
					
						
							|  |  |  |  |     and to compare all of the files in this cookbook sample. | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-19 20:52:28 +01:00
										 |  |  |  |     运行[在线例子](#live-example),查看响应式表单是的行为,并与本章中的例子文件作比较。 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  | .l-main-section | 
					
						
							|  |  |  |  | a#custom-validation | 
					
						
							|  |  |  |  | :marked | 
					
						
							|  |  |  |  |   ## Custom validation | 
					
						
							| 
									
										
										
										
											2016-10-19 20:52:28 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   ## 自定义验证 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |   This cookbook sample has a custom `forbiddenNamevalidator` function that's applied to both the  | 
					
						
							|  |  |  |  |   template-driven and the reactive form controls. It's in the `app/shared` folder | 
					
						
							|  |  |  |  |   and declared in the `SharedModule`. | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-19 20:52:28 +01:00
										 |  |  |  |   本烹饪书例子有一个自定义`forbiddenNameValidator`函数,在模板驱动和响应式表单中都有使用。 | 
					
						
							|  |  |  |  |   它在`app/shared`目录,在`SharedModule`中被声明。 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |   Here's the `forbiddenNamevalidator` function itself: | 
					
						
							| 
									
										
										
										
											2016-10-19 20:52:28 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   下面是`forbiddenNameValidator`函数: | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  | +makeExample('cb-form-validation/ts/app/shared/forbidden-name.directive.ts','custom-validator', 'shared/forbidden-name.directive.ts (forbiddenNameValidator)')(format='.') | 
					
						
							|  |  |  |  | :marked | 
					
						
							|  |  |  |  |   The function is actually a factory that takes a regular expression to detect a _specific_ forbidden name | 
					
						
							|  |  |  |  |   and returns a validator function. | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-19 20:52:28 +01:00
										 |  |  |  |   该函数其实是一个工厂函数,接受一个正则表达式,用来检测**指定**的禁止的名字,并返回验证器函数。 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |   In this sample, the forbidden name is "bob";  | 
					
						
							|  |  |  |  |   the validator rejects any hero name containing "bob". | 
					
						
							|  |  |  |  |   Elsewhere it could reject "alice" or any name that the configuring regular expression matches. | 
					
						
							| 
									
										
										
										
											2016-10-19 20:52:28 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   在本例中,禁止的名字是"bob"; | 
					
						
							|  |  |  |  |   验证器拒绝任何带有"bob"的英雄名字。 | 
					
						
							|  |  |  |  |   在其他地方,只要配置的正则表达式可以匹配上,它可能拒绝"alice"或者任何其他名字。 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |    | 
					
						
							|  |  |  |  |   The `forbiddenNamevalidator` factory returns the configured validator function. | 
					
						
							|  |  |  |  |   That function takes an Angular control object and returns _either_ | 
					
						
							|  |  |  |  |   null if the control value is valid _or_ a validation error object. | 
					
						
							|  |  |  |  |   The validation error object typically has a property whose name is the validation key ('forbiddenName') | 
					
						
							|  |  |  |  |   and whose value is an arbitrary dictionary of values that we could insert into an error message (`{name}`). | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-19 20:52:28 +01:00
										 |  |  |  |   `forbiddenNameValidator`工厂函数返回配置好的验证器函数。 | 
					
						
							|  |  |  |  |   该函数接受一个Angular控制器对象,并在控制器值有效时返回null,或无效时返回验证错误对象。 | 
					
						
							|  |  |  |  |   验证错误对象通常有一个名为验证秘钥(`forbiddenName`)的属性。其值为一个任意词典,我们可以用来插入错误信息(`{name}`)。 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  | .l-sub-section | 
					
						
							|  |  |  |  |   :marked | 
					
						
							|  |  |  |  |     Learn more about validator functions in a _forthcoming_ chapter on custom form validation. | 
					
						
							| 
									
										
										
										
											2016-10-19 20:52:28 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     在**即将到来**的自定义表单验证章节,学习更多关于验证器函数的知识。 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  | :marked | 
					
						
							|  |  |  |  |   #### Custom validation directive | 
					
						
							| 
									
										
										
										
											2016-10-19 20:52:28 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   #### 自定义验证指令 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |   In the reactive forms component we added a configured `forbiddenNamevalidator` | 
					
						
							|  |  |  |  |   to the bottom of the `'name'` control's validator function list. | 
					
						
							| 
									
										
										
										
											2016-10-19 20:52:28 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   在响应式表单组件中,我们在`'name'`控制器的验证函数列表的底部添加了一个配置了的`forbiddenNameValidator`。 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  | +makeExample('cb-form-validation/ts/app/reactive/hero-form-reactive.component.ts','name-validators', 'reactive/hero-form-reactive.component.ts (name validators)')(format='.') | 
					
						
							|  |  |  |  | :marked | 
					
						
							|  |  |  |  |   In the template-driven component template, we add the selector (`forbiddenName`) of a custom _attribute directive_ to the name's input box | 
					
						
							|  |  |  |  |   and configured it to reject "bob". | 
					
						
							| 
									
										
										
										
											2016-10-19 20:52:28 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   在模板驱动组件的模板中,我们在name的输入框元素中添加了自定义**属性指令**的选择器(`forbiddenName`),并配置它来拒绝“bob”。 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  | +makeExample('cb-form-validation/ts/app/template/hero-form-template2.component.html','name-input', 'template/hero-form-template2.component.html (name input)')(format='.') | 
					
						
							|  |  |  |  | :marked | 
					
						
							|  |  |  |  |   The corresponding `ForbiddenValidatorDirective` is a wrapper around the `forbiddenNamevalidator`. | 
					
						
							| 
									
										
										
										
											2016-10-19 20:52:28 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   对应的`ForbiddenValidatorDirective`包装了`forbiddenNamevalidator`。 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |    | 
					
						
							|  |  |  |  |   Angular forms recognizes the directive's role in the validation process because the directive registers itself | 
					
						
							|  |  |  |  |   with the `NG_VALIDATORS` provider, a provider with an extensible collection of validation directives. | 
					
						
							| 
									
										
										
										
											2016-10-19 20:52:28 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   Angular表单接受指令在验证流程中的作用,因为指令注册自己到`NG_VALIDATORS`提供商中,该提供商拥有可扩展的验证指令集。 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  | +makeExample('cb-form-validation/ts/app/shared/forbidden-name.directive.ts','directive-providers', 'shared/forbidden-name.directive.ts (providers)')(format='.') | 
					
						
							|  |  |  |  | :marked | 
					
						
							|  |  |  |  |   The rest of the directive is unremarkable and we present it here without further comment. | 
					
						
							| 
									
										
										
										
											2016-10-19 20:52:28 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   指令的剩余部分没有什么特殊的,所以我们将它展示在下面,不作任何注解。 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  | +makeExample('cb-form-validation/ts/app/shared/forbidden-name.directive.ts','directive', 'shared/forbidden-name.directive.ts (directive)') | 
					
						
							|  |  |  |  | :marked | 
					
						
							|  |  |  |  | .l-sub-section | 
					
						
							|  |  |  |  |   :marked | 
					
						
							|  |  |  |  |     See the [Attribute Directives](../guide/attribute-directives.html) chapter. | 
					
						
							| 
									
										
										
										
											2016-06-01 11:54:30 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-19 20:52:28 +01:00
										 |  |  |  |     参见[属性型指令](../guide/attribute-directives.html)章节。 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  | .l-main-section | 
					
						
							|  |  |  |  | a#testing | 
					
						
							| 
									
										
										
										
											2016-06-01 11:54:30 -07:00
										 |  |  |  | :marked | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |   ## Testing Considerations | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-19 20:52:28 +01:00
										 |  |  |  |   ## 测试注意事项 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |   We can write _isolated unit tests_ of validation and control logic in _Reactive Forms_. | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-19 20:52:28 +01:00
										 |  |  |  |   我们可以为**响应式表单**的验证器和控制器逻辑编写**孤立单元测试**。 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |   _Isolated unit tests_ probe the component class directly, independent of its | 
					
						
							|  |  |  |  |   interactions with its template, the DOM, other dependencies, or Angular itself. | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-19 20:52:28 +01:00
										 |  |  |  |   **孤立单元测试**直接检测组件类,与组件和它的模板的交互、DOM、其他以来和Angular本省都无关。 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |   Such tests have minimal setup, are quick to write, and easy to maintain. | 
					
						
							|  |  |  |  |   They do not require the `Angular TestBed` or asynchronous testing practices. | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-19 20:52:28 +01:00
										 |  |  |  |   这样的测试具有简单设置#,快速编写和容易维护的特征。它们不需要`Angular TestBed`或异步测试工序。 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |   That's not possible with _Template-driven_ forms. | 
					
						
							|  |  |  |  |   The template-driven approach relies on Angular to produce the control model and  | 
					
						
							|  |  |  |  |   to derive validation rules from the HTML validation attributes. | 
					
						
							|  |  |  |  |   You must use the `Angular TestBed` to create component test instances, | 
					
						
							|  |  |  |  |   write asynchronous tests, and interact with the DOM. | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-19 20:52:28 +01:00
										 |  |  |  |   这对**模板驱动**表单来说是不可能的。 | 
					
						
							|  |  |  |  |   模板驱动方法依靠Angular来生成控制器模型并从HTML验证属性中衍生验证规则。 | 
					
						
							|  |  |  |  |   你必须使用`Angular TestBed`来创建组件测试实例,编写异步测试并与DOM交互。 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 17:07:13 -07:00
										 |  |  |  |   While not difficult, this takes more time, work and skill —  | 
					
						
							|  |  |  |  |   factors that tend to diminish test code coverage and quality. | 
					
						
							| 
									
										
										
										
											2016-10-19 20:52:28 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   虽然这种测试并不困难,但是它需要更多时间、工作和能力 - 这些因素往往会降低测试代码覆盖率和测试质量。 |