442 lines
16 KiB
Markdown
442 lines
16 KiB
Markdown
# The Hero Editor
|
||
|
||
# 英雄编辑器
|
||
|
||
The application now has a basic title.
|
||
Next you will create a new component to display hero information
|
||
and place that component in the application shell.
|
||
|
||
应用程序现在有了基本的标题。
|
||
接下来你要创建一个新的组件来显示英雄信息并且把这个组件放到应用程序的外壳里去。
|
||
|
||
## Create the heroes component
|
||
|
||
## 创建英雄列表组件
|
||
|
||
Using the Angular CLI, generate a new component named `heroes`.
|
||
|
||
使用 Angular CLI 创建一个名为 `heroes` 的新组件。
|
||
|
||
<code-example language="sh" class="code-shell">
|
||
|
||
ng generate component heroes
|
||
|
||
</code-example>
|
||
|
||
The CLI creates a new folder, `src/app/heroes/` and generates
|
||
the three files of the `HeroesComponent`.
|
||
|
||
CLI 创建了一个新的文件夹 `src/app/heroes/`,并生成了 `HeroesComponent` 的三个文件。
|
||
|
||
The `HeroesComponent` class file is as follows:
|
||
|
||
`HeroesComponent` 的类文件如下:
|
||
|
||
<code-example
|
||
path="toh-pt1/src/app/heroes/heroes.component.ts" region="v1"
|
||
title="app/heroes/heroes.component.ts (initial version)" linenums="false">
|
||
|
||
</code-example>
|
||
|
||
You always import the `Component` symbol from the Angular core library
|
||
and annotate the component class with `@Component`.
|
||
|
||
你要从 Angular 核心库中导入 `Component` 符号,并为组件类加上 `@Component` 装饰器。
|
||
|
||
`@Component` is a decorator function that specifies the Angular metadata for the component.
|
||
|
||
`@Component` 是个装饰器函数,用于为该组件指定 Angular 所需的元数据。
|
||
|
||
The CLI generated three metadata properties:
|
||
|
||
CLI 自动生成了三个元数据属性:
|
||
|
||
1. `selector`— the component's CSS element selector
|
||
|
||
`selector`— 组件的选择器(CSS 元素选择器)
|
||
|
||
1. `templateUrl`— the location of the component's template file.
|
||
|
||
`templateUrl`— 组件模板文件的位置。
|
||
|
||
1. `styleUrls`— the location of the component's private CSS styles.
|
||
|
||
`styleUrls`— 组件私有 CSS 样式表文件的位置。
|
||
|
||
{@a selector}
|
||
|
||
The [CSS element selector](https://developer.mozilla.org/en-US/docs/Web/CSS/Type_selectors),
|
||
`'app-heroes'`, matches the name of the HTML element that identifies this component within a parent component's template.
|
||
|
||
[CSS 元素选择器](https://developer.mozilla.org/en-US/docs/Web/CSS/Type_selectors) `app-heroes` 用来在父组件的模板中匹配 HTML 元素的名称,以识别出该组件。
|
||
|
||
The `ngOnInit` is a [lifecycle hook](guide/lifecycle-hooks#oninit)
|
||
Angular calls `ngOnInit` shortly after creating a component.
|
||
It's a good place to put initialization logic.
|
||
|
||
`ngOnInit` 是一个[生命周期钩子](guide/lifecycle-hooks#oninit),Angular 在创建完组件后很快就会调用 `ngOnInit`。这里是放置初始化逻辑的好地方。
|
||
|
||
Always `export` the component class so you can `import` it elsewhere ... like in the `AppModule`.
|
||
|
||
始终要 `export` 这个组件类,以便在其它地方(比如 `AppModule`)导入它。
|
||
|
||
### Add a _hero_ property
|
||
|
||
### 添加 `hero` 属性
|
||
|
||
Add a `hero` property to the `HeroesComponent` for a hero named "Windstorm."
|
||
|
||
往 `HeroesComponent` 中添加一个 `hero` 属性,用来表示一个名叫 “Windstorm” 的英雄。
|
||
|
||
<code-example path="toh-pt1/src/app/heroes/heroes.component.ts" region="add-hero" title="heroes.component.ts (hero property)" linenums="false">
|
||
|
||
</code-example>
|
||
|
||
### Show the hero
|
||
|
||
### 显示英雄
|
||
|
||
Open the `heroes.component.html` template file.
|
||
Delete the default text generated by the Angular CLI and
|
||
replace it with a data binding to the new `hero` property.
|
||
|
||
打开模板文件 `heroes.component.html`。删除 Angular CLI 自动生成的默认内容,改为到 `hero` 属性的数据绑定。
|
||
|
||
<code-example path="toh-pt1/src/app/heroes/heroes.component.1.html" title="heroes.component.html" region="show-hero-1" linenums="false">
|
||
|
||
</code-example>
|
||
|
||
## Show the _HeroesComponent_ view
|
||
|
||
## 显示 `HeroesComponent` 视图
|
||
|
||
To display the `HeroesComponent`, you must add it to the template of the shell `AppComponent`.
|
||
|
||
要显示 `HeroesComponent` 你必须把它加到壳组件 `AppComponent` 的模板中。
|
||
|
||
Remember that `app-heroes` is the [element selector](#selector) for the `HeroesComponent`.
|
||
So add an `<app-heroes>` element to the `AppComponent` template file, just below the title.
|
||
|
||
别忘了,`app-heroes` 就是 `HeroesComponent` 的 [元素选择器](#selector)。
|
||
所以,只要把 `<app-heroes>` 元素添加到 `AppComponent` 的模板文件中就可以了,就放在标题下方。
|
||
|
||
<code-example path="toh-pt1/src/app/app.component.html" title="src/app/app.component.html" linenums="false">
|
||
|
||
</code-example>
|
||
|
||
Assuming that the CLI `ng serve` command is still running,
|
||
the browser should refresh and display both the application title and the hero name.
|
||
|
||
如果 CLI 的 `ng serve` 命令仍在运行,浏览器就会自动刷新,并且同时显示出应用的标题和英雄的名字。
|
||
|
||
## Create a Hero class
|
||
|
||
## 创建 `Hero` 类
|
||
|
||
A real hero is more than a name.
|
||
|
||
真实的英雄当然不止一个名字。
|
||
|
||
Create a `Hero` class in its own file in the `src/app` folder.
|
||
Give it `id` and `name` properties.
|
||
|
||
在 `src/app` 文件夹中为 `Hero` 类创建一个文件,并添加 `id` 和 `name` 属性。
|
||
|
||
<code-example path="toh-pt1/src/app/hero.ts" title="src/app/hero.ts" linenums="false">
|
||
|
||
</code-example>
|
||
|
||
Return to the `HeroesComponent` class and import the `Hero` class.
|
||
|
||
回到 `HeroesComponent` 类,并且导入这个 `Hero` 类。
|
||
|
||
Refactor the component's `hero` property to be of type `Hero`.
|
||
Initialize it with an `id` of `1` and the name `Windstorm`.
|
||
|
||
把组件的 `hero` 属性的类型重构为 `Hero`。
|
||
然后以`1`为 `id`、以 “Windstorm” 为名字初始化它。
|
||
|
||
The revised `HeroesComponent` class file should look like this:
|
||
|
||
修改后的 `HeroesComponent` 类应该是这样的:
|
||
|
||
<code-example path="toh-pt1/src/app/heroes/heroes.component.ts" linenums="false"
|
||
title= "src/app/heroes/heroes.component.ts">
|
||
|
||
</code-example>
|
||
|
||
The page no longer displays properly because you changed the hero from a string to an object.
|
||
|
||
页面显示变得不正常了,因为你刚刚把 `hero` 从字符串改成了对象。
|
||
|
||
## Show the hero object
|
||
|
||
## 显示 `hero` 对象
|
||
|
||
Update the binding in the template to announce the hero's name
|
||
and show both `id` and `name` in a details layout like this:
|
||
|
||
修改模板中的绑定,以显示英雄的名字,并在详情中显示 `id` 和 `name`,就像这样:
|
||
|
||
<code-example
|
||
path="toh-pt1/src/app/heroes/heroes.component.1.html"
|
||
region="show-hero-2"
|
||
title="heroes.component.html (HeroesComponent's template)" linenums="false">
|
||
|
||
</code-example>
|
||
|
||
The browser refreshes and display's the hero's information.
|
||
|
||
浏览器自动刷新,并显示这位英雄的信息。
|
||
|
||
## Format with the _UppercasePipe_
|
||
|
||
## 使用 `UppercasePipe` 进行格式化
|
||
|
||
Modify the `hero.name` binding like this.
|
||
|
||
把 `hero.name` 的绑定修改成这样:
|
||
|
||
<code-example
|
||
path="toh-pt1/src/app/heroes/heroes.component.html"
|
||
region="pipe">
|
||
|
||
</code-example>
|
||
|
||
The browser refreshes and now the hero's name is displayed in capital letters.
|
||
|
||
浏览器刷新了。现在,英雄的名字显示成了大写字母。
|
||
|
||
The word `uppercase` in the interpolation binding,
|
||
right after the pipe operator ( | ),
|
||
activates the built-in `UppercasePipe`.
|
||
|
||
绑定表达式中的 `uppercase` 位于管道操作符( | )的右边,用来调用内置管道 `UppercasePipe`。
|
||
|
||
[Pipes](guide/pipes) are a good way to format strings, currency amounts, dates and other display data.
|
||
Angular ships with several built-in pipes and you can create your own.
|
||
|
||
[管道](guide/pipes) 是格式化字符串、金额、日期和其它显示数据的好办法。
|
||
Angular 发布了一些内置管道,而且你还可以创建自己的管道。
|
||
|
||
## Edit the hero
|
||
|
||
## 编辑英雄名字
|
||
|
||
Users should be able to edit the hero name in an `<input>` textbox.
|
||
|
||
用户应该能在一个`<input>`输入框中编辑英雄的名字。
|
||
|
||
The textbox should both _display_ the hero's `name` property
|
||
and _update_ that property as the user types.
|
||
That means data flow from the component class _out to the screen_ and
|
||
from the screen _back to the class_.
|
||
|
||
当用户输入时,这个输入框应该能同时*显示*和*修改*英雄的`name`属性。
|
||
也就是说,数据流从组件类**流出到屏幕**,并且从屏幕**流回到组件类**。
|
||
|
||
To automate that data flow, setup a two-way data binding between the `<input>` form element and the `hero.name` property.
|
||
|
||
要想让这种数据流动自动化,就要在表单元素`<input>`和组件的`hero.name`属性之间建立双向数据绑定。
|
||
|
||
### Two-way binding
|
||
|
||
### 双向绑定
|
||
|
||
Refactor the details area in the `HeroesComponent` template so it looks like this:
|
||
|
||
把模板中的英雄名字重构成这样:
|
||
|
||
<code-example path="toh-pt1/src/app/heroes/heroes.component.1.html" region="name-input" title="src/app/heroes/heroes.component.html (HeroesComponent's template)" linenums="false">
|
||
|
||
</code-example>
|
||
|
||
**[(ngModel)]** is Angular's two-way data binding syntax.
|
||
|
||
**[(ngModel)]** 是 Angular 的双向数据绑定语法。
|
||
|
||
Here it binds the `hero.name` property to the HTML textbox so that data can flow _in both directions:_ from the `hero.name` property to the textbox, and from the textbox back to the `hero.name`.
|
||
|
||
这里把 `hero.name` 属性绑定到了 HTML 的 textbox 元素上,以便数据流可以**双向流动**:从 `hero.name` 属性流动到 textbox,并且从 textbox 流回到 `hero.name` 。
|
||
|
||
### The missing _FormsModule_
|
||
|
||
### 缺少 `FormsModule`
|
||
|
||
Notice that the app stopped working when you added `[(ngModel)]`.
|
||
|
||
注意,当你加上 `[(ngModel)]` 之后这个应用无法工作了。
|
||
|
||
To see the error, open the browser development tools and look in the console
|
||
for a message like
|
||
|
||
打开浏览器的开发工具,就会在控制台中看到如下信息:
|
||
|
||
<code-example language="sh" class="code-shell">
|
||
|
||
Template parse errors:
|
||
Can't bind to 'ngModel' since it isn't a known property of 'input'.
|
||
|
||
</code-example>
|
||
|
||
Although `ngModel` is a valid Angular directive, it isn't available by default.
|
||
|
||
虽然 `ngModel` 是一个有效的 Angular 指令,不过它在默认情况下是不可用的。
|
||
|
||
It belongs to the optional `FormsModule` and you must _opt-in_ to using it.
|
||
|
||
它属于一个可选模块`FormsModule`,你必须自行添加此模块才能使用该指令。
|
||
|
||
## _AppModule_
|
||
|
||
Angular needs to know how the pieces of your application fit together
|
||
and what other files and libraries the app requires.
|
||
This information is called _metadata_
|
||
|
||
Angular 需要知道如何把应用程序的各个部分组合到一起,以及该应用需要哪些其它文件和库。
|
||
这些信息被称为*元数据(metadata)*。
|
||
|
||
Some of the metadata is in the `@Component` decorators that you added to your component classes.
|
||
Other critical metadata is in [`@NgModule`](guide/ngmodules) decorators.
|
||
|
||
有些元数据位于 `@Component` 装饰器中,你会把它加到组件类上。
|
||
另一些关键性的元数据位于 [`@NgModule`](guide/ngmodules) 装饰器中。
|
||
|
||
The most important `@NgModule`decorator annotates the top-level **AppModule** class.
|
||
|
||
最重要的 `@NgModule` 装饰器位于顶级类 **AppModule** 上。
|
||
|
||
The Angular CLI generated an `AppModule` class in `src/app/app.module.ts` when it created the project.
|
||
This is where you _opt-in_ to the `FormsModule`.
|
||
|
||
Angular CLI 在创建项目的时候就在 `src/app/app.module.ts` 中生成了一个 `AppModule` 类。
|
||
这里也就是你要添加 `FormsModule` 的地方。
|
||
|
||
### Import _FormsModule_
|
||
|
||
### 导入 `FormsModule`
|
||
|
||
Open `AppModule` (`app.module.ts`) and import the `FormsModule` symbol from the `@angular/forms` library.
|
||
|
||
打开 `AppModule` (`app.module.ts`) 并从 `@angular/forms` 库中导入 `FormsModule` 符号。
|
||
|
||
<code-example path="toh-pt1/src/app/app.module.ts" title="app.module.ts (FormsModule symbol import)"
|
||
region="formsmodule-js-import">
|
||
|
||
</code-example>
|
||
|
||
Then add `FormsModule` to the `@NgModule` metadata's `imports` array, which contains a list of external modules that the app needs.
|
||
|
||
然后把 `FormsModule` 添加到 `@NgModule` 元数据的 `imports` 数组中,这里是该应用所需外部模块的列表。
|
||
|
||
<code-example path="toh-pt1/src/app/app.module.ts" title="app.module.ts ( @NgModule imports)"
|
||
region="ng-imports">
|
||
|
||
</code-example>
|
||
|
||
When the browser refreshes, the app should work again. You can edit the hero's name and see the changes reflected immediately in the `<h2>` above the textbox.
|
||
|
||
刷新浏览器,应用又能正常工作了。你可以编辑英雄的名字,并且会看到这个改动立刻体现在这个输入框上方的`<h2>`中。
|
||
|
||
### Declare _HeroesComponent_
|
||
|
||
### 声明 `HeroesComponent`
|
||
|
||
Every component must be declared in _exactly one_ [NgModule](guide/ngmodules).
|
||
|
||
每个组件都必须声明在(且只能声明在)一个 [NgModule](guide/ngmodules) 中。
|
||
|
||
_You_ didn't declare the `HeroesComponent`.
|
||
So why did the application work?
|
||
|
||
*你*没有声明过 `HeroesComponent`,可为什么本应用却正常呢?
|
||
|
||
It worked because the Angular CLI declared `HeroesComponent` in the `AppModule` when it generated that component.
|
||
|
||
这是因为 Angular CLI 在生成 `HeroesComponent` 组件的时候就自动把它加到了 `AppModule` 中。
|
||
|
||
Open `src/app/app.module.ts` and find `HeroesComponent` imported near the top.
|
||
|
||
打开 `src/app/app.module.ts` 你就会发现 `HeroesComponent` 已经在顶部导入过了。
|
||
|
||
<code-example path="toh-pt1/src/app/app.module.ts" region="heroes-import" >
|
||
|
||
</code-example>
|
||
|
||
The `HeroesComponent` is declared in the `@NgModule.declarations` array.
|
||
|
||
`HeroesComponent` 也已经声明在了 `@NgModule.declarations` 数组中。
|
||
|
||
<code-example path="toh-pt1/src/app/app.module.ts" region="declarations">
|
||
|
||
</code-example>
|
||
|
||
Note that `AppModule` declares both application components, `AppComponent` and `HeroesComponent`.
|
||
|
||
注意 `AppModule` 声明了应用中的所有组件,`AppComponent` 和 `HeroesComponent`。
|
||
|
||
## Final code review
|
||
|
||
## 查看最终代码
|
||
|
||
Your app should look like this <live-example></live-example>. Here are the code files discussed on this page.
|
||
|
||
应用跑起来应该是这样的:<live-example></live-example>。本页中所提及的代码如下:
|
||
|
||
<code-tabs>
|
||
|
||
<code-pane title="src/app/heroes/heroes.component.ts" path="toh-pt1/src/app/heroes/heroes.component.ts">
|
||
</code-pane>
|
||
|
||
<code-pane title="src/app/heroes/heroes.component.html" path="toh-pt1/src/app/heroes/heroes.component.html">
|
||
</code-pane>
|
||
|
||
<code-pane title="src/app/app.module.ts"
|
||
path="toh-pt1/src/app/app.module.ts">
|
||
</code-pane>
|
||
|
||
<code-pane title="src/app/app.component.ts" path="toh-pt1/src/app/app.component.ts">
|
||
</code-pane>
|
||
|
||
<code-pane title="src/app/app.component.html" path="toh-pt1/src/app/app.component.html">
|
||
</code-pane>
|
||
|
||
<code-pane title="src/app/hero.ts"
|
||
path="toh-pt1/src/app/hero.ts">
|
||
</code-pane>
|
||
|
||
</code-tabs>
|
||
|
||
## Summary
|
||
|
||
## 小结
|
||
|
||
* You used the CLI to create a second `HeroesComponent`.
|
||
|
||
你使用 CLI 创建了第二个组件 `HeroesComponent`。
|
||
|
||
* You displayed the `HeroesComponent` by adding it to the `AppComponent` shell.
|
||
|
||
你把 `HeroesComponent` 添加到了壳组件 `AppComponent` 中,以便显示它。
|
||
|
||
* You applied the `UppercasePipe` to format the name.
|
||
|
||
你使用 `UppercasePipe` 来格式化英雄的名字。
|
||
|
||
* You used two-way data binding with the `ngModel` directive.
|
||
|
||
你用 `ngModel` 指令实现了双向数据绑定。
|
||
|
||
* You learned about the `AppModule`.
|
||
|
||
你知道了 `AppModule`。
|
||
|
||
* You imported the `FormsModule` in the `AppModule` so that Angular would recognize and apply the `ngModel` directive.
|
||
|
||
你把 `FormsModule` 导入了 `AppModule`,以便 Angular 能识别并应用 `ngModel` 指令。
|
||
|
||
* You learned the importance of declaring components in the `AppModule`
|
||
and appreciated that the CLI declared it for you.
|
||
|
||
你知道了把组件声明到 `AppModule` 是很重要的,并认识到 CLI 会自动帮你声明它。
|