diff --git a/aio/content/navigation.json b/aio/content/navigation.json
index cf1f2ea4d4..bb40ea706c 100644
--- a/aio/content/navigation.json
+++ b/aio/content/navigation.json
@@ -32,26 +32,22 @@
"tooltip": "Angular 开发文档",
"hidden": true
},
-
{
"url": "guide/docs-style-guide",
"title": "文档风格指南",
"tooltip": "给文档作者的风格指南",
"hidden": true
},
-
{
"url": "guide/webpack",
"title": "Webpack: 简介",
"hidden": true
},
-
{
"url": "guide/quickstart",
"title": "快速上手",
"tooltip": "Angular 破冰"
},
-
{
"title": "教程",
"tooltip": "此《英雄指南》教程会带你用 TypeScript 一步步创建一个 Angular 应用。",
@@ -78,7 +74,7 @@
},
{
"url": "tutorial/toh-pt3",
- "title": "5. 主从结构",
+ "title": "5. 主从组件",
"tooltip": "第五部分:把主从结构的页面重构成多个组件"
},
{
@@ -98,7 +94,6 @@
}
]
},
-
{
"title": "核心知识",
"tooltip": "学习 Angular 的核心知识",
@@ -226,14 +221,12 @@
}
]
},
-
{
"url": "guide/bootstrapping",
"title": "引导启动",
"tooltip": "在应用的根模块(AppModule)中告诉 Angular 如何构造并引导引用。"
},
{
-
"title": "NgModules",
"tooltip": "NgModules.",
"children": [
@@ -297,8 +290,8 @@
"title": "NgModule 常见问题",
"tooltip": "回答关于 NgModules 的常见问题。"
}
- ]},
-
+ ]
+ },
{
"title": "依赖注入",
"tooltip": "依赖注入:创建并注入各种服务。",
@@ -325,7 +318,6 @@
}
]
},
-
{
"url": "guide/http",
"title": "HttpClient",
@@ -346,13 +338,12 @@
"title": "速查表",
"tooltip": "关于 Angular 常用编码技术的快速指南。"
}
- ]},
-
+ ]
+ },
{
"title": "其它技术",
"tooltip": "把 Angular 用到你的实际工作中的一些技巧",
"children": [
-
{
"url": "guide/i18n",
"title": "国际化 (i18n)",
@@ -392,7 +383,6 @@
"title": "npm 包",
"tooltip": "建议的 npm 包,以及如何指定包的依赖。"
},
-
{
"url": "guide/typescript-configuration",
"title": "TypeScript 配置",
@@ -441,7 +431,6 @@
}
]
},
-
{
"title": "升级",
"tooltip": "渐进式的把 AngularJS 应用升级到 Angular。",
@@ -480,7 +469,6 @@
}
]
},
-
{
"title": "API 参考手册",
"tooltip": "关于 Angular 中类和值的详细信息。",
@@ -493,7 +481,6 @@
"hidden": true
}
],
-
"Footer": [
{
"title": "资源",
@@ -585,11 +572,18 @@
]
}
],
-
"docVersions": [
- { "title": "v4 (LTS)", "url": "https://v4.angular.io" },
- { "title": "v2", "url": "https://v2.angular.cn" },
- { "title": "AngularDart", "url": "https://webdev.dartlang.org/angular" }
-
+ {
+ "title": "v4 (LTS)",
+ "url": "https://v4.angular.io"
+ },
+ {
+ "title": "v2",
+ "url": "https://v2.angular.cn"
+ },
+ {
+ "title": "AngularDart",
+ "url": "https://webdev.dartlang.org/angular"
+ }
]
}
diff --git a/aio/content/tutorial/toh-pt0.md b/aio/content/tutorial/toh-pt0.md
index 78c21556aa..32467727b1 100644
--- a/aio/content/tutorial/toh-pt0.md
+++ b/aio/content/tutorial/toh-pt0.md
@@ -130,7 +130,7 @@ This interpolation binding presents the component's `title` property value
inside the HTML header tag.
双花括号语法是 Angular 的*插值绑定*语法。
-这个插值绑定的意思是把组件的 `title` 属性的值绑定到 HTML 中的 header 标记中。
+这个插值绑定的意思是把组件的 `title` 属性的值绑定到 HTML 中的 `h1` 标记中。
The browser refreshes and displays the new application title.
@@ -160,7 +160,7 @@ Here's an excerpt from the `styles.css` for the _Tour of Heroes_ sample app.
## Final code review
-## 最终的代码
+## 查看最终代码
The source code for this tutorial and the complete _Tour of Heroes_ global styles
are available in the .
diff --git a/aio/content/tutorial/toh-pt1.md b/aio/content/tutorial/toh-pt1.md
index 12cfbf62ce..9006b90def 100644
--- a/aio/content/tutorial/toh-pt1.md
+++ b/aio/content/tutorial/toh-pt1.md
@@ -15,6 +15,8 @@ and place that component in the application shell.
Using the Angular CLI, generate a new component named `heroes`.
+使用 Angular CLI 创建一个名为 `heroes` 的新组件。
+
ng generate component heroes
@@ -24,8 +26,12 @@ Using the Angular CLI, generate a new component named `heroes`.
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` 的类文件如下:
+
@@ -35,52 +41,85 @@ The `HeroesComponent` class file is as follows:
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” 的英雄。
+
### 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` 属性的数据绑定。
+
## 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 `` element to the `AppComponent` template file, just below the title.
+别忘了,`app-heroes` 就是 `HeroesComponent` 的 [元素选择器](#selector)。
+所以,只要把 `` 元素添加到 `AppComponent` 的模板文件中就可以了,就放在标题下方。
+
@@ -88,24 +127,34 @@ So add an `` element to the `AppComponent` template file, just below
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` 属性。
+
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`属性的类型换成`Hero`。
-然后以`1`为 id、以 “Windstorm” 为名字,初始化它。
+把组件的 `hero` 属性的类型重构为 `Hero`。
+然后以`1`为 `id`、以 “Windstorm” 为名字初始化它。
The revised `HeroesComponent` class file should look like this:
@@ -118,11 +167,17 @@ The revised `HeroesComponent` class file should look like this:
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`,就像这样:
+
@@ -146,13 +205,20 @@ Modify the `hero.name` binding like this.
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
## 编辑英雄名字
@@ -215,26 +281,45 @@ Can't bind to 'ngModel' since it isn't a known property of 'input'.
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` 符号。
+
@@ -242,6 +327,8 @@ Open `AppModule` (`app.module.ts`) and import the `FormsModule` symbol from the
Then add `FormsModule` to the `@NgModule` metadata's `imports` array, which contains a list of external modules that the app needs.
+然后把 `FormsModule` 添加到 `@NgModule` 元数据的 `imports` 数组中,这里是该应用所需外部模块的列表。
+
@@ -249,35 +336,53 @@ region="ng-imports">
When the browser refreshes, the app should work again. You can edit the hero's name and see the changes reflected immediately in the `
` above the textbox.
-浏览器刷新。又见到我们的英雄了。我们可以编辑英雄的名字,也能看到这个改动立刻体现在`
`中。
### 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` 已经在顶部导入过了。
+
The `HeroesComponent` is declared in the `@NgModule.declarations` array.
+`HeroesComponent` 也已经声明在了 `@NgModule.declarations` 数组中。
+
Note that `AppModule` declares both application components, `AppComponent` and `HeroesComponent`.
+注意 `AppModule` 声明了应用中的所有组件,`AppComponent` 和 `HeroesComponent`。
+
## Final code review
+## 查看最终代码
+
Your app should look like this . Here are the code files discussed on this page.
+应用跑起来应该是这样的:。本页中所提及的代码如下:
+
@@ -308,15 +413,29 @@ Your app should look like this . Here are the code
* 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 会自动帮你声明它。
diff --git a/aio/content/tutorial/toh-pt2.md b/aio/content/tutorial/toh-pt2.md
index ee999bec5a..31eab672d8 100644
--- a/aio/content/tutorial/toh-pt2.md
+++ b/aio/content/tutorial/toh-pt2.md
@@ -1,22 +1,35 @@
# Display a Heroes List
+# 显示英雄列表
+
In this page, you'll expand the Tour of Heroes app to display a list of heroes, and
allow users to select a hero and display the hero's details.
-我们需要管理多个英雄。我们将扩展《英雄指南》应用,让它显示一个英雄列表,
- 允许用户选择一个英雄,查看该英雄的详细信息。
+本页中,你将扩展《英雄指南》应用,让它显示一个英雄列表,
+并允许用户选择一个英雄,查看该英雄的详细信息。
## Create mock heroes
+## 创建模拟(mock)的英雄数据
+
You'll need some heroes to display.
+你需要一些英雄数据以供显示。
+
Eventually you'll get them from a remote data server.
For now, you'll create some _mock heroes_ and pretend they came from the server.
+最终,你会从远端的数据服务器获取它。
+不过目前,你要先创建一些*模拟的英雄数据*,并假装它们是从服务器上取到的。
+
Create a file called `mock-heroes.ts` in the `src/app/` folder.
Define a `HEROES` constant as an array of ten heroes and export it.
The file should look like this.
+在 `src/app/` 文件夹中创建一个名叫 `mock-heroes.ts` 的文件。
+定义一个包含十个英雄的常量数组 `HEROES`,并导出它。
+该文件是这样的。
+
@@ -24,42 +37,64 @@ title="src/app/mock-heroes.ts">
## Displaying heroes
-## 显示我们的英雄
+## 显示这些英雄
You're about to display the list of heroes at the top of the `HeroesComponent`.
+你要在 `HeroesComponent` 的顶部显示这个英雄列表。
+
Open the `HeroesComponent` class file and import the mock `HEROES`.
+打开 `HeroesComponent` 类文件,并导入模拟的 `HEROES`。
+
Add a `heroes` property to the class that exposes these heroes for binding.
+往类中添加一个 `heroes` 属性,这样可以暴露出这些英雄,以供绑定。
+
### List heroes with _*ngFor_
+### 使用 `*ngFor` 列出这些英雄
+
Open the `HeroesComponent` template file and make the following changes:
+打开 `HeroesComponent` 的模板文件,并做如下修改:
+
* Add an `
` at the top,
+ 在顶部添加 `
`,
+
* Below it add an HTML unordered list (`
`)
+ 然后添加表示无序列表的 HTML 元素(`
`)
+
* Insert an `
` within the `
` that displays properties of a `hero`.
+ 在 `
` 中插入一个 `
` 元素,以显示单个 `hero` 的属性。
+
* Sprinkle some CSS classes for styling (you'll add the CSS styles shortly).
+ 点缀上一些 CSS 类(稍后你还会添加更多 CSS 样式)。
+
Make it look like this:
+做完之后应该是这样的:
+
Now change the `
` to this:
+现在,把 `
` 修改成这样:
+
@@ -67,48 +102,78 @@ Now change the `
` to this:
The [`*ngFor`](guide/template-syntax#ngFor) is Angular's _repeater_ directive.
It repeats the host element for each element in a list.
+[`*ngFor`](guide/template-syntax#ngFor) 是一个 Angular 的复写器(repeater)指令。
+它会为列表中的每项数据复写它的宿主元素。
+
In this example
+在这个例子中
+
* `
` is the host element
+ `
` 就是 `*ngFor` 的宿主元素
+
* `heroes` is the list from the `HeroesComponent` class.
+ `heroes` 就是来自 `HeroesComponent` 类的列表。
+
* `hero` holds the current hero object for each iteration through the list.
+ 当依次遍历这个列表时,`hero` 会为每个迭代保存当前的英雄对象。
+
Don't forget the asterisk (*) in front of `ngFor`. It's a critical part of the syntax.
+不要忘了 `ngFor` 前面的星号(`*`),它是该语法中的关键部分。
+
After the browser refreshes, the list of heroes appears.
+浏览器刷新之后,英雄列表出现了。
+
{@a styles}
### Style the heroes
-### 给我们的英雄们“美容”
+### 给英雄们“美容”
The heroes list should be attractive and should respond visually when users
hover over and select a hero from the list.
+英雄列表应该富有吸引力,并且当用户把鼠标移到某个英雄上和从列表中选中某个英雄时,应该给出视觉反馈。
+
In the [first tutorial](tutorial/toh-pt0#app-wide-styles), you set the basic styles for the entire application in `styles.css`.
That stylesheet didn't include styles for this list of heroes.
+在[教程的第一章](tutorial/toh-pt0#app-wide-styles),你曾在 `styles.css` 中为整个应用设置了一些基础的样式。
+但那个样式表并不包含英雄列表所需的样式。
+
You could add more styles to `styles.css` and keep growing that stylesheet as you add components.
+固然,你可以把更多样式加入到 `styles.css`,并且放任它随着你添加更多组件而不断膨胀。
+
You may prefer instead to define private styles for a specific component and keep everything a component needs— the code, the HTML,
and the CSS —together in one place.
+但还有更好的方式。你可以定义属于特定组件的私有样式,并且让组件所需的一切(代码、HTML 和 CSS)都放在一起。
+
This approach makes it easier to re-use the component somewhere else
and deliver the component's intended appearance even if the global styles are different.
+这种方式让你在其它地方复用该组件更加容易,并且即使全局样式和这里不一样,组件也仍然具有期望的外观。
+
You define private styles either inline in the `@Component.styles` array or
as stylesheet file(s) identified in the `@Component.styleUrls` array.
+你可以用多种方式定义私有样式,或者内联在 `@Component.styles` 数组中,或者在 `@Component.styleUrls` 所指出的样式表文件中。
+
When the CLI generated the `HeroesComponent`, it created an empty `heroes.component.css` stylesheet for the `HeroesComponent`
and pointed to it in `@Component.styleUrls` like this.
+当 CLI 生成 `HeroesComponent` 时,它也同时为 `HeroesComponent` 创建了空白的 `heroes.component.css` 样式表文件,并且让 `@Component.styleUrls` 指向它,就像这样:
+
@@ -117,23 +182,37 @@ and pointed to it in `@Component.styleUrls` like this.
Open the `heroes.component.css` file and paste in the private CSS styles for the `HeroesComponent`.
You'll find them in the [final code review](#final-code-review) at the bottom of this guide.
+打开 `heroes.component.css` 文件,并且把 `HeroesComponent` 的私有 CSS 样式粘贴进去。
+你可以在本指南底部的[查看最终代码](#final-code-review)中找到它们。
+
Styles and stylesheets identified in `@Component` metadata are scoped to that specific component.
The `heroes.component.css` styles apply only to the `HeroesComponent` and don't affect the outer HTML or the HTML in any other component.
+`@Component` 元数据中指定的样式和样式表都是局限于该组件的。
+`heroes.component.css` 中的样式只会作用于 `HeroesComponent`,既不会影响到组件外的 HTML,也不会影响到其它组件中的 HTML。
+
## Master/Detail
+## 主从结构
+
When the user clicks a hero in the **master** list,
the component should display the selected hero's **details** at the bottom of the page.
+当用户在**主**列表中点击一个英雄时,该组件应该在页面底部显示所选英雄的**详情**。
+
In this section, you'll listen for the hero item click event
and update the hero detail.
+在本节,你将监听英雄条目的点击事件,并更新英雄的详情。
+
### Add a click event binding
+### 添加 `click` 事件绑定
+
Add a click event binding to the `
` like this:
我们再往`
`元素上插入一句点击事件的绑定代码:
@@ -144,40 +223,66 @@ Add a click event binding to the `
` like this:
This is an example of Angular's [event binding](guide/template-syntax#event-binding) syntax.
+这是 Angular [事件绑定](guide/template-syntax#event-binding) 语法的例子。
+
The parentheses around `click` tell Angular to listen for the `
` element's `click` event.
When the user clicks in the `
` 时,Angular 就会执行表达式 `onSelect(hero)`。
+
`onSelect()` is a `HeroesComponent` method that you're about to write.
Angular calls it with the `hero` object displayed in the clicked `
`,
the same `hero` defined previously in the `*ngFor` expression.
+`onSelect()` 是 `HeroesComponent` 上的一个方法,你很快就要写它。
+Angular 会把所点击的 `
` 上的 `hero` 对象传给它,这个 `hero` 也就是以前在 `*ngFor` 表达式中定义的那个。
+
### Add the click event handler
+### 添加 `click` 事件处理器
+
Rename the component's `hero` property to `selectedHero` but don't assign it.
There is no _selected hero_ when the application starts.
+把该组件的 `hero` 属性改名为 `selectedHero`,但不要为它赋值。
+因为应用刚刚启动时并没有*所选英雄*。
+
Add the following `onSelect()` method, which assigns the clicked hero from the template
to the component's `selectedHero`.
+添加如下 `onSelect()` 方法,它会把模板中被点击的英雄赋值给组件的 `selectedHero` 属性。
+
### Update the details template
+### 修改详情模板
+
The template still refers to the component's old `hero` property which no longer exists.
Rename `hero` to `selectedHero`.
+该模板引用的仍然是老的 `hero` 属性,但它已经不存在了。
+把 `hero` 改名为 `selectedHero`。
+
### Hide empty details with _*ngIf_
+### 使用 `*ngIf` 隐藏空白的详情
+
After the browser refreshes, the application is broken.
+刷新浏览器,应用挂了。
+
Open the browser developer tools and look in the console for an error message like this:
+打开浏览器的开发者工具,它的控制台中显示出如下错误信息:
+
HeroesComponent.html:3 ERROR TypeError: Cannot read property 'name' of undefined
@@ -188,23 +293,42 @@ Now click one of the list items.
The app seems to be working again.
The heroes appear in a list and details about the clicked hero appear at the bottom of the page.
+现在,从列表中随便点击一个条目。
+应用又正常了。
+英雄们显示在列表中,并且所点英雄的详情也显示在了页面的下方。
+
#### What happened?
+#### 怎么回事?
+
When the app starts, the `selectedHero` is `undefined` _by design_.
+当应用启动时,`selectedHero` 是 `undefined`,*设计如此*。
+
Binding expressions in the template that refer to properties of `selectedHero` — expressions like `{{selectedHero.name}}` — _must fail_ because there is no selected hero.
+但模板中的绑定表达式引用了 `selectedHero` 的属性(表达式为`{{selectedHero.name}}`),这必然会失败,因为你还没选过英雄呢。
+
#### The fix
+#### 修复
+
The component should only display the selected hero details if the `selectedHero` exists.
+该组件应该只有当 `selectedHero` 存在时才显示所选英雄的详情。
+
Wrap the hero detail HTML in a `
`.
Add Angular's `*ngIf` directive to the `
` and set it to `selectedHero`.
+把显示英雄详情的 HTML 包裹在一个`
`中。
+并且为这个 div 添加 Angular 的 `*ngIf` 指令,把它的值设置为 `selectedHero`。
+
Don't forget the asterisk (*) in front of `ngIf`. It's a critical part of the syntax.
+不要忘了 `ngIf` 前面的星号(`*`),它是该语法中的关键部分。
+
@@ -215,21 +339,35 @@ After the browser refreshes, the list of names reappears.
The details area is blank.
Click a hero and its details appear.
+浏览器刷新之后,英雄名字的列表又出现了。
+详情部分仍然是空。
+点击一个英雄,它的详情就出现了。
+
#### Why it works
+#### 为什么改好了?
+
When `selectedHero` is undefined, the `ngIf` removes the hero detail from the DOM. There are no `selectedHero` bindings to worry about.
+当 `selectedHero` 为 `undefined` 时,`ngIf` 从 DOM 中移除了英雄详情。因此也就不用担心 `selectedHero` 的绑定了。
+
When the user picks a hero, `selectedHero` has a value and
`ngIf` puts the hero detail into the DOM.
+当用户选择一个英雄时,`selectedHero` 也就有了值,并且 `ngIf` 把英雄的详情放回到 DOM 中。
+
### Style the selected hero
### 给所选英雄添加样式
It's difficult to identify the _selected hero_ in the list when all `
` elements look alike.
+所有的 `
` 元素看起来都是一样的,因此很难从列表中识别出*所选英雄*。
+
If the user clicks "Magneta", that hero should render with a distinctive but subtle background color like this:
+如果用户点击了“Magneta”,这个英雄应该用一个略有不同的背景色显示出来,就像这样:
+
@@ -239,19 +377,31 @@ If the user clicks "Magneta", that hero should render with a distinctive but sub
That _selected hero_ coloring is the work of the `.selected` CSS class in the [styles you added earlier](#styles).
You just have to apply the `.selected` class to the `
` when the user clicks it.
+*所选英雄*的颜色来自于[你前面添加的样式](#styles)中的 CSS 类 `.selected`。
+所以你只要在用户点击一个 `
` 时把 `.selected` 类应用到该元素上就可以了。
+
The Angular [class binding](guide/template-syntax#class-binding) makes it easy to add and remove a CSS class conditionally.
Just add `[class.some-css-class]="some-condition"` to the element you want to style.
+Angular 的 [CSS 类绑定](guide/template-syntax#class-binding)机制让根据条件添加或移除一个 CSS 类变得很容易。
+只要把 `[class.some-css-class]="some-condition"` 添加到你要施加样式的元素上就可以了。
+
Add the following `[class.selected]` binding to the `
` in the `HeroesComponent` template:
+在 `HeroesComponent` 模板中的 `
` 元素上添加 `[class.selected]` 绑定,代码如下:
+
When the current row hero is the same as the `selectedHero`, Angular adds the `selected` CSS class. When the two heroes are different, Angular removes the class.
+如果当前行的英雄和 `selectedHero` 相同,Angular 就会添加 CSS 类 `selected`,否则就会移除它。
+
The finished `
` looks like this:
+最终的 `
` 是这样的:
+
@@ -260,12 +410,16 @@ The finished `
` looks like this:
## Final code review
+## 查看最终代码
+
Your app should look like this .
我们的应用现在变成了这样:。
Here are the code files discussed on this page, including the `HeroesComponent` styles.
+下面是本页面中所提及的代码文件,包括 `HeroesComponent` 的样式。
+
@@ -285,10 +439,20 @@ Here are the code files discussed on this page, including the `HeroesComponent`
* The Tour of Heroes app displays a list of heroes in a Master/Detail view.
+ 英雄指南应用在一个主从视图中显示了英雄列表。
+
* The user can select a hero and see that hero's details.
+ 用户可以选择一个英雄,并查看该英雄的详情。
+
* You used `*ngFor` to display a list.
+ 你使用 `*ngFor` 显示了一个列表。
+
* You used `*ngIf` to conditionally include or exclude a block of HTML.
+ 你使用 `*ngIf` 来根据条件包含或排除了一段 HTML。
+
* You can toggle a CSS style class with a `class` binding.
+
+ 你可以用 `class` 绑定来切换 CSS 的样式类。
\ No newline at end of file
diff --git a/aio/content/tutorial/toh-pt3.md b/aio/content/tutorial/toh-pt3.md
index 572b68711c..e803579a5a 100644
--- a/aio/content/tutorial/toh-pt3.md
+++ b/aio/content/tutorial/toh-pt3.md
@@ -1,19 +1,35 @@
# Master/Detail Components
+# 主从组件
+
At the moment, the `HeroesComponent` displays both the list of heroes and the selected hero's details.
+此刻,`HeroesComponent` 同时显示了英雄列表和所选英雄的详情。
+
Keeping all features in one component as the application grows will not be maintainable.
You'll want to split up large components into smaller sub-components, each focused on a specific task or workflow.
-In this page, you'll take the first step in that direction by moving the hero details into a separate, reusable `HeroDetailsComponent`.
+把所有特性都放在同一个组件中,将会使应用“长大”后变得不可维护。
+你要把大型组件拆分成小一点的子组件,每个子组件都要集中精力处理某个特定的任务或工作流。
+
+In this page, you'll take the first step in that direction by moving the hero details into a separate, reusable `HeroDetailComponent`.
+
+本页面中,你将迈出第一步 —— 把英雄详情移入一个独立的、可复用的 `HeroDetailComponent`。
The `HeroesComponent` will only present the list of heroes.
-The `HeroDetailsComponent` will present details of a selected hero.
+The `HeroDetailComponent` will present details of a selected hero.
+
+`HeroesComponent` 将仅仅用来表示英雄列表。
+`HeroDetailComponent` 将用来表示所选英雄的详情。
## Make the `HeroDetailComponent`
+## 制作 `HeroDetailComponent`
+
Use the Angular CLI to generate a new component named `hero-detail`.
+使用 Angular CLI 生成一个名叫 `hero-detail` 的新组件。
+
ng generate component hero-detail
@@ -22,27 +38,44 @@ Use the Angular CLI to generate a new component named `hero-detail`.
The command scaffolds the `HeroDetailComponent` files and declares the component in `AppModule`.
+该命令会生成 `HeroDetailComponent` 文件的脚手架,并把它声明在 `AppModule` 中。
+
### Write the template
+### 编写模板
+
Cut the HTML for the hero detail from the bottom of the `HeroesComponent` template and paste it over the generated boilerplate in the `HeroDetailComponent` template.
+从 `HeroesComponent` 模板的底部把表示英雄详情的 HTML 代码剪切粘贴到所生成的 `HeroDetailComponent` 模板中。
+
The pasted HTML refers to a `selectedHero`.
The new `HeroDetailComponent` can present _any_ hero, not just a selected hero.
So replace "selectedHero" with "hero" everywhere in the template.
+所粘贴的 HTML 引用了 `selectedHero`。
+新的 `HeroDetailComponent` 可以展示*任意*英雄,而不仅仅所选的。因此还要把模板中的所有 `selectedHero` 替换为 `hero`。
+
When you're done, the `HeroDetailComponent` template should look like this:
+完工之后,`HeroDetailComponent` 的模板应该是这样的:
+
### Add the `@Input()` hero property
+### 添加 `@Input() hero` 属性
+
The `HeroDetailComponent` template binds to the component's `hero` property
which is of type `Hero`.
+`HeroDetailComponent` 模板中绑定了组件中的 `hero` 属性,它的类型是 `Hero`。
+
Open the `HeroDetailComponent` class file and import the `Hero` symbol.
+打开 `HeroDetailComponent` 类文件,并导入 `Hero` 符号。
+
@@ -53,18 +86,24 @@ The `hero` property
annotated with the `@Input()` decorator,
because the _external_ `HeroesComponent` [will bind to it](#heroes-component-template) like this.
+`hero` 属性[必须是一个带有`@Input()`装饰器的输入属性](guide/template-syntax#inputs-outputs "Input and Output properties"),因为*外部的* `HeroesComponent` 组件[将会绑定到它](#heroes-component-template)。就像这样:
+
Amend the `@angular/core` import statement to include the `Input` symbol.
+修改 `@angular/core` 的导入语句,导入 `Input` 符号。
+
Add a `hero` property, preceded by the `@Input()` decorator.
+添加一个带有 `@Input()` 装饰器的 `hero` 属性。
+
@@ -73,43 +112,73 @@ That's the only change you should make to the `HeroDetailComponent` class.
There are no more properties. There's no presentation logic.
This component simply receives a hero object through its `hero` property and displays it.
+这就是你要对 `HeroDetailComponent` 类做的唯一一项修改。
+没有其它属性,也没有展示逻辑。这个组件所做的只是通过 `hero` 属性接收一个英雄对象,并显示它。
+
## Show the `HeroDetailComponent`
+## 显示 `HeroDetailComponent`
+
The `HeroesComponent` is still a master/detail view.
+`HeroesComponent` 仍然是主从视图。
+
It used to display the hero details on its own, before you cut that portion of the template. Now it will delegate to the `HeroDetailComponent`.
+在你从模板中剪切走代码之前,它自己负责显示英雄的详情。现在它要把这个职责委托给 `HeroDetailComponent` 了。
+
The two components will have a parent/child relationship.
The parent `HeroesComponent` will control the child `HeroDetailComponent`
by sending it a new hero to display whenever
the user selects a hero from the list.
+这两个组件将会具有父子关系。
+当用户从列表中选择了某个英雄时,父组件 `HeroesComponent` 将通过把要显示的新英雄发送给子组件 `HeroDetailComponent`,来控制子组件。
+
You won't change the `HeroesComponent` _class_ but you will change its _template_.
+你不用修改 `HeroesComponent` *类*,但是要修改它的*模板*。
+
{@a heroes-component-template}
### Update the `HeroesComponent` template
+### 修改 `HeroesComponent` 的模板
+
The `HeroDetailComponent` selector is `'app-hero-detail'`.
Add an `` element near the bottom of the `HeroesComponent` template, where the hero detail view used to be.
+`HeroDetailComponent` 的选择器是 `'app-hero-detail'`。
+把 `` 添加到 `HeroesComponent` 模板的底部,以便把英雄详情的视图显示到那里。
+
Bind the `HeroesComponent.selectedHero` to the element's `hero` property like this.
+把 `HeroesComponent.selectedHero` 绑定到钙元素的 `hero` 属性,就像这样:
+
`[hero]="selectedHero"` is an Angular [property binding](guide/template-syntax#property-binding).
+`[hero]="selectedHero"` 是 Angular 的[属性绑定](guide/template-syntax#property-binding)语法。
+
It's a _one way_ data binding from
the `selectedHero` property of the `HeroesComponent` to the `hero` property of the target element, which maps to the `hero` property of the `HeroDetailComponent`.
+这是一种*单向*数据绑定。从 `HeroesComponent` 的 `selectedHero` 属性绑定到目标元素的 `hero` 属性,并映射到了 `HeroDetailComponent` 的 `hero` 属性。
+
Now when the user clicks a hero in the list, the `selectedHero` changes.
When the `selectedHero` changes, the _property binding_ updates `hero`
and the `HeroDetailComponent` displays the new hero.
+现在,当用户在列表中点击某个英雄时,`selectedHero` 就改变了。
+当 `selectedHero` 改变时,*属性绑定*会修改 `HeroDetailComponent` 的 `hero` 属性,`HeroDetailComponent` 就会显示这个新的英雄。
+
The revised `HeroesComponent` template should look like this:
+修改后的 `HeroesComponent` 的模板是这样的:
+
@@ -117,6 +186,8 @@ The revised `HeroesComponent` template should look like this:
The browser refreshes and the app starts working again as it did before.
+浏览器刷新,应用又像以前一样开始工作了。
+
## What changed?
## 有哪些变化?
@@ -125,21 +196,38 @@ As [before](tutorial/toh-pt2), whenever a user clicks on a hero name,
the hero detail appears below the hero list.
Now the `HeroDetailComponent` is presenting those details instead of the `HeroesComponent`.
+像[以前](tutorial/toh-pt2)一样,一旦用户点击了一个英雄的名字,该英雄的详情就显示在了英雄列表下方。
+现在,`HeroDetailComponent` 负责显示那些详情,而不再是 `HeroesComponent`。
+
Refactoring the original `HeroesComponent` into two components yields benefits, both now and in the future:
+把原来的 `HeroesComponent` 重构成两个组件带来了一些优点,无论是现在还是未来:
+
1. You simplified the `HeroesComponent` by reducing its responsibilities.
+ 你通过缩减 `HeroesComponent` 的职责简化了该组件。
+
1. You can evolve the `HeroDetailComponent` into a rich hero editor
without touching the parent `HeroesComponent`.
+ 你可以把 `HeroDetailComponent` 改进成一个功能丰富的英雄编辑器,而不用改动父组件 `HeroesComponent`。
+
1. You can evolve the `HeroesComponent` without touching the hero detail view.
+ 你可以改进 `HeroesComponent`,而不用改动英雄详情视图。
+
1. You can re-use the `HeroDetailComponent` in the template of some future component.
+ 将来你可以在其它组件的模板中重复使用 `HeroDetailComponent`。
+
## Final code review
+## 查看最终代码
+
Here are the code files discussed on this page and your app should look like this .
+你的应用应该变成了这样 。本页所提及的代码文件如下:
+
@@ -159,8 +247,15 @@ Here are the code files discussed on this page and your app should look like thi
* You created a separate, reusable `HeroDetailComponent`.
+ 你创建了一个独立的、可复用的 `HeroDetailComponent` 组件。
+
* You used a [property binding](guide/template-syntax#property-binding) to give the parent `HeroesComponent` control over the child `HeroDetailComponent`.
+ 你用[属性绑定](guide/template-syntax#property-binding)语法来让父组件 `HeroesComponent` 可以控制子组件 `HeroDetailComponent`。
+
* You used the [`@Input` decorator](guide/template-syntax#inputs-outputs)
to make the `hero` property available for binding
by the external `HeroesComponent`.
+
+ 你用 [`@Input` 装饰器](guide/template-syntax#inputs-outputs)来让 `hero` 属性可以在外部的 `HeroesComponent` 中绑定。
+
\ No newline at end of file
diff --git a/aio/content/tutorial/toh-pt4.md b/aio/content/tutorial/toh-pt4.md
index 6baa247b39..59e77c0127 100644
--- a/aio/content/tutorial/toh-pt4.md
+++ b/aio/content/tutorial/toh-pt4.md
@@ -1,31 +1,56 @@
# Services
+# 服务
+
The Tour of Heroes `HeroesComponent` is currently getting and displaying fake data.
+英雄指南的 `HeroesComponent` 目前获取和显示的都是模拟数据。
+
After the refactoring in this tutorial, `HeroesComponent` will be lean and focused on supporting the view.
It will also be easier to unit-test with a mock service.
+本节课的重构完成之后,`HeroesComponent` 变得更精简,并且聚焦于为它的视图提供支持。这也让它更容易使用模拟服务进行单元测试。
+
## Why services
+## 为什么需要服务
+
Components shouldn't fetch or save data directly and they certainly shouldn't knowingly present fake data.
They should focus on presenting data and delegate data access to a service.
+组件不应该直接获取或保存数据,它们不应该了解是否在展示假数据。
+它们应该聚焦于展示数据,而把数据访问的职责委托给某个服务。
+
In this tutorial, you'll create a `HeroService` that all application classes can use to get heroes.
Instead of creating that service with `new`,
you'll rely on Angular [*dependency injection*](guide/dependency-injection)
to inject it into the `HeroesComponent` constructor.
+本节课,你将创建一个 `HeroService`,应用中的所有类都可以使用它来获取英雄列表。
+不要使用 `new` 来创建此服务,而要依靠 Angular 的[*依赖注入*](guide/dependency-injection)机制把它注入到 `HeroesComponent` 的构造函数中。
+
Services are a great way to share information among classes that _don't know each other_.
You'll create a `MessageService` and inject it in two places:
+服务是在多个“互相不知道”的类之间共享信息的好办法。
+你将创建一个 `MessageService`,并且把它注入到两个地方:
+
1. in `HeroService` which uses the service to send a message.
+ `HeroService` 中,它会使用该服务发送消息。
+
2. in `MessagesComponent` which displays that message.
+ `MessagesComponent` 中,它会显示其中的消息。
+
## Create the _HeroService_
+## 创建 `HeroService`
+
Using the Angular CLI, create a service called `hero`.
+使用 Angular CLI 创建一个名叫 `hero` 的服务。
+
ng generate service hero
@@ -35,6 +60,9 @@ Using the Angular CLI, create a service called `hero`.
The command generates skeleton `HeroService` class in `src/app/hero.service.ts`
The `HeroService` class should look like the below.
+该命令会在 `src/app/hero.service.ts` 中生成 `HeroService` 类的骨架。
+`HeroService` 类的代码如下:
+
@@ -42,38 +70,61 @@ The `HeroService` class should look like the below.
### _@Injectable()_ services
+### _@Injectable()_ 服务
+
Notice that the new service imports the Angular `Injectable` symbol and annotates
the class with the `@Injectable()` decorator.
+注意,这个新的服务导入了 Angular 的 `Injectable` 符号,并且给这个服务类添加了 `@Injectable()` 装饰器。
+
The `@Injectable()` decorator tells Angular that this service _might_ itself
have injected dependencies.
It doesn't have dependencies now but [it will soon](#inject-message-service).
Whether it does or it doesn't, it's good practice to keep the decorator.
+`@Injectable()` 装饰器告诉 Angular 这个服务本身*可能*拥有被注入的依赖。
+目前它还没有依赖,但是[很快就会有了](#inject-message-service)。
+无论它会不会有,总是给服务加上这个装饰器都是一种好的做法。
+
The Angular [style guidelines](guide/styleguide#style-07-04) strongly recommend keeping it
and the linter enforces this rule.
+Angular 的[风格指南](guide/styleguide#style-07-04)强烈建议加上,而且 linter(代码检查器) 也会确保这条规则。
+
### Get hero data
+### 获取英雄数据
+
The `HeroService` could get hero data from anywhere—a web service, local storage, or a mock data source.
+`HeroService` 可以从任何地方获取数据:Web 服务、本地存储(LocalStorage)或一个模拟的数据源。
+
Removing data access from components means you can change your mind about the implementation anytime, without touching any components.
They don't know how the service works.
+从组件中移除数据访问逻辑,意味着将来任何时候你都可以改变目前的实现方式,而不用改动任何组件。
+这些组件不需要了解该服务的内部实现。
+
The implementation in _this_ tutorial will continue to deliver _mock heroes_.
+这节课中的实现仍然会提供*模拟的英雄列表*。
+
Import the `Hero` and `HEROES`.
+导入 `Hero` 和 `HEROES`。
+
Add a `getHeroes` method to return the _mock heroes_.
+添加一个 `getHeroes` 方法,让它返回*模拟的英雄列表*。
+
@@ -82,19 +133,30 @@ Add a `getHeroes` method to return the _mock heroes_.
## Provide the `HeroService`
+## 提供(provide) `HeroService`
+
You must _provide_ the `HeroService` in the _dependency injection system_
before Angular can _inject_ it into the `HeroesComponent`,
as you will do [below](#inject).
+在要求 Angular 把`HeroService` 注入到 `HeroesComponent` 之前,你必须先把这个服务*提供给依赖注入系统*。[稍后](#inject)你就要这么做。
+
There are several ways to provide the `HeroService`:
in the `HeroesComponent`, in the `AppComponent`, in the `AppModule`.
Each option has pros and cons.
+有很多途径可以提供 `HeroService`:在 `HeroesComponent` 中、在 `AppComponent` 中,或在 `AppModule` 中。
+每个选项都各有优缺点。
+
This tutorial chooses to provide it in the `AppModule`.
+这节课选择在 `AppModule` 中提供它。
+
That's such a popular choice that you could have told the CLI to provide it there automatically
by appending `--module=app`.
+这是一个常用的选择,因此你可以通过 `--module=app` 选项让 CLI 自动把它提供给 `AppModule`。
+
ng generate service hero --module=app
@@ -103,8 +165,12 @@ by appending `--module=app`.
Since you did not, you'll have to provide it yourself.
+如果不这样做,你就要自行提供它。
+
Open the `AppModule` class, import the `HeroService`, and add it to the `@NgModule.providers` array.
+打开 `AppModule` 类,导入 `HeroService`,并把它加入 `@NgModule.providers` 数组中。
+
@@ -112,33 +178,50 @@ Open the `AppModule` class, import the `HeroService`, and add it to the `@NgModu
The `providers` array tells Angular to create a single, shared instance of `HeroService`
and inject into any class that asks for it.
+`providers` 数组会告诉 Angular 创建 `HeroService` 的单一、共享的实例,并且把它注入到如何请求注入它的类中。
+
The `HeroService` is now ready to plug into the `HeroesComponent`.
+现在 `HeroService` 已经准备好插入到 `HeroesComponent` 中了。
+
This is a interim code sample that will allow you to provide and use the `HeroService`. At this point, the code will differ from the `HeroService` in the ["final code review"](#final-code-review).
+这是一个过渡性的代码范例,它将会允许你提供并使用 `HeroService`。此刻的代码和[最终代码](#final-code-review)相差很大。
+
Learn more about _providers_ in the [Providers](guide/providers) guide.
+ 要进一步了解*提供商*,参见[服务提供商](guide/providers)一章。
+
## Update `HeroesComponent`
+## 修改 `HeroesComponent`
+
Open the `HeroesComponent` class file.
+打开 `HeroesComponent` 类文件。
+
Delete the `HEROES` import as you won't need that anymore.
Import the `HeroService` instead.
+删除 `HEROES` 导入,我们以后不会再用它了。
+转而导入 `HeroService`。
+
Replace the definition of the `heroes` property with a simple declaration.
+把 `heroes` 属性的定义改为一句简单的声明。
+
@@ -147,21 +230,33 @@ Replace the definition of the `heroes` property with a simple declaration.
### Inject the `HeroService`
+### 注入 `HeroService`
+
Add a private `heroService` parameter of type `HeroService` to the constructor.
+往构造函数中添加一个私有的 `heroService`,其类型为 `HeroService`。
+
The parameter simultaneously defines a private `heroService` property and identifies it as a `HeroService` injection site.
+这个参数同时做了两件事:1. 声明了一个私有 `heroService` 属性,2. 把它标记为一个 `HeroService` 的注入点。
+
When Angular creates a `HeroesComponent`, the [Dependency Injection](guide/dependency-injection) system
sets the `heroService` parameter to the singleton instance of `HeroService`.
+当 Angular 创建 `HeroesComponent` 时,[依赖注入](guide/dependency-injection)系统就会把这个 `heroService` 参数设置为 `HeroService` 的单例对象。
+
### Add _getHeroes()_
+### 添加 _getHeroes()_
+
Create a function to retrieve the heroes from the service.
+创建一个函数,以从服务中获取这些英雄数据。
+
@@ -170,31 +265,51 @@ Create a function to retrieve the heroes from the service.
### Call it in `ngOnInit`
+### 在 `ngOnInit` 中调用它
+
While you could call `getHeroes()` in the constructor, that's not the best practice.
+你固然可以在构造函数中调用 `getHeroes()`,但那不是最佳实践。
+
Reserve the constructor for simple initialization such as wiring constructor parameters to properties.
The constructor shouldn't _do anything_.
It certainly shouldn't call a function that makes HTTP requests to a remote server as a _real_ data service would.
+让构造函数保持简单,只做初始化操作,比如把构造函数的参数赋值给属性。
+构造函数不应该*做任何事*。
+它肯定不能调用某个函数来向远端服务(比如真实的数据服务)发起 HTTP 请求。
+
Instead, call `getHeroes()` inside the [*ngOnInit lifecycle hook*](guide/lifecycle-hooks) and
let Angular call `ngOnInit` at an appropriate time _after_ constructing a `HeroesComponent` instance.
+你应该改为在 [*ngOnInit 生命周期钩子*](guide/lifecycle-hooks)中调用 `getHeroes()`,并且等 Angular 构造出 `HeroesComponent` 的实例之后,找个恰当的时机调用 `ngOnInit`。
+
### See it run
+### 查看运行效果
+
After the browser refreshes, the app should run as before,
showing a list of heroes and a hero detail view when you click on a hero name.
+刷新浏览器,该应用仍运行的一如既往。
+显示英雄列表,并且当你点击某个英雄的名字时显示出英雄详情视图。
+
## Observable data
+## 可观察(Observable)的数据
+
The `HeroService.getHeroes()` method has a _synchronous signature_,
which implies that the `HeroService` can fetch heroes synchronously.
The `HeroesComponent` consumes the `getHeroes()` result
as if heroes could be fetched synchronously.
+`HeroService.getHeroes()` 的函数签名是*同步的*,它所隐含的假设是 `HeroService` 总是能同步获取英雄列表数据。
+而 `HeroesComponent` 也同样假设能同步取到 `getHeroes()` 的结果。
+
@@ -204,27 +319,50 @@ You're getting away with it now because the service currently returns _mock hero
But soon the app will fetch heroes from a remote server,
which is an inherently _asynchronous_ operation.
+这在真实的应用中几乎是不可能的。
+现在能这么做,只是因为目前该服务返回的是*模拟数据*。
+不过很快,该应用就要从远端服务器获取英雄数据了,而那天生就是*异步*操作。
+
The `HeroService` must wait for the server to respond,
`getHeroes()` cannot return immediately with hero data,
and the browser will not block while the service waits.
+`HeroService` 必须等服务器给出相应,
+而 `getHeroes()` 不能立即返回英雄数据,
+浏览器也不会在该服务等待期间停止响应。
+
`HeroService.getHeroes()` must have an _asynchronous signature_ of some kind.
+`HeroService.getHeroes()` 必须具有某种形式的*异步函数签名*。
+
It can take a callback. It could return a `Promise`. It could return an `Observable`.
+它可以使用回调函数,可以返回 `Promise`(承诺),也可以返回 `Observable`(可观察对象)。
+
In this tutorial, `HeroService.getHeroes()` will return an `Observable`
in part because it will eventually use the Angular `HttpClient.get` method to fetch the heroes
and [`HttpClient.get()` returns an `Observable`](guide/http).
+这节课,`HeroService.getHeroes()` 将会返回 `Observable`,因为它最终会使用 Angular 的 `HttpClient.get` 方法来获取英雄数据,而 [`HttpClient.get()` 会返回 `Observable`](guide/http)。
+
### Observable _HeroService_
+### 可观察对象版本的 `HeroService`
+
`Observable` is one of the key classes in the [RxJS library](http://reactivex.io/rxjs/).
+`Observable` 是 [RxJS 库](http://reactivex.io/rxjs/)中的一个关键类。
+
In a [later tutorial on HTTP](tutorial/toh-pt6), you'll learn that Angular's `HttpClient` methods return RxJS `Observable`s.
In this tutorial, you'll simulate getting data from the server with the RxJS `of()` function.
+在[稍后的 HTTP 教程](tutorial/toh-pt6)中,你就会知道 Angular `HttpClient` 的方法会返回 RxJS 的 `Observable`。
+这节课,你将使用 RxJS 的 `of()` 函数来模拟从服务器返回数据。
+
Open the `HeroService` file and import the `Observable` and `of` symbols from RxJS.
+打开 `HeroService` 文件,并从 RxJS 中导入 `Observable` 和 `of` 符号。
+
@@ -232,26 +370,41 @@ title="src/app/hero.service.ts (Observable imports)" region="import-observable">
Replace the `getHeroes` method with this one.
+把 `getHeroes` 方法改成这样:
+
`of(HEROES)` returns an `Observable` that emits _a single value_, the array of mock heroes.
+`of(HEROES)` 会返回一个 `Observable`,它会发出单个值,这个值就是这些模拟英雄的数组。
+
In the [HTTP tutorial](tutorial/toh-pt6), you'll call `HttpClient.get()` which also returns an `Observable` that emits _a single value_, an array of heroes from the body of the HTTP response.
+在 [HTTP 教程](tutorial/toh-pt6)中,你将会调用 `HttpClient.get()` 它也同样返回一个 `Observable`,它也会发出单个值,这个值就是来自 HTTP 响应体中的英雄数组。
+
### Subscribe in _HeroesComponent_
+### 在 `HeroesComponent` 中订阅
+
The `HeroService.getHeroes` method used to return a `Hero[]`.
Now it returns an `Observable`.
+`HeroService.getHeroes` 方法用于返回一个 `Hero[]`,
+目前它返回的是 `Observable`。
+
You'll have to adjust to that difference in `HeroesComponent`.
+你必须在 `HeroesComponent` 中也向本服务中的这种形式看齐。
+
Find the `getHeroes` method and replace it with the following code
(shown side-by-side with the previous version for comparison)
+找到 `getHeroes` 方法,并且把它替换为如下代码(和前一个版本对比显示):
+
ng generate component messages
@@ -304,8 +485,12 @@ Use the CLI to create the `MessagesComponent`.
The CLI creates the component files in the `src/app/messages` folder and declare `MessagesComponent` in `AppModule`.
+CLI 在 `src/app/messages` 中创建了组件文件,并且把 `MessagesComponent` 声明在了 `AppModule` 中。
+
Modify the `AppComponent` template to display the generated `MessagesComponent`
+修改 `AppComponent` 的模板来显示所生成的 `MessagesComponent`:
+
@@ -314,11 +499,18 @@ Modify the `AppComponent` template to display the generated `MessagesComponent`
You should see the default paragraph from `MessagesComponent` at the bottom of the page.
+你可以在页面的底部看到来自的 `MessagesComponent` 的默认内容。
+
### Create the _MessageService_
+### 创建 `MessageService`
+
Use the CLI to create the `MessageService` in `src/app`.
The `--module=app` option tells the CLI to [_provide_ this service](#provide) in the `AppModule`,
+使用 CLI 在 `src/app` 中创建 `MessageService`。
+`--module=app` 选项告诉 CLI 在 `AppModule` 中[提供这个服务](#provide)。
+
ng generate service message --module=app
@@ -327,6 +519,8 @@ The `--module=app` option tells the CLI to [_provide_ this service](#provide) i
Open `MessageService` and replace its contents with the following.
+打开 `MessageService`,并把它的内容改成这样:
+
@@ -335,12 +529,18 @@ Open `MessageService` and replace its contents with the following.
The service exposes its cache of `messages` and two methods: one to `add()` a message to the cache and another to `clear()` the cache.
+该服务对外暴露了它的 `messages` 缓存,以及两个方法:`add()` 方法往缓存中添加一条消息,`clear()` 方法用于清空缓存。
+
{@a inject-message-service}
### Inject it into the `HeroService`
+### 把它注入到 `HeroService` 中
+
Re-open the `HeroService` and import the `MessageService`.
+重新打开 `HeroService`,并且导入 `MessageService`。
+
@@ -351,6 +551,9 @@ Modify the constructor with a parameter that declares a private `messageService`
Angular will inject the singleton `MessageService` into that property
when it creates the `HeroService`.
+修改这个构造函数,添加一个私有的 `messageService` 属性参数。
+Angular 将会在创建 `HeroService` 时把 `MessageService` 的单例注入到这个属性中。
+
@@ -361,23 +564,37 @@ when it creates the `HeroService`.
This is a typical "*service-in-service*" scenario:
you inject the `MessageService` into the `HeroService` which is injected into the `HeroesComponent`.
+这是一个典型的“服务中的服务”场景:
+你把 `MessageService` 注入到了 `HeroService` 中,而 `HeroService` 又被注入到了 `HeroesComponent` 中。
+
### Send a message from `HeroService`
+### 从 `HeroService` 中发送一条消息
+
Modify the `getHeroes` method to send a message when the heroes are fetched.
+修改 `getHeroes` 方法,在获取到英雄数组时发送一条消息。
+
### Display the message from `HeroService`
+### 从 `HeroService` 中显示消息
+
The `MessagesComponent` should display all messages,
including the message sent by the `HeroService` when it fetches heroes.
+`MessagesComponent` 可以显示所有消息,
+包括当 `HeroService` 获取到英雄数据时发送的那条。
+
Open `MessagesComponent` and import the `MessageService`.
+打开 `MessagesComponent`,并且导入 `MessageService`。
+
@@ -386,7 +603,10 @@ Open `MessagesComponent` and import the `MessageService`.
Modify the constructor with a parameter that declares a **public** `messageService` property.
Angular will inject the singleton `MessageService` into that property
-when it creates the `HeroService`.
+when it creates the `MessagesComponent`.
+
+修改构造函数,添加一个 **public** 的 `messageService` 属性。
+Angular 将会在创建 `MessagesComponent` 的实例时 把 `MessageService` 的实例注入到这个属性中。
@@ -395,16 +615,24 @@ when it creates the `HeroService`.
The `messageService` property **must be public** because you're about to bind to it in the template.
+这个 `messageService` 属性必须是公开的,因为你将会在模板中绑定到它。
+
Angular only binds to _public_ component properties.
+Angular 只会绑定到组件的*公开*属性。
+
### Bind to the _MessageService_
+### 绑定到 `MessageService`
+
Replace the CLI-generated `MessagesComponent` template with the following.
+把 CLI 生成的 `MessagesComponent` 的模板改成这样:
+
@@ -413,26 +641,44 @@ Replace the CLI-generated `MessagesComponent` template with the following.
This template binds directly to the component's `messageService`.
+这个模板直接绑定到了组件的 `messageService` 属性上。
+
* The `*ngIf` only displays the messages area if there are messages to show.
+ `*ngIf` 只有当在有消息时才会显示消息区。
+
* An `*ngFor` presents the list of messages in repeated `
` elements.
+ `*ngFor` 用来在一系列 `
` 元素中展示消息列表。
+
* An Angular [event binding](guide/template-syntax#event-binding) binds the button's click event
to `MessageService.clear()`.
+ Angular 的[事件绑定](guide/template-syntax#event-binding)把按钮的`click`事件绑定到了`MessageService.clear()`。
+
The messages will look better when you add the private CSS styles to `messages.component.css`
as listed in one of the ["final code review"](#final-code-review) tabs below.
+当你把 [最终代码](#final-code-review) 某一页的内容添加到 `messages.component.css` 中时,这些消息会变得好看一些。
+
The browser refreshes and the page displays the list of heroes.
Scroll to the bottom to see the message from the `HeroService` in the message area.
Click the "clear" button and the message area disappears.
+刷新浏览器,页面显示出了英雄列表。
+滚动到底部,就会在消息区看到来自 `HeroService` 的消息。
+点击“清空”按钮,消息区不见了。
+
{@a final-code-review}
## Final code review
+## 查看最终代码
+
Here are the code files discussed on this page and your app should look like this .
+你的应用应该变成了这样 。本页所提及的代码文件如下:
+
`).
+ 你使用 RxJS 的 `of()` 方法返回了一个模拟英雄数据的*可观察对象* (`Observable`)。
+
* The component's `ngOnInit` lifecycle hook calls the `HeroService` method, not the constructor.
+ 在组件的 `ngOnInit` 生命周期钩子中调用 `HeroService` 方法,而不是构造函数中。
+
* You created a `MessageService` for loosely-coupled communication between classes.
+ 你创建了一个 `MessageService`,以便在类之间实现松耦合通讯。
+
* The `HeroService` injected into a component is created with another injected service,
`MessageService`.
+
+ `HeroService` 连同注入到它的服务 `MessageService` 一起,注入到了组件中。