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