review of forms.jade in guide.

This commit is contained in:
Rex Ye 2016-05-17 15:42:22 +01:00
parent 170d037722
commit 68392d3fb0
1 changed files with 128 additions and 24 deletions

View File

@ -13,7 +13,7 @@ include ../_util-fns
user efficiently and effectively through the workflow behind the form.
不管什么样的Web开发者都能使用正确的标签“捏”出一个HTML。
但要想做出一个优秀的表单,让它具有贴心的数据输入体验,以指导用户明晰、高效的通表单背后的工作流,这个挑战就大多了。
但要想做出一个优秀的表单,让它具有贴心的数据输入体验,以指导用户明晰、高效的通表单完成背后的工作流,这个挑战就大多了。
*That* takes design skills that are, to be frank, well out of scope for this chapter.
@ -28,24 +28,30 @@ include ../_util-fns
We will build a simple form from scratch, one step at a time. Along the way we'll learn
我们将构建一个简单的表单,我们把它简化到只需要一次一步。通过这种方式,我们将学到:
我们将从零构建一个简单的表单,把它简化到一次一步。通过这种方式,我们将学到:
- to build an Angular form with a component and template
- 使用组件和模板构建一个Angular表单
- two-way data binding with `[(ngModel)]` syntax for reading and writing values to input controls
- 使用`[(ngModel)]`语法实现双向数据绑定,以便从输入控件中读取和写入值
- 使用`[(ngModel)]`语法实现双向数据绑定,以便输入控件的值读取和写入
- using `ngControl` to track the change state and validity of form controls
- 使用`ngControl`来跟踪变更状态并对表单控件做验证
- the special CSS classes that `ngControl` adds to form controls and how we can use them to provide strong visual feedback
- `ngControl`添加到表单控件上的那些特殊的CSS类以及我们该如何使用它们来提供强烈的视觉反馈
- displaying validation errors to users and enable/disable form controls
- 向用户显示有效性验证的错误提示,以及禁用/使能表单控件
- sharing information among controls with template reference variables
- 通过模板引用变量,在控件之间共享信息
[Live Example](/resources/live-examples/forms/ts/plnkr.html)
@ -54,17 +60,20 @@ include ../_util-fns
.l-main-section
:marked
## Template-Driven Forms
## 模板驱动的表单
Many of us will build forms by writing templates in the Angular [template syntax](./template-syntax.html) with
the form-specific directives and techniques described in this chapter.
大多数人都可以使用表单特有的指令和本章所描述的技术在模板中按照Angular[模板语法](./template-syntax.html)来构建表单。
我们大多数都可以使用表单特有的指令和本章所描述的技术在模板中按照Angular[模板语法](./template-syntax.html)来构建表单。
.l-sub-section
:marked
That's not the only way to create a form but it's the way we'll cover in this chapter.
这不是创建表单的唯一方式,但它是我们将在本章中使用的方式。
:marked
We can build almost any form we need with an Angular template — login forms, contact forms ... pretty much any business forms.
We can lay out the controls creatively, bind them to data, specify validation rules and display validation errors,
@ -89,7 +98,7 @@ figure.image-display
Here at the *Hero Employment Agency* we use this form to maintain personal information about the
heroes in our stable. Every hero needs a job. It's our company mission to match the right hero with the right crisis!
这里是*英雄职介中心*,我们使用这个表单来维护我们候选英雄们的个人信息。每个英雄都需要一份工作。我们公司的任务就是让正确的英雄去解决他/她所擅长的危机!
这里是*英雄职介中心*,我们使用这个表单来维护我们候选英雄们的个人信息。每个英雄都需要一份工作。我们公司的任务就是让正确的英雄去解决他/她所擅长对付的危机!
Two of the three fields on this form are required. Required fields have a green bar on the left to make them easy to spot.
@ -118,40 +127,60 @@ figure.image-display
我们将按照一系列很小的步骤来构建此表单:
1. Create the `Hero` model class
1. 创建`Hero`模型类
1. Create the component that controls the form
1. 创建控制此表单的组件
1. Create a template with the initial form layout
1. 创建具有初始表单布局的模板
1. Bind data properties to each form input control with the `ngModel` two-way data binding syntax
1. 使用`ngModel`双向数据绑定语法把数据属性绑定到每个表单输入控件
1. Add the **ngControl** directive to each form input control
1. 往每个表单输入控件上添加**ngControl**指令
1. Add custom CSS to provide visual feedback
1. 添加自定义CSS来显示视觉反馈
1. 添加自定义CSS来提供视觉反馈
1. Show and hide validation error messages
1. 显示和隐藏有效性验证的错误信息
1. Handle form submission with **ngSubmit**
1. 使用**ngSubmit**处理表单提交
1. Disable the forms submit button until the form is valid
1. 禁用此表单的提交按钮,直到表单变为有效的
:marked
## Setup
## 起步
Create a new project folder (`angular2-forms`) and follow the steps in the [QuickStart](../quickstart.html).
创建一个新的项目文件夹(`angular2-forms`),并且遵循[QuickStart](../quickstart.html)中的步骤进行初始化
创建一个新的项目文件夹(`angular2-forms`),并且完成[快速开始](../quickstart.html)中的步骤
include ../_quickstart_repo
:marked
## Create the Hero Model Class
## 创建一个Hero模型类
As users enter form data, we capture their changes and update an instance of a model.
We can't layout the form until we know what the model looks like.
当用户输入表单数据时,我们要捕获他们的更改,并更新到模型的一个实例中。
当用户输入表单数据时,我们要捕获他们的变化,并更新到模型的一个实例中。
除非我们知道模型里有什么,否则没法设计表单。
A model can be as simple as a "property bag" that holds facts about a thing of application importance.
@ -193,6 +222,7 @@ code-example(format="").
.l-main-section
:marked
## Create a Form component
## 创建一个表单组件
An Angular form has two parts: an HTML-based template and a code-based Component to handle data and user interactions.
@ -219,25 +249,30 @@ code-example(format="").
要理解这个组件,只会用到前面章节中已经学过的那些概念:
1. We import the `Component` decorator from the Angular library as we usually do.
1. 像往常一样我们从Angular库中导入`Component`装饰器。
1. The `@Component` selector value of "hero-form" means we can drop this form in a parent template with a `<hero-form>` tag.
1. `@Component`选择器的值"hero-form"表示我们将把此表单扔进父模板中的一个`<hero-form>`标签中。
1. The `templateUrl` property points to a separate file for template HTML called `hero-form.component.html`.
1. `templateUrl`属性指向一个独立的HTML模板文件名叫`hero-form.component.html`。
1. We defined dummy data for `model` and `powers` as befits a demo.
Down the road, we can inject a data service to get and save real data
or perhaps expose these properties as [inputs and outputs](./template-syntax.html#inputs-outputs) for binding to a
parent component. None of this concerns us now and these future changes won't affect our form.
1. 我们为`model`和`powers`定义了供演示用的假数据。
接下来,我们可以注入一个用于获取和保存真实数据的服务,
或者把这些属性暴露为[输入与输出属性](./template-syntax.html#inputs-outputs),以绑定到父组件上。
我们现在所关注的这些变更点,即使将来真的发生了,也不会影响到我们的表单。
我们目前不关心这些,因为将来这些变化不会影响到我们的表单。
1. We threw in a `diagnostic` property at the end to return a JSON representation of our model.
It'll help us see what we're doing during our development; we've left ourselves a cleanup note to discard it later.
1. 我们在最后增加一个`diagnostic`属性它返回这个模型的JSON形式。
它会帮我们看清开发过程中发生的事,等最后做清理时我们会丢弃它。
@ -251,7 +286,7 @@ code-example(format="").
write (or read) large stretches of HTML and few editors are much help with files that have a mix of HTML and code.
We also like short files with a clear and obvious purpose like this one.
没有什么答在所有场合都是“正确”的。当行内模板足够短的时候,我们喜欢用它。
没有什么答在所有场合都是“正确”的。当行内模板足够短的时候,我们喜欢用它。
但大多数的表单模板都不短。普遍来讲TypeScript和JavaScript文件不会是写大型HTML的好地方也不好读
而且没有几个编辑器能对混写的HTML和代码提供足够的帮助。
我们还是喜欢写成像这个一样清晰明确的短文件。
@ -266,6 +301,7 @@ code-example(format="").
.l-main-section
:marked
## Revise the *app.component.ts*
## 修改*app.component.ts*文件
`app.component.ts` is the application's root component. It will host our new `HeroFormComponent`.
@ -274,7 +310,7 @@ code-example(format="").
Replace the contents of the "QuickStart" version with the following:
把"QuickStart"版的内容替换成下列代码:
把"快速开始"的版本内容替换成下列代码:
+makeExample('forms/ts/app/app.component.ts', null, 'app/app.component.ts')
:marked
@ -285,18 +321,22 @@ code-example(format="").
只有三处修改:
1. We import the new `HeroFormComponent`.
1. 导入了新的`HeroFormComponent`组件。
1. The `template` is simply the new element tag identified by the component's `selector` property.
1. 直接把`template`的内容改成`HeroFormComponent`的`selector`属性中指定的新元素标签。
1. The `directives` array tells Angular that our template depends upon the `HeroFormComponent`
which is itself a Directive (as are all Components).
1. `directives`数组告诉Angular我们的模板依赖于`HeroFormComponent`组件,它本身也是一个指令(所有组件都是指令)。
.l-main-section
:marked
## Create an initial HTML Form Template
## 创建一个初始的HTML表单模板
Create a new template file called `hero-form.component.html` and give it the following definition:
@ -309,7 +349,7 @@ code-example(format="").
That is plain old HTML 5. We're presenting two of the `Hero` fields, `name` and `alterEgo`, and
opening them up for user input in input boxes.
这只是一段普通的旧式HTML 5代码。这里出现了两个`Hero`字段,`name`和`alterEgo`并且开放它们,让用户可以在输入框中输入。
这只是一段普通的旧式HTML 5代码。这里出现了两个`Hero`字段,`name`和`alterEgo`,让用户可以在输入框中输入,修改他们
The *Name* `<input>` control has the HTML5 `required` attribute;
the *Alter Ego* `<input>` control does not because `alterEgo` is optional.
@ -335,7 +375,9 @@ code-example(format="").
.callout.is-important
header Angular Forms Do Not Require A Style Library
header Angular表单不需要任何样式库
:marked
Angular makes no use of the `container`, `form-group`, `form-control`, and `btn` classes or
the styles of any external library. Angular apps can use any CSS library
@ -350,17 +392,21 @@ code-example(format="").
ol
li Open a terminal window in the application root folder and enter the command:
li 在应用的根目录下打开一个终端窗口,敲如下命令:
code-example(language="html" escape="html").
npm install bootstrap --save
li Open <code>index.html</code> and add the following link to the <code>&lt;head></code>.
li 打开<code>index.html</code>文件并且把下列链接添加到<code>&lt;head></code>中。
+makeExample('forms/ts/index.html', 'bootstrap')(format=".")
:marked
.l-main-section
:marked
## Add Powers with ***ngFor**
## 用***ngFor***添加超能力
Our hero may choose one super power from a fixed list of Agency-approved powers.
We maintain that list internally (in `HeroFormComponent`).
@ -377,6 +423,7 @@ ol
Add the following HTML *immediately below* the *Alter Ego* group.
在*Alter Ego*的紧下方添加如下HTML
+makeExample('forms/ts/app/hero-form.component.html', 'powers', 'app/hero-form.component.html (节选)')(format=".")
:marked
@ -384,14 +431,16 @@ ol
The `p` template input variable is a different power in each iteration;
we display its name using the interpolation syntax with the double-curly-braces.
我们为列表中的每一项超能力重复渲染出`<option>`标签。
我们为列表中的每一项超能力渲染出一个`<option>`标签。
模板输入变量`p`在每个迭代中都代表一个不同的超能力,我们使用双花括号中的插值表达式语法来显示它的名称。
<a id="ngModel"></a>
.l-main-section
:marked
## Two-way data binding with **ngModel**
## 使用**ngModel**进行双向数据绑定
Running the app right now would be disappointing.
如果立即运行此应用,你将会失望。
@ -448,6 +497,7 @@ figure.image-display
如果我们现在运行这个应用,并且开始在*姓名*输入框中键入、添加和删除字符,我们将看到它们从插值结果中显示和消失。
某一瞬间,它看起来可能是这样。
figure.image-display
img(src="/resources/images/devguide/forms/ng-model-in-action.png" width="400px" alt="操作中的ngModel")
:marked
@ -476,6 +526,7 @@ figure.image-display
If we ran the app right now and changed every Hero model property, the form might display like this:
如果现在我们运行本应用并且修改Hero模型的每一个属性表单看起来像这样
figure.image-display
img(src="/resources/images/devguide/forms/ng-model-in-action-2.png" width="400px" alt="ngModel in super action")
:marked
@ -491,8 +542,11 @@ figure.image-display
.l-sub-section
:marked
### Inside [(ngModel)]
### [(ngModel)]内幕
*This section is an optional deep dive into [(ngModel)]. Not interested? Skip ahead!*
*本节是对[(ngModel)]的深入剖析,它是可选的。如果不感兴趣,那就放心大胆的跳过它。*
The punctuation in the binding syntax, <span style="font-family:courier"><b>[()]</b></span>, is a good clue to what's going on.
@ -524,6 +578,7 @@ figure.image-display
as we do in this re-write of the "Name" `<input>` binding:
事实上,我们可以把`NgModel`绑定拆成两个独立的绑定,就像我们重写的“姓名”`<input>`绑定一样:
+makeExample('forms/ts/app/hero-form.component.html', 'ngModel-3','app/hero-form.component.html (节选)')(format=".")
:marked
@ -549,7 +604,7 @@ figure.image-display
模板表达式中的另一个古怪之处是`model.name = $event`。
我们以前看到的`$event`变量是来自DOM事件的。
但`ngModelChange`属性不处理DOM事件 —— 它是一个Angular `EventEmitter`类型的属性,当它触发时,
但`ngModelChange`属性不会生成DOM事件 —— 它是一个Angular `EventEmitter`类型的属性,当它触发时,
它返回的是输入框的值 —— 它恰好和我们希望赋给模型上`name`属性的值一样。
Nice to know but is it practical? We almost always prefer `[(ngModel)]`.
@ -568,6 +623,7 @@ figure.image-display
.l-main-section
:marked
## Track change-state and validity with **ngControl**
## 通过**ngControl**跟踪修改状态与有效性验证
A form isn't just about data binding. We'd also like to know the state of the controls on our form.
@ -596,7 +652,7 @@ figure.image-display
:marked
We set this particular `ngControl` to "name" which makes sense for our app. Any unique value will do.
对本应用来说,把这个`ngControl`设置为"name"会更容易理解。但也可以设置成任何唯一的值。
对本应用来说,把这个`ngControl`赋值为"name"会更容易理解。但也可以设置成任何唯一的值。
.l-sub-section
:marked
@ -627,6 +683,7 @@ figure.image-display
.l-main-section
:marked
## Add Custom CSS for Visual Feedback
## 添加自定义CSS以提供视觉反馈
The *NgControl* directive doesn't just track state.
@ -641,10 +698,10 @@ table
p 状态
th
p Class if true
p 有此CSS类
p 为真时的CSS类
th
p Class if false
p 无此CSS类
p 为假时的CSS类
tr
td
p Control has been visited
@ -689,17 +746,25 @@ table
然后严格按照下面四个步骤来做:
1. Look but don't touch
1. 查看输入框,但别碰它
1. Click in the input box, then click outside the text input box
1. 点击输入框,然后点击输入框外面
1. Add slashes to the end of the name
1. 在名字的末尾添加一个斜杠
1. Erase the name
1. 删除名字
The actions and effects are as follows:
动作和它对应的效果如下:
figure.image-display
img(src="/resources/images/devguide/forms/control-state-transitions-anim.gif" alt="控件状态转换")
:marked
@ -732,14 +797,16 @@ figure.image-display
:marked
These styles select for the two Angular validity classes and the HTML 5 "required" attribute.
这些样式的选择器是这两个Angular有效性类和HTML5的“required” Attribute
这些样式的选择器是这两个Angular有效性类和HTML5的“required” 属性
We update the `<head>` of the `index.html` to include this style sheet.
我们更新`index.html`中的`<head>`标签来包含这个样式表。
+makeExample('forms/ts/index.html', 'styles', 'index.html (节选)')(format=".")
:marked
## Show and Hide Validation Error messages
## 显示和隐藏有效性校验的错误信息
We can do better.
@ -756,6 +823,7 @@ figure.image-display
Here's the way it should look when the user deletes the name:
当用户删除姓名时,显示方式应该是这样的:
figure.image-display
img(src="/resources/images/devguide/forms/name-required-error.png" width="400px" alt="必须填写姓名")
@ -763,14 +831,19 @@ figure.image-display
To achieve this effect we extend the `<input>` tag with
要达到这个效果,我们得通过下列方式扩展`<input>`标签:
1. a [template reference variable](./template-syntax.html#ref-vars)
1. 一个[模板引用变量](./template-syntax.html#ref-vars)
1. the "*is required*" message in a nearby `<div>` which we'll display only if the control is invalid.
1. “本项必须填写”的消息放在附近的一个`<div>`元素中,只有当控件无效时,我们才显示它。
1. “is required”的消息放在附近的一个`<div>`元素中,只有当控件无效时,我们才显示它。
Here's how we do it for the *name* input box:
下面是我们应该对 *姓名* 输入框所要做的:
+makeExample('forms/ts/app/hero-form.component.html',
'name-with-error-msg',
'app/hero-form.component.html (节选)')(format=".")
@ -804,6 +877,7 @@ figure.image-display
Now we can control visibility of the "name" error message by binding properties of the `name` control to the message `<div>` element's `hidden` property.
现在,通过把`div`元素的`hidden`属性绑定到`name`控件的属性,我们就可以控制“姓名”字段错误信息的可见性了。
+makeExample('forms/ts/app/hero-form.component.html',
'hidden-error-msg',
'app/hero-form.component.html (节选)')
@ -847,12 +921,14 @@ figure.image-display
.l-main-section
:marked
## Add a hero and reset the form
## 添加一个英雄,并且重置表单
We'd like to add a new hero in this form.
We place a "New Hero" button at the bottom of the form and bind its click event to a component method.
我们希望在这个表单中添加一个新的英雄。
我们在表单的底部放一个“新增英雄”按钮,并且把它的click事件绑定到一个组件方法上。
我们在表单的底部放一个“新增英雄”按钮,并且把它的点击事件绑定到一个组件方法上。
+makeExample('forms/ts/app/hero-form.component.html',
'new-hero-button',
'app/hero-form.component.html (新增英雄按钮)')
@ -890,6 +966,7 @@ figure.image-display
这反映出在这种实现方式下Angular没办法区分是替换了整个英雄数据还是用程序单独清除了`name`属性。
Angular不能作出假设因此只好让控件保留当前状态 —— 脏状态。
:marked
We'll have to reset the form controls manually with a small trick.
We add an `active` flag to the component, initialized to `true`. When we add a new hero,
@ -956,7 +1033,9 @@ figure.image-display
.l-sub-section
:marked
### The NgForm directive
### NgForm指令
What `NgForm` directive? We didn't add an [NgForm](../api/common/NgForm-directive.html) directive!
什么`NgForm`指令?我们没有添加过[NgForm](../api/common/NgForm-directive.html)指令啊!
@ -971,8 +1050,8 @@ figure.image-display
It also has its own `valid` property which is true only *if every contained
control* is valid.
`NgForm`指令使用额外的特性扩充了`form`元素
它保存我们通过`ngControl` Attribute为各个元素创建的控件类,并且监视它们的属性变化,包括有效性。
`NgForm`指令为普通的`form`元素扩充了额外的特性
它保存我们通过`ngControl` 属性为各个元素创建的控件类,并且监视它们的属性变化,包括有效性。
它还有自己的`valid`属性,只有当*每一个被包含的控件*都有效时,它才有效。
:marked
@ -999,15 +1078,21 @@ figure.image-display
For us, it was as simple as
有了Angular它就是这么简单
1. Define a template reference variable on the (enhanced) form element
1. 定义一个模板引用变量,放在(强化过的)form元素上
2. Reference that variable in a button some 50 lines away.
2. 从50行之外的按钮上引用这个变量。
.l-main-section
:marked
## Toggle two form regions (extra credit)
## 切换两个表单区域(额外的荣誉)
Submitting the form isn't terribly dramatic at the moment.
现在就提交表单还不够激动人心。
@ -1027,7 +1112,7 @@ figure.image-display
Let's do something more strikingly visual.
Let's hide the data entry area and display something else.
我们来一些更明显的视觉效果吧。
我们来实现一些更明显的视觉效果吧。
隐藏掉数据输入框,并且显示一些别的东西。
Start by wrapping the form in a `<div>` and bind
@ -1082,34 +1167,53 @@ figure.image-display
.l-main-section
:marked
## Conclusion
## 结论
The Angular 2 form discussed in this chapter takes advantage of the following framework features to provide support for data modification, validation and more:
从本章对Angular 2表单的讨论中得到的用来支持数据修改、验证等的框架高级特性如下
本章讨论的Angular 2表单利用了下列框架特征来支持数据修改、验证和更多操作
- An Angular HTML form template.
- Angular HTML表单模板。
- A form component class with a `Component` decorator.
- 带有`Component`装饰器的组件类。
- The `ngSubmit` directive for handling the form submission.
- 用来处理表单提交的`ngSubmit`指令。
- Template reference variables such as `#heroForm`, `#name`, `#alter-ego` and `#power`.
- 模板引用变量,如`#heroForm`、`#name`、`#alter-ego`和`#power`。
- The `[(ngModel)]` syntax for two-way data binding.
- 用于双向数据绑定的`[(ngModel)]`语法
- The `ngControlName` directive for validation and form element change tracking.
- 用于验证和表单元素变化跟踪的`ngControlName`指令
- The reference variables `valid` property on input controls to check if a control is valid and show/hide error messages.
- 指向input控件的引用变量上的`valid`属性,可用于检查控件是否有效、是否显示/隐藏错误信息。
- Controlling the submit button's enabled state by binding to `NgForm` validity.
- 通过绑定到`NgForm`的有效性状态,控制提交按钮的禁用状态。
- Custom CSS classes that provide visual feedback to users about invalid controls.
- 对无效控件定制CSS类来给用户提供视觉反馈。
Our final project folder structure should look like this:
我们最终的项目目录结构看起来是这样:
.filetree
.file angular2-forms
.children