2017-11-06 13:02:18 -05:00
# The Hero Editor
2017-02-22 13:09:39 -05:00
2018-02-27 19:08:59 -05:00
# 英雄编辑器
2017-02-22 13:09:39 -05:00
2017-11-06 13:02:18 -05:00
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.
2017-03-31 19:57:13 -04:00
2018-02-27 19:08:59 -05:00
应用程序现在有了基本的标题。
接下来我们要创建一个新的组件来显示英雄信息并且把这个组件放到应用程序的外壳里去。
2017-08-28 23:01:56 -04:00
2017-11-06 13:02:18 -05:00
## Create the heroes component
2017-08-28 23:01:56 -04:00
2018-02-27 19:08:59 -05:00
## 创建英雄列表组件
2017-07-22 23:51:25 -04:00
2017-11-06 13:02:18 -05:00
Using the Angular CLI, generate a new component named `heroes` .
2017-03-30 15:04:18 -04:00
2017-02-22 13:09:39 -05:00
< code-example language = "sh" class = "code-shell" >
2018-03-03 08:06:01 -05:00
2017-11-06 13:02:18 -05:00
ng generate component heroes
2018-03-03 08:06:01 -05:00
2017-02-22 13:09:39 -05:00
< / code-example >
2017-11-06 13:02:18 -05:00
The CLI creates a new folder, `src/app/heroes/` and generates
the three files of the `HeroesComponent` .
2017-03-31 19:57:13 -04:00
2017-11-06 13:02:18 -05:00
The `HeroesComponent` class file is as follows:
2017-03-31 19:57:13 -04:00
2017-11-06 13:02:18 -05:00
< code-example
path="toh-pt1/src/app/heroes/heroes.component.ts" region="v1"
title="app/heroes/heroes.component.ts (initial version)" linenums="false">
2018-03-03 08:06:01 -05:00
2017-11-06 13:02:18 -05:00
< / code-example >
2017-07-22 23:51:25 -04:00
2017-11-06 13:02:18 -05:00
You always import the `Component` symbol from the Angular core library
and annotate the component class with `@Component` .
2017-02-22 13:09:39 -05:00
2017-11-06 13:02:18 -05:00
`@Component` is a decorator function that specifies the Angular metadata for the component.
2017-07-22 23:51:25 -04:00
2017-11-06 13:02:18 -05:00
The CLI generated three metadata properties:
2017-03-31 19:57:13 -04:00
2018-02-12 12:58:42 -05:00
1. `selector` — the component's CSS element selector
2018-03-03 08:06:01 -05:00
2017-11-06 13:02:18 -05:00
1. `templateUrl` — the location of the component's template file.
2018-03-03 08:06:01 -05:00
2017-11-06 13:02:18 -05:00
1. `styleUrls` — the location of the component's private CSS styles.
2017-03-31 19:57:13 -04:00
2017-11-06 13:02:18 -05:00
{@a selector}
2017-07-22 23:51:25 -04:00
2017-11-06 13:02:18 -05:00
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.
2017-07-22 23:51:25 -04:00
2017-11-06 13:02:18 -05:00
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.
2017-02-22 13:09:39 -05:00
2017-11-06 13:02:18 -05:00
Always `export` the component class so you can `import` it elsewhere ... like in the `AppModule` .
2017-07-22 23:51:25 -04:00
2017-11-06 13:02:18 -05:00
### Add a _hero_ property
2017-02-22 13:09:39 -05:00
2017-11-06 13:02:18 -05:00
Add a `hero` property to the `HeroesComponent` for a hero named "Windstorm."
2017-02-22 13:09:39 -05:00
2017-11-06 13:02:18 -05:00
< code-example path = "toh-pt1/src/app/heroes/heroes.component.ts" region = "add-hero" title = "heroes.component.ts (hero property)" linenums = "false" >
2018-03-03 08:06:01 -05:00
2017-03-27 11:08:53 -04:00
< / code-example >
2017-11-06 13:02:18 -05:00
### Show the hero
2017-03-31 19:57:13 -04:00
2017-11-06 13:02:18 -05:00
Open the `heroes.component.html` template file.
Delete the default text generated by the Angular CLI and
2018-03-06 22:25:56 -05:00
replace it with a data binding to the new `hero` property.
2017-03-31 19:57:13 -04:00
2017-11-06 13:02:18 -05:00
< code-example path = "toh-pt1/src/app/heroes/heroes.component.1.html" title = "heroes.component.html" region = "show-hero-1" linenums = "false" >
2018-03-03 08:06:01 -05:00
2017-03-27 11:08:53 -04:00
< / code-example >
2017-02-22 13:09:39 -05:00
2017-11-06 13:02:18 -05:00
## Show the _HeroesComponent_ view
2017-03-31 19:57:13 -04:00
2017-11-06 13:02:18 -05:00
To display the `HeroesComponent` , you must add it to the template of the shell `AppComponent` .
2017-03-31 19:57:13 -04:00
2017-11-06 13:02:18 -05:00
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.
2017-07-22 23:51:25 -04:00
2017-11-06 13:02:18 -05:00
< code-example path = "toh-pt1/src/app/app.component.html" title = "src/app/app.component.html" linenums = "false" >
2018-03-03 08:06:01 -05:00
2017-03-27 11:08:53 -04:00
< / code-example >
2017-11-06 13:02:18 -05:00
Assuming that the CLI `ng serve` command is still running,
the browser should refresh and display both the application title and the hero name.
2017-02-22 13:09:39 -05:00
2017-11-06 13:02:18 -05:00
## Create a Hero class
2017-07-22 23:51:25 -04:00
2017-11-06 13:02:18 -05:00
A real hero is more than a name.
2017-02-22 13:09:39 -05:00
2017-11-06 13:02:18 -05:00
Create a `Hero` class in its own file in the `src/app` folder.
Give it `id` and `name` properties.
2017-02-22 13:09:39 -05:00
2017-11-06 13:02:18 -05:00
< code-example path = "toh-pt1/src/app/hero.ts" title = "src/app/hero.ts" linenums = "false" >
2017-02-22 13:09:39 -05:00
2018-03-03 08:06:01 -05:00
< / code-example >
2017-03-31 19:57:13 -04:00
2017-11-06 13:02:18 -05:00
Return to the `HeroesComponent` class and import the `Hero` class.
2017-03-31 19:57:13 -04:00
2017-11-06 13:02:18 -05:00
Refactor the component's `hero` property to be of type `Hero` .
Initialize it with an `id` of `1` and the name `Windstorm` .
2017-02-22 13:09:39 -05:00
2017-07-22 23:51:25 -04:00
现在,有了一个`Hero`类,我们把组件`hero`属性的类型换成`Hero`。
然后以`1`为 id、以 “Windstorm” 为名字,初始化它。
2017-11-06 13:02:18 -05:00
The revised `HeroesComponent` class file should look like this:
2017-02-22 13:09:39 -05:00
2018-02-27 19:08:59 -05:00
修改后的 `HeroesComponent` 类应该是这样的:
2017-02-22 13:09:39 -05:00
2017-11-06 13:02:18 -05:00
< code-example path = "toh-pt1/src/app/heroes/heroes.component.ts" linenums = "false"
title= "src/app/heroes/heroes.component.ts">
2018-03-03 08:06:01 -05:00
2017-03-27 11:08:53 -04:00
< / code-example >
2017-02-22 13:09:39 -05:00
2017-11-06 13:02:18 -05:00
The page no longer displays properly because you changed the hero from a string to an object.
2017-03-31 19:57:13 -04:00
2017-11-06 13:02:18 -05:00
## Show the hero object
2017-03-31 19:57:13 -04:00
2017-11-06 13:02:18 -05:00
Update the binding in the template to announce the hero's name
and show both `id` and `name` in a details layout like this:
2017-02-22 13:09:39 -05:00
2017-11-06 13:02:18 -05:00
< 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">
2018-03-03 08:06:01 -05:00
2017-03-27 11:08:53 -04:00
< / code-example >
2017-02-22 13:09:39 -05:00
2017-11-06 13:02:18 -05:00
The browser refreshes and display's the hero's information.
2017-03-31 19:57:13 -04:00
2018-02-27 19:08:59 -05:00
浏览器自动刷新,并显示这位英雄的信息。
2017-03-31 19:57:13 -04:00
2017-11-06 13:02:18 -05:00
## Format with the _UppercasePipe_
2017-02-22 13:09:39 -05:00
2017-11-06 13:02:18 -05:00
Modify the `hero.name` binding like this.
< code-example
path="toh-pt1/src/app/heroes/heroes.component.html"
region="pipe">
2018-03-03 08:06:01 -05:00
2017-03-27 11:08:53 -04:00
< / code-example >
2017-02-22 13:09:39 -05:00
2017-11-06 13:02:18 -05:00
The browser refreshes and now the hero's name is displayed in capital letters.
2017-02-22 13:09:39 -05:00
2017-11-06 13:02:18 -05:00
The word `uppercase` in the interpolation binding,
right after the pipe operator ( | ),
activates the built-in `UppercasePipe` .
2017-03-31 19:57:13 -04:00
2017-11-06 13:02:18 -05:00
[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.
2017-03-31 19:57:13 -04:00
2017-11-06 13:02:18 -05:00
## Edit the hero
2017-02-22 13:09:39 -05:00
2017-07-22 23:51:25 -04:00
## 编辑英雄名字
2017-03-27 11:08:53 -04:00
Users should be able to edit the hero name in an `<input>` textbox.
2017-02-22 13:09:39 -05:00
2018-02-27 19:08:59 -05:00
用户应该能在一个`< input > `输入框中编辑英雄的名字。
2017-03-27 11:08:53 -04:00
The textbox should both _display_ the hero's `name` property
and _update_ that property as the user types.
2017-11-06 13:02:18 -05:00
That means data flow from the component class _out to the screen_ and
from the screen _back to the class_ .
2017-02-22 13:09:39 -05:00
2017-07-22 23:51:25 -04:00
当用户输入时,这个输入框应该能同时*显示*和*修改*英雄的`name`属性。
2018-02-27 19:08:59 -05:00
也就是说,数据流从组件类**流出到屏幕**,并且从屏幕**流回到组件类**。
2017-07-22 23:51:25 -04:00
2017-11-06 13:02:18 -05:00
To automate that data flow, setup a two-way data binding between the `<input>` form element and the `hero.name` property.
2017-02-22 13:09:39 -05:00
2018-02-27 19:08:59 -05:00
要想让这种数据流动自动化,就要在表单元素`< input > `和组件的`hero.name`属性之间建立双向数据绑定。
2017-07-22 23:51:25 -04:00
2017-03-27 11:08:53 -04:00
### Two-way binding
2017-02-22 13:09:39 -05:00
2017-07-22 23:51:25 -04:00
### 双向绑定
2017-11-06 13:02:18 -05:00
Refactor the details area in the `HeroesComponent` template so it looks like this:
2017-02-22 13:09:39 -05:00
2017-07-22 23:51:25 -04:00
把模板中的英雄名字重构成这样:
2017-11-06 13:02:18 -05:00
< 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" >
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-11-06 13:02:18 -05:00
**[(ngModel)]** is Angular's two-way data binding syntax.
2017-03-31 19:57:13 -04:00
2018-02-27 19:08:59 -05:00
**[(ngModel)]** 是 Angular 的双向数据绑定语法。
2017-03-31 19:57:13 -04:00
2017-11-06 13:02:18 -05:00
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` .
2017-07-22 23:51:25 -04:00
2018-02-27 19:08:59 -05:00
这里把 `hero.name` 属性绑定到了 HTML 的 textbox 元素上,以便数据流可以**双向流动**:从 `hero.name` 属性流动到 textbox, 并且从 textbox 流回到 `hero.name` 。
2017-02-22 13:09:39 -05:00
2017-11-06 13:02:18 -05:00
### The missing _FormsModule_
2017-07-22 23:51:25 -04:00
2018-02-27 19:08:59 -05:00
### 缺少 `FormsModule`
2017-02-22 13:09:39 -05:00
2017-11-06 13:02:18 -05:00
Notice that the app stopped working when you added `[(ngModel)]` .
2017-07-22 23:51:25 -04:00
2018-02-27 19:08:59 -05:00
注意,当我们加上 `[(ngModel)]` 之后这个应用无法工作了。
2017-02-22 13:09:39 -05:00
2017-11-06 13:02:18 -05:00
To see the error, open the browser development tools and look in the console
for a message like
2017-07-22 23:51:25 -04:00
2018-02-27 19:08:59 -05:00
打开浏览器的开发工具,就会在控制台中看到如下信息:
2017-02-22 13:09:39 -05:00
2017-11-06 13:02:18 -05:00
< code-example language = "sh" class = "code-shell" >
2018-03-03 08:06:01 -05:00
2017-11-06 13:02:18 -05:00
Template parse errors:
Can't bind to 'ngModel' since it isn't a known property of 'input'.
2018-03-03 08:06:01 -05:00
2017-02-22 13:09:39 -05:00
< / code-example >
2017-11-06 13:02:18 -05:00
Although `ngModel` is a valid Angular directive, it isn't available by default.
2017-02-22 13:09:39 -05:00
2017-11-06 13:02:18 -05:00
It belongs to the optional `FormsModule` and you must _opt-in_ to using it.
2017-02-22 13:09:39 -05:00
2017-11-06 13:02:18 -05:00
## _AppModule_
2017-03-27 11:08:53 -04:00
2017-11-06 13:02:18 -05:00
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_
2017-03-31 19:57:13 -04:00
2017-11-06 13:02:18 -05:00
Some of the metadata is in the `@Component` decorators that you added to your component classes.
2017-07-04 10:58:20 -04:00
Other critical metadata is in [`@NgModule` ](guide/ngmodules ) decorators.
2017-03-31 19:57:13 -04:00
2017-11-06 13:02:18 -05:00
The most important `@NgModule` decorator annotates the top-level **AppModule** class.
2017-02-22 13:09:39 -05:00
2017-11-06 13:02:18 -05:00
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` .
2017-07-22 23:51:25 -04:00
2017-11-06 13:02:18 -05:00
### Import _FormsModule_
2017-03-27 11:08:53 -04:00
2017-11-06 13:02:18 -05:00
Open `AppModule` (`app.module.ts`) and import the `FormsModule` symbol from the `@angular/forms` library.
2017-03-31 19:57:13 -04:00
2017-11-06 13:02:18 -05:00
< code-example path = "toh-pt1/src/app/app.module.ts" title = "app.module.ts (FormsModule symbol import)"
region="formsmodule-js-import">
2018-03-03 08:06:01 -05:00
2017-03-27 11:08:53 -04:00
< / code-example >
2017-03-31 19:57:13 -04:00
2017-11-06 13:02:18 -05:00
Then add `FormsModule` to the `@NgModule` metadata's `imports` array, which contains a list of external modules that the app needs.
2017-03-31 19:57:13 -04:00
2017-11-06 13:02:18 -05:00
< code-example path = "toh-pt1/src/app/app.module.ts" title = "app.module.ts ( @NgModule imports)"
region="ng-imports">
2018-03-03 08:06:01 -05:00
2017-11-06 13:02:18 -05:00
< / code-example >
2017-07-22 23:51:25 -04:00
2017-11-06 13:02:18 -05:00
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.
2017-03-27 11:08:53 -04:00
2018-03-07 02:42:49 -05:00
浏览器刷新。又见到我们的英雄了。我们可以编辑英雄的名字,也能看到这个改动立刻体现在`< h2 > `中。
2017-11-06 13:02:18 -05:00
### Declare _HeroesComponent_
2017-07-22 23:51:25 -04:00
2017-07-04 10:58:20 -04:00
Every component must be declared in _exactly one_ [NgModule ](guide/ngmodules ).
2017-07-22 23:51:25 -04:00
2017-11-06 13:02:18 -05:00
_You_ didn't declare the `HeroesComponent` .
So why did the application work?
2017-07-22 23:51:25 -04:00
2017-11-06 13:02:18 -05:00
It worked because the Angular CLI declared `HeroesComponent` in the `AppModule` when it generated that component.
2017-07-22 23:51:25 -04:00
2017-11-06 13:02:18 -05:00
Open `src/app/app.module.ts` and find `HeroesComponent` imported near the top.
2018-03-03 08:06:01 -05:00
2017-11-06 13:02:18 -05:00
< code-example path = "toh-pt1/src/app/app.module.ts" region = "heroes-import" >
2018-03-03 08:06:01 -05:00
2017-02-22 13:09:39 -05:00
< / code-example >
2017-03-27 11:08:53 -04:00
2017-11-06 13:02:18 -05:00
The `HeroesComponent` is declared in the `@NgModule.declarations` array.
2018-03-03 08:06:01 -05:00
2017-11-06 13:02:18 -05:00
< code-example path = "toh-pt1/src/app/app.module.ts" region = "declarations" >
2018-03-03 08:06:01 -05:00
2017-11-06 13:02:18 -05:00
< / code-example >
2017-07-22 23:51:25 -04:00
2017-11-06 13:02:18 -05:00
Note that `AppModule` declares both application components, `AppComponent` and `HeroesComponent` .
2017-02-22 13:09:39 -05:00
2017-11-06 13:02:18 -05:00
## Final code review
2017-02-22 13:09:39 -05:00
2017-11-06 13:02:18 -05:00
Your app should look like this < live-example > < / live-example > . Here are the code files discussed on this page.
2017-07-22 23:51:25 -04:00
2017-11-06 13:02:18 -05:00
< code-tabs >
2017-02-22 13:09:39 -05:00
2017-11-06 13:02:18 -05:00
< code-pane title = "src/app/heroes/heroes.component.ts" path = "toh-pt1/src/app/heroes/heroes.component.ts" >
< / code-pane >
2017-03-27 11:08:53 -04:00
2017-11-06 13:02:18 -05:00
< code-pane title = "src/app/heroes/heroes.component.html" path = "toh-pt1/src/app/heroes/heroes.component.html" >
< / code-pane >
2017-02-22 13:09:39 -05:00
2017-11-06 13:02:18 -05:00
< code-pane title = "src/app/app.module.ts"
path="toh-pt1/src/app/app.module.ts">
< / code-pane >
2017-02-22 13:09:39 -05:00
2017-11-06 13:02:18 -05:00
< code-pane title = "src/app/app.component.ts" path = "toh-pt1/src/app/app.component.ts" >
< / code-pane >
2017-03-31 19:57:13 -04:00
2017-11-06 13:02:18 -05:00
< code-pane title = "src/app/app.component.html" path = "toh-pt1/src/app/app.component.html" >
< / code-pane >
2017-03-31 19:57:13 -04:00
2017-11-06 13:02:18 -05:00
< code-pane title = "src/app/hero.ts"
path="toh-pt1/src/app/hero.ts">
< / code-pane >
2017-07-22 23:51:25 -04:00
2017-11-06 13:02:18 -05:00
< / code-tabs >
2017-07-22 23:51:25 -04:00
2017-09-27 16:45:47 -04:00
## Summary
2017-07-22 23:51:25 -04:00
2018-03-03 08:06:01 -05:00
## 小结
2017-11-06 13:02:18 -05:00
* You used the CLI to create a second `HeroesComponent` .
2018-03-03 08:06:01 -05:00
2017-11-06 13:02:18 -05:00
* You displayed the `HeroesComponent` by adding it to the `AppComponent` shell.
2018-03-03 08:06:01 -05:00
2017-11-06 13:02:18 -05:00
* You applied the `UppercasePipe` to format the name.
2018-03-03 08:06:01 -05:00
2017-11-06 13:02:18 -05:00
* You used two-way data binding with the `ngModel` directive.
2018-03-03 08:06:01 -05:00
2017-11-06 13:02:18 -05:00
* You learned about the `AppModule` .
2018-03-03 08:06:01 -05:00
2017-11-06 13:02:18 -05:00
* You imported the `FormsModule` in the `AppModule` so that Angular would recognize and apply the `ngModel` directive.
2018-03-03 08:06:01 -05:00
2017-11-06 13:02:18 -05:00
* You learned the importance of declaring components in the `AppModule`
2018-03-03 08:06:01 -05:00
and appreciated that the CLI declared it for you.