Add some blank lines, to avoid unnecessary conflicts on merging.
This commit is contained in:
parent
c1b8183c38
commit
20c59f9e4a
@ -32,7 +32,7 @@
|
||||
},
|
||||
"toh-pt6": {
|
||||
"title": "Http",
|
||||
"intro": "We convert our service and components to use Http",
|
||||
"intro": "把我们的服务和组件改用Http实现",
|
||||
"nextable": true
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ include ../_util-fns
|
||||
// #docregion intro
|
||||
:marked
|
||||
# Tour of Heroes: the vision
|
||||
|
||||
# 《英雄指南》目标概览
|
||||
|
||||
Our grand plan is to build an app to help a staffing agency manage its stable of heroes.
|
||||
@ -53,6 +54,7 @@ p 运行这部分的#[+liveExampleLink2('在线例子', 'toh-6')]。
|
||||
.l-main-section
|
||||
:marked
|
||||
## The End Game
|
||||
|
||||
## 游戏的终点
|
||||
|
||||
Here's a visual idea of where we're going in this tour, beginning with the "Dashboard"
|
||||
@ -118,6 +120,7 @@ figure.image-display
|
||||
.l-main-section
|
||||
:marked
|
||||
## Up Next
|
||||
|
||||
## 接下来
|
||||
|
||||
We’ll build this Tour of Heroes together, step by step.
|
||||
|
@ -2,6 +2,7 @@ include ../_util-fns
|
||||
|
||||
:marked
|
||||
# Once Upon a Time
|
||||
|
||||
# 很久很久以前
|
||||
|
||||
Every story starts somewhere. Our story starts where the [QuickStart](../quickstart.html) ends.
|
||||
@ -41,7 +42,9 @@ include ../_quickstart_repo
|
||||
.file typings.json
|
||||
:marked
|
||||
## Keep the app transpiling and running
|
||||
|
||||
## 保持应用不断转译和运行
|
||||
|
||||
We want to start the TypeScript compiler, have it watch for changes, and start our server. We'll do this by typing
|
||||
|
||||
我们要启动TypeScript编译器,它会监视文件变更,并且启动开发服务器。我们只要敲:
|
||||
@ -58,7 +61,9 @@ code-example(language="bash").
|
||||
.l-main-section
|
||||
:marked
|
||||
## Show our Hero
|
||||
|
||||
## 显示我们的英雄
|
||||
|
||||
We want to display Hero data in our app
|
||||
|
||||
我们要在应用中显示英雄数据
|
||||
@ -92,8 +97,10 @@ code-example(language="bash").
|
||||
Learn more about interpolation in the [Displaying Data chapter](../guide/displaying-data.html).
|
||||
|
||||
要了解插值表达式的更多知识,参阅[“显示数据”一章](../guide/displaying-data.html)。
|
||||
|
||||
:marked
|
||||
### Hero object
|
||||
|
||||
### Hero对象
|
||||
|
||||
At the moment, our hero is just a name. Our hero needs more properties.
|
||||
@ -132,7 +139,9 @@ code-example(language="bash").
|
||||
浏览器自动刷新,并继续显示这位英雄的名字。
|
||||
|
||||
### Adding more HTML
|
||||
|
||||
### 添加更多的HTML
|
||||
|
||||
Displaying a name is good, but we want to see all of our hero’s properties.
|
||||
We’ll add a `<div>` for our hero’s `id` property and another `<div>` for our hero’s `name`.
|
||||
|
||||
@ -146,6 +155,7 @@ code-example(language="bash").
|
||||
啊哦!我们的模板字符串已经太长了。我们最好小心点,免得在模板中出现拼写错误。
|
||||
|
||||
### Multi-line template strings
|
||||
|
||||
### 多行模板字符串
|
||||
|
||||
We could make a more readable template with string concatenation
|
||||
@ -166,7 +176,9 @@ code-example(language="bash").
|
||||
|
||||
.callout.is-important
|
||||
header A back-tick is not a single quote
|
||||
|
||||
header 反引号不是单引号
|
||||
|
||||
:marked
|
||||
**Be careful!** A back-tick (`) looks a lot like a single quote (').
|
||||
It's actually a completely different character.
|
||||
@ -183,6 +195,7 @@ code-example(language="bash").
|
||||
.l-main-section
|
||||
:marked
|
||||
## Editing Our Hero
|
||||
|
||||
## 编辑我们的英雄
|
||||
|
||||
We want to be able to edit the hero name in a textbox.
|
||||
@ -194,6 +207,7 @@ code-example(language="bash").
|
||||
把英雄的名字从单纯的`<label>`重构成`<label>`和`<input>`元素的组合,就像下面这样:
|
||||
|
||||
+makeExample('toh-1/ts-snippets/app.component.snippets.pt1.ts', 'editing-Hero', 'app.component.ts (input元素)')
|
||||
|
||||
:marked
|
||||
We see in the browser that the hero’s name does appear in the `<input>` textbox.
|
||||
But something doesn’t feel right.
|
||||
@ -205,6 +219,7 @@ code-example(language="bash").
|
||||
当修改名字时,我们的改动并没有反映到`<h2>`中。使用单向数据绑定,我们没法实现所期望的这种行为。
|
||||
|
||||
### Two-Way Binding
|
||||
|
||||
### 双向绑定
|
||||
|
||||
We intend to display the name of the hero in the `<input>`, change it,
|
||||
@ -226,6 +241,7 @@ code-example(language="bash").
|
||||
|
||||
要学习关于`ngModel`的更多知识,参见[表单](../guide/forms.html#ngModel)和
|
||||
[模板语法](../guide/template-syntax.html#ngModel)两章
|
||||
|
||||
:marked
|
||||
Replace the `<input>` with the following HTML
|
||||
|
||||
@ -243,20 +259,29 @@ code-example(language="html").
|
||||
.l-main-section
|
||||
:marked
|
||||
## The Road We’ve Travelled
|
||||
|
||||
## 我们已经走过的路
|
||||
|
||||
Let’s take stock of what we’ve built.
|
||||
|
||||
我们来盘点一下已经构建完成的部分。
|
||||
|
||||
* Our Tour of Heroes uses the double curly braces of interpolation (a form of one-way data binding)
|
||||
to display the application title and properties of a `Hero` object.
|
||||
|
||||
* 我们的《英雄指南》使用双大括号(插值表达式 —— 单向数据绑定的一种形式)来显示应用的标题和`Hero`对象的属性。
|
||||
|
||||
* We wrote a multi-line template using ES2015’s template strings to make our template readable.
|
||||
|
||||
* 我们使用ES2105的模板字符串写了一个多行模板,来让我们的模板更有可读性。
|
||||
|
||||
* We can both display and change the hero’s name after adding a two-way data binding to the `<input>` element
|
||||
using the built-in `ngModel` directive.
|
||||
|
||||
* 为了同时显示和修改英雄的名字,我们还使用了内建的`ngModel`指令,往`<input>`元素上添加了双向数据绑定。
|
||||
|
||||
* The `ngModel` directive also propagates changes to every other binding of the `hero.name`.
|
||||
|
||||
* 通过`ngModel`指令,这些修改还影响到了每一个对`hero.name`的其它绑定。
|
||||
|
||||
p Run the #[+liveExampleLink2('', 'toh-1')] for this part.
|
||||
@ -273,7 +298,9 @@ p 运行这部分的#[+liveExampleLink2('在线例子', 'toh-1')]。
|
||||
.l-main-section
|
||||
:marked
|
||||
## The Road Ahead
|
||||
|
||||
## 前方的路
|
||||
|
||||
Our Tour of Heroes only displays one hero and we really want to display a list of heroes.
|
||||
We also want to allow the user to select a hero and display their details.
|
||||
We’ll learn more about how to retrieve lists, bind them to the
|
||||
|
@ -2,7 +2,9 @@ include ../_util-fns
|
||||
|
||||
:marked
|
||||
# It Takes Many Heroes
|
||||
|
||||
# 需要多个英雄
|
||||
|
||||
Our story needs more heroes.
|
||||
We’ll expand our Tour of Heroes app to display a list of heroes,
|
||||
allow the user to select a hero, and display the hero’s details.
|
||||
@ -24,7 +26,9 @@ p 运行这部分的#[+liveExampleLink2('在线例子', 'toh-2')]。
|
||||
.l-main-section
|
||||
:marked
|
||||
## Where We Left Off
|
||||
|
||||
## 我们在哪儿
|
||||
|
||||
Before we continue with Part 2 of the Tour of Heroes,
|
||||
let’s verify we have the following structure after [Part 1](./toh-pt1.html).
|
||||
If not, we’ll need to go back to Part 1 and figure out what we missed.
|
||||
@ -48,7 +52,9 @@ p 运行这部分的#[+liveExampleLink2('在线例子', 'toh-2')]。
|
||||
.file typings.json
|
||||
:marked
|
||||
### Keep the app transpiling and running
|
||||
|
||||
### 让应用代码保持转译和运行
|
||||
|
||||
We want to start the TypeScript compiler, have it watch for changes, and start our server. We'll do this by typing
|
||||
|
||||
我们要启动TypeScript编译器,它会监视文件变更,并启动开发服务器。我们只要敲:
|
||||
@ -64,9 +70,13 @@ code-example(language="bash").
|
||||
.l-main-section
|
||||
:marked
|
||||
## Displaying Our Heroes
|
||||
|
||||
## 显示我们的英雄
|
||||
|
||||
### Creating heroes
|
||||
|
||||
### 创建英雄
|
||||
|
||||
Let’s create an array of ten heroes at the bottom of `app.component.ts`.
|
||||
|
||||
我们先在`app.component.ts`的底部创建一个由十位英雄组成的数组。
|
||||
@ -83,7 +93,9 @@ code-example(language="bash").
|
||||
我们当然希望从一个Web服务中获取这个英雄列表,但别急,我们得把步子迈得小一点儿 —— 先用一组Mock(模拟)出来的英雄。
|
||||
|
||||
### Exposing heroes
|
||||
|
||||
### 导出英雄们
|
||||
|
||||
Let’s create a public property in `AppComponent` that exposes the heroes for binding.
|
||||
|
||||
我们在`AppComponent`上创建一个公开属性,用来暴露这些英雄,以供绑定。
|
||||
@ -94,6 +106,7 @@ code-example(language="bash").
|
||||
We did not have to define the `heroes` type. TypeScript can infer it from the `HEROES` array.
|
||||
|
||||
我们并不需要明确定义`heroes`属性的数据类型,TypeScript能从`HEROES`数组中推断出来。
|
||||
|
||||
.l-sub-section
|
||||
:marked
|
||||
We could have defined the heroes list here in this component class.
|
||||
@ -104,9 +117,12 @@ code-example(language="bash").
|
||||
我们已经把英雄列表定义在了这个组件类中。
|
||||
但显然,我们最终还是得从一个数据服务中获取这些英雄。
|
||||
正因如此,我们从一开始就要有意识的把英雄数据隔离到一个类中来实现。
|
||||
|
||||
:marked
|
||||
### Displaying heroes in a template
|
||||
|
||||
### 在一个模板中显示英雄
|
||||
|
||||
Our component has `heroes`. Let’s create an unordered list in our template to display them.
|
||||
We’ll insert the following chunk of HTML below the title and above the hero details.
|
||||
|
||||
@ -121,6 +137,7 @@ code-example(language="bash").
|
||||
现在,我们有了一个模板。接下来,就用英雄们的数据来填充它。
|
||||
|
||||
### Listing heroes with ngFor
|
||||
|
||||
### 通过ngFor来显示英雄列表
|
||||
|
||||
We want to bind the array of `heroes` in our component to our template, iterate over them,
|
||||
@ -187,7 +204,9 @@ code-example(language="bash").
|
||||
当浏览器刷新时,我们就看到了英雄列表。
|
||||
|
||||
### Styling our heroes
|
||||
|
||||
### 给我们的英雄们“美容”
|
||||
|
||||
Our list of heroes looks pretty bland.
|
||||
We want to make it visually obvious to a user which hero we are hovering over and which hero is selected.
|
||||
|
||||
@ -224,7 +243,9 @@ code-example(language="bash").
|
||||
.l-main-section
|
||||
:marked
|
||||
## Selecting a Hero
|
||||
|
||||
## 选择英雄
|
||||
|
||||
We have a list of heroes and we have a single hero displayed in our app.
|
||||
The list and the single hero are not connected in any way.
|
||||
We want the user to select a hero from our list, and have the selected hero appear in the details view.
|
||||
@ -242,7 +263,9 @@ code-example(language="bash").
|
||||
我们通过组件中的一个`selectedHero`属性来连接主从视图。它被绑定到了click事件上。
|
||||
|
||||
### Click event
|
||||
|
||||
### click事件
|
||||
|
||||
We modify the `<li>` by inserting an Angular event binding to its click event.
|
||||
|
||||
我们往`<li>`元素上插入一句Angular事件绑定代码,绑定到它的click事件。
|
||||
@ -253,8 +276,10 @@ code-example(language="bash").
|
||||
Focus on the event binding
|
||||
|
||||
事件绑定详解
|
||||
|
||||
code-example(format="." language="bash").
|
||||
(click)="onSelect(hero)"
|
||||
|
||||
:marked
|
||||
The parenthesis identify the `<li>` element’s `click` event as the target.
|
||||
The expression to the right of the equal sign calls the `AppComponent` method, `onSelect()`,
|
||||
@ -277,7 +302,9 @@ code-example(language="bash").
|
||||
|
||||
:marked
|
||||
### Add the click handler
|
||||
|
||||
### 添加click事件处理器
|
||||
|
||||
Our event binding refers to an `onSelect` method that doesn’t exist yet.
|
||||
We’ll add that method to our component now.
|
||||
|
||||
@ -293,6 +320,7 @@ code-example(language="bash").
|
||||
我们的组件还没有用来表示“当前选中的英雄”的变量,我们就从这一步儿开始。
|
||||
|
||||
### Expose the selected hero
|
||||
|
||||
### 导出“当前选中的英雄”
|
||||
|
||||
We no longer need the static `hero` property of the `AppComponent`.
|
||||
@ -324,8 +352,10 @@ code-example(language="bash").
|
||||
我们这就修改模板,让它绑定到新的`selectedHero`属性上去。
|
||||
|
||||
+makeExample('toh-2/ts-snippets/app.component.snippets.pt2.ts', 'selectedHero-details', 'app.component.ts (template excerpt)')
|
||||
|
||||
:marked
|
||||
### Hide the empty detail with ngIf
|
||||
|
||||
### 利用ngIf隐藏空的详情
|
||||
|
||||
When our app loads we see a list of heroes, but a hero is not selected.
|
||||
@ -335,6 +365,7 @@ code-example(language="bash").
|
||||
当应用刚加载时,我们会看到一个英雄列表,但还没有任何英雄被选中。
|
||||
`selectedHero`属性是`undefined`。
|
||||
因此,我们会看到浏览器控制台中出现下列错误:
|
||||
|
||||
code-example(language="html").
|
||||
EXCEPTION: TypeError: Cannot read property 'name' of undefined in [null]
|
||||
|
||||
@ -402,6 +433,7 @@ code-example(language="bash").
|
||||
正如我们所预期的那样。
|
||||
|
||||
### Styling the selection
|
||||
|
||||
### 给所选英雄添加样式
|
||||
|
||||
We see the selected hero in the details area below but we can’t quickly locate that hero in the list above.
|
||||
@ -415,6 +447,7 @@ code-example(language="bash").
|
||||
|
||||
figure.image-display
|
||||
img(src='/resources/images/devguide/toh/heroes-list-selected.png' alt="选中的英雄")
|
||||
|
||||
:marked
|
||||
We’ll add a property binding on `class` for the `selected` class to the template. We'll set this to an expression that compares the current `selectedHero` to the `hero`.
|
||||
|
||||
@ -425,7 +458,9 @@ code-example(language="bash").
|
||||
|
||||
关键是CSS类的名字:`selected`。当两位英雄一致时,它为`true`,否则为`false`。
|
||||
也就是说:“*当两位英雄匹配时,应用上`selected`类,否则不应用*”。
|
||||
|
||||
+makeExample('toh-2/ts-snippets/app.component.snippets.pt2.ts', 'class-selected-1', 'app.component.ts (setting the CSS class)')(format=".")
|
||||
|
||||
:marked
|
||||
Notice in the template that the `class.selected` is surrounded in square brackets (`[]`).
|
||||
This is the syntax for a **property binding**, a binding in which data flows one way
|
||||
@ -450,6 +485,7 @@ code-example(language="bash").
|
||||
|
||||
浏览器重新加载了你的应用。
|
||||
我们选中英雄Magneta,于是他通过背景色的变化被清晰的标记了出来。
|
||||
|
||||
figure.image-display
|
||||
img(src='/resources/images/devguide/toh/heroes-list-1.png' alt="英雄列表应用的输出")
|
||||
|
||||
@ -467,16 +503,23 @@ code-example(language="bash").
|
||||
.l-main-section
|
||||
:marked
|
||||
## The Road We’ve Travelled
|
||||
|
||||
## 已走的路
|
||||
|
||||
Here’s what we achieved in this chapter:
|
||||
|
||||
在本章中,我们达成了这些:
|
||||
|
||||
* Our Tour of Heroes now displays a list of selectable heroes
|
||||
|
||||
* 我们的《英雄指南》现在显示一个可选英雄的列表
|
||||
|
||||
* We added the ability to select a hero and show the hero’s details
|
||||
|
||||
* 我们添加了选择英雄的能力,并且会显示这个英雄的详情
|
||||
|
||||
* We learned how to use the built-in directives `ngIf` and `ngFor` in a component’s template
|
||||
|
||||
* 我们学会了如何在组件模板中使用内建的`ngIf`和`ngFor`指令
|
||||
|
||||
p Run the #[+liveExampleLink2('', 'toh-2')] for this part.
|
||||
@ -485,7 +528,9 @@ p 运行这部分的#[+liveExampleLink2('在线例子', 'toh-2')]。
|
||||
|
||||
:marked
|
||||
### The Road Ahead
|
||||
|
||||
### 前方的路
|
||||
|
||||
Our Tour of Heroes has grown, but it’s far from complete.
|
||||
We can't put the entire app into a single component.
|
||||
We need to break it up into sub-components and teach them to work together
|
||||
|
@ -14,7 +14,9 @@ p 运行这部分的#[+liveExampleLink2('在线例子', 'toh-3')]。
|
||||
.l-main-section
|
||||
:marked
|
||||
## Where We Left Off
|
||||
|
||||
## 我们在哪儿
|
||||
|
||||
Before we continue with our Tour of Heroes, let’s verify we have the following structure. If not, we’ll need to go back and follow the previous chapters.
|
||||
|
||||
在继续《英雄指南》之前,先来检查一下,你是否已经有了如下目录结构。如果没有,你得先回上一章,看看错过了哪里。
|
||||
@ -34,9 +36,12 @@ p 运行这部分的#[+liveExampleLink2('在线例子', 'toh-3')]。
|
||||
.file systemjs.config.js
|
||||
.file tsconfig.json
|
||||
.file typings.json
|
||||
|
||||
:marked
|
||||
### Keep the app transpiling and running
|
||||
|
||||
### 让应用代码保持转译和运行
|
||||
|
||||
We want to start the TypeScript compiler, have it watch for changes, and start our server. We'll do this by typing
|
||||
|
||||
我们要启动TypeScript编译器,它会监视文件变更,并启动开发服务器。只要敲:
|
||||
@ -50,7 +55,9 @@ code-example(language="bash").
|
||||
这个命令会在我们构建《英雄指南》的时候让应用得以持续运行。
|
||||
|
||||
## Making a Hero Detail Component
|
||||
|
||||
## 制作英雄详情组件
|
||||
|
||||
Our heroes list and our hero details are in the same component in the same file.
|
||||
They're small now but each could grow.
|
||||
We are sure to receive new requirements for one and not the other.
|
||||
@ -77,16 +84,21 @@ code-example(language="bash").
|
||||
我们来把英雄详情拆分成一个独立的组件。
|
||||
|
||||
### Separating the Hero Detail Component
|
||||
|
||||
### 拆分英雄详情组件
|
||||
|
||||
Add a new file named `hero-detail.component.ts` to the `app` folder and create `HeroDetailComponent` as follows.
|
||||
|
||||
在`app`目录下添加一个名叫`hero-detail.component.ts`的文件,并且创建`HeroDetailComponent`。代码如下:
|
||||
|
||||
+makeExample('toh-3/ts/app/hero-detail.component.ts', 'v1', 'app/hero-detail.component.ts (initial version)')(format=".")
|
||||
|
||||
.l-sub-section
|
||||
:marked
|
||||
### Naming conventions
|
||||
|
||||
### 命名约定
|
||||
|
||||
We like to identify at a glance which classes are components and which files contain components.
|
||||
|
||||
我们希望一眼就能看出哪个类是组件,以及哪个文件包含组件。
|
||||
@ -128,9 +140,12 @@ code-example(language="bash").
|
||||
When we finish here, we'll import it into `AppComponent` and create a corresponding `<my-hero-detail>` element.
|
||||
|
||||
做完这些,我们把它导入`AppComponent`组件,并创建相应的`<my-hero-detail>`元素。
|
||||
|
||||
:marked
|
||||
#### Hero Detail Template
|
||||
|
||||
#### 英雄详情模板
|
||||
|
||||
At the moment, the *Heroes* and *Hero Detail* views are combined in one template in `AppComponent`.
|
||||
Let’s **cut** the *Hero Detail* content from `AppComponent` and **paste** it into the new template property of `HeroDetailComponent`.
|
||||
|
||||
@ -155,11 +170,15 @@ code-example(language="bash").
|
||||
现在,我们的英雄详情布局只存在于`HeroDetailComponent`组件中了。
|
||||
|
||||
#### Add the *hero* property
|
||||
|
||||
#### 添加 *hero* 属性
|
||||
|
||||
Let’s add that `hero` property we were talking about to the component class.
|
||||
|
||||
我们这就添加刚刚所说的`hero`属性到组件类中。
|
||||
|
||||
+makeExample('toh-3/ts/app/hero-detail.component.ts', 'hero')
|
||||
|
||||
:marked
|
||||
Uh oh. We declared the `hero` property as type `Hero` but our `Hero` class is over in the `app.component.ts` file.
|
||||
We have two components, each in their own file, that need to reference the `Hero` class.
|
||||
@ -184,6 +203,7 @@ code-example(language="bash").
|
||||
|
||||
:marked
|
||||
#### The *hero* property is an ***input***
|
||||
|
||||
#### *hero*是一个***输入***属性
|
||||
|
||||
The `HeroDetailComponent` must be told what hero to display. Who will tell it? The parent `AppComponent`!
|
||||
@ -201,8 +221,10 @@ code-example(language="bash").
|
||||
|
||||
我们马上升级`AppComponent`的模板,以便把该组件的`selectedHero`属性绑定到`HeroDetailComponent`组件的`hero`属性上。
|
||||
绑定看起来*可能*是这样的:
|
||||
|
||||
code-example(format=".").
|
||||
<my-hero-detail [hero]="selectedHero"></my-hero-detail>
|
||||
|
||||
:marked
|
||||
Notice that the `hero` property is the ***target*** of a property binding — it's in square brackets to the left of the (=).
|
||||
|
||||
@ -212,6 +234,7 @@ code-example(format=".").
|
||||
If we don't, Angular rejects the binding and throws an error.
|
||||
|
||||
Angular希望我们把***目标属性***定义成组件的***输入属性***,否则,Angular会拒绝绑定,并且抛出一个错误。
|
||||
|
||||
.l-sub-section
|
||||
:marked
|
||||
We explain input properties in more detail [here](../guide/attribute-directives.html#why-input)
|
||||
@ -219,12 +242,14 @@ code-example(format=".").
|
||||
*source* properties do not.
|
||||
|
||||
我们在[这里](../guide/attribute-directives.html#why-input)详细解释了输入属性,以及为什么*目标属性*需要“显式定义”这样的特殊待遇,而*来源属性*却不需要。
|
||||
|
||||
:marked
|
||||
There are a couple of ways we can declare that `hero` is an *input*.
|
||||
We'll do it the way we *prefer*, by annotating the `hero` property with the `@Input` decorator that we imported earlier.
|
||||
|
||||
我们有几种方式把`hero`声明成*输入属性*。
|
||||
这里我们采用*首选*的方式:使用我们前面导入的`@Input`装饰器,为`hero`属性加上注解。
|
||||
|
||||
+makeExample('toh-3/ts/app/hero-detail.component.ts', 'hero-input')(format='.')
|
||||
|
||||
.l-sub-section
|
||||
@ -233,10 +258,13 @@ code-example(format=".").
|
||||
[Attribute Directives](../guide/attribute-directives.html#input) chapter.
|
||||
|
||||
要了解`@Input()`装饰器的更多知识,参见[属性型指令](../guide/attribute-directives.html#input)一章。
|
||||
|
||||
.l-main-section
|
||||
:marked
|
||||
## Refresh the AppComponent
|
||||
|
||||
## 刷新AppComponent
|
||||
|
||||
We return to the `AppComponent` and teach it to use the `HeroDetailComponent`.
|
||||
|
||||
回到`AppComponent`组件,我们要教它使用`HeroDetailComponent`组件。
|
||||
@ -244,6 +272,7 @@ code-example(format=".").
|
||||
We begin by importing the `HeroDetailComponent` so we can refer to it.
|
||||
|
||||
我们先导入`HeroDetailComponent`组件,好让我们可以引用它。
|
||||
|
||||
+makeExample('toh-3/ts/app/app.component.ts', 'hero-detail-import')
|
||||
|
||||
:marked
|
||||
@ -251,18 +280,22 @@ code-example(format=".").
|
||||
and add an element tag that represents the `HeroDetailComponent`.
|
||||
|
||||
找到我们刚刚从模板中移除*英雄详情*的地方,放上用来表示`HeroDetailComponent`组件的HTML标签。
|
||||
|
||||
code-example(format=".").
|
||||
<my-hero-detail></my-hero-detail>
|
||||
|
||||
.l-sub-section
|
||||
:marked
|
||||
*my-hero-detail* is the name we set as the `selector` in the `HeroDetailComponent` metadata.
|
||||
|
||||
*my-hero-detail*是我们在`HeroDetailComponent`元数据中的`selector`属性所指定的名字。
|
||||
|
||||
:marked
|
||||
The two components won't coordinate until we bind the `selectedHero` property of the `AppComponent`
|
||||
to the `HeroDetailComponent` element's `hero` property like this:
|
||||
|
||||
这两个组件目前还不能协同工作,直到我们把`AppComponent`组件的`selectedHero`属性和`HeroDetailComponent`组件的`hero`属性绑定在一起,就像这样:
|
||||
|
||||
code-example(format=".").
|
||||
<my-hero-detail [hero]="selectedHero"></my-hero-detail>
|
||||
:marked
|
||||
@ -304,11 +337,14 @@ code-example(format=".").
|
||||
|
||||
我们要把它列在元数据的`directives`数组中,这样Angular才会知道它。
|
||||
让我们把这个数组属性加在`@Component`配置对象的底部,紧跟在`template`和`styles`属性之后。
|
||||
|
||||
+makeExample('toh-3/ts/app/app.component.ts', 'directives', 'app/app.component.ts (Directives)')
|
||||
|
||||
:marked
|
||||
### It works!
|
||||
|
||||
### 搞定!
|
||||
|
||||
When we view our app in the browser we see the list of heroes.
|
||||
When we select a hero we can see the selected hero’s details.
|
||||
|
||||
@ -325,7 +361,9 @@ code-example(format=".").
|
||||
我们创建了第一个可复用组件!
|
||||
|
||||
### Reviewing the App Structure
|
||||
|
||||
### 回顾应用结构
|
||||
|
||||
Let’s verify that we have the following structure after all of our good refactoring in this chapter:
|
||||
|
||||
来验证下吧,在本章中,经过这些漂亮的重构,我们应该得到了下列结构:
|
||||
@ -345,6 +383,7 @@ code-example(format=".").
|
||||
.file package.json
|
||||
.file tsconfig.json
|
||||
.file typings.json
|
||||
|
||||
:marked
|
||||
Here are the code files we discussed in this chapter.
|
||||
|
||||
@ -363,18 +402,27 @@ code-example(format=".").
|
||||
.l-main-section
|
||||
:marked
|
||||
## The Road We’ve Travelled
|
||||
|
||||
## 走过的路
|
||||
|
||||
Let’s take stock of what we’ve built.
|
||||
|
||||
来盘点一下我们已经构建完的部分。
|
||||
|
||||
* We created a reusable component
|
||||
|
||||
* 我们创建了一个可复用组件
|
||||
|
||||
* We learned how to make a component accept input
|
||||
|
||||
* 我们学会了如何让一个组件接收输入
|
||||
|
||||
* We learned to bind a parent component to a child component.
|
||||
|
||||
* 我们学会了把父组件绑定到子组件。
|
||||
|
||||
* We learned to declare the application directives we need in a `directives` array.
|
||||
|
||||
* 我们学会了在`directives`中定义应用所需的指令。
|
||||
|
||||
p Run the #[+liveExampleLink2('', 'toh-3')] for this part.
|
||||
@ -384,7 +432,9 @@ p 运行这部分的#[+liveExampleLink2('在线例子', 'toh-3')]。
|
||||
.l-main-section
|
||||
:marked
|
||||
## The Road Ahead
|
||||
|
||||
## 前方的路
|
||||
|
||||
Our Tour of Heroes has become more reusable with shared components.
|
||||
|
||||
通过抽取共享组件,我们的《英雄指南》变得更有复用性了。
|
||||
|
@ -2,7 +2,9 @@ include ../_util-fns
|
||||
|
||||
:marked
|
||||
# Services
|
||||
|
||||
# 服务
|
||||
|
||||
The Tour of Heroes is evolving and we anticipate adding more components in the near future.
|
||||
|
||||
《英雄指南》继续前行。接下来,我们准备添加更多的组件。
|
||||
@ -33,7 +35,9 @@ p 运行这部分的#[+liveExampleLink2('在线例子', 'toh-4')]。
|
||||
.l-main-section
|
||||
:marked
|
||||
## Where We Left Off
|
||||
|
||||
## 我们在哪儿
|
||||
|
||||
Before we continue with our Tour of Heroes, let’s verify we have the following structure.
|
||||
If not, we’ll need to go back and follow the previous chapters.
|
||||
|
||||
@ -74,7 +78,9 @@ code-example(language="bash").
|
||||
当我们继续构建《英雄指南》时,应用会自动运行和更新。
|
||||
|
||||
## Creating a Hero Service
|
||||
|
||||
## 创建英雄服务
|
||||
|
||||
Our stakeholders have shared their larger vision for our app.
|
||||
They tell us they want to show the heroes in various ways on different pages.
|
||||
We already can select a hero from a list.
|
||||
@ -101,10 +107,13 @@ code-example(language="bash").
|
||||
我们可以把提供英雄数据的任务重构为一个单独的服务,它将提供英雄数据,并且把这个服务在所有需要英雄数据的组件之间共享。
|
||||
|
||||
### Create the HeroService
|
||||
|
||||
### 创建HeroService
|
||||
|
||||
Create a file in the `app` folder called `hero.service.ts`.
|
||||
|
||||
在`app`目录下创建一个名叫`hero.service.ts`的文件。
|
||||
|
||||
.l-sub-section
|
||||
:marked
|
||||
We've adopted a convention in which we spell the name of a service in lowercase followed by `.service`.
|
||||
@ -114,6 +123,7 @@ code-example(language="bash").
|
||||
我们遵循的文件命名约定是:服务名称的小写形式(基本名),加上`.service`后缀。
|
||||
如果服务名称包含多个单词,我们就把基本名部分写成中线形式(dash-case,也被称作烤串形式kebab-case)。
|
||||
比如,`SpecialSuperHeroService`服务应该被定义在`special-super-hero.service.ts`文件中。
|
||||
|
||||
:marked
|
||||
We name the class `HeroService` and export it for others to import.
|
||||
|
||||
@ -123,15 +133,19 @@ code-example(language="bash").
|
||||
|
||||
:marked
|
||||
### Injectable Services
|
||||
|
||||
### 可注入的服务
|
||||
|
||||
Notice that we imported the Angular `Injectable` function and applied that function as an `@Injectable()` decorator.
|
||||
|
||||
注意,我们引入了Angular的`Injectable`函数,并通过`@Injectable()`装饰器使用这个函数。
|
||||
|
||||
.callout.is-helpful
|
||||
:marked
|
||||
**Don't forget the parentheses!** Neglecting them leads to an error that's difficult to diagnose.
|
||||
|
||||
**不要忘了写圆括号!**如果忘了写,就会导致一个很难诊断的错误。
|
||||
|
||||
:marked
|
||||
TypeScript sees the `@Injectable()` decorator and emits metadata about our service,
|
||||
metadata that Angular may need to inject other dependencies into this service.
|
||||
@ -146,12 +160,15 @@ code-example(language="bash").
|
||||
|
||||
:marked
|
||||
### Getting Heroes
|
||||
|
||||
### 获取英雄
|
||||
|
||||
Add a `getHeroes` method stub.
|
||||
|
||||
添加一个名叫`getHeros`的桩方法。
|
||||
|
||||
+makeExample('toh-4/ts/app/hero.service.1.ts', 'getHeroes-stub', 'app/hero.service.ts (getHeroes stub)')(format=".")
|
||||
|
||||
:marked
|
||||
We're holding back on the implementation for a moment to make an important point.
|
||||
|
||||
@ -174,7 +191,9 @@ code-example(language="bash").
|
||||
这下子,我们可以随时改变数据访问的实现方式了。
|
||||
|
||||
### Mock Heroes
|
||||
|
||||
### Mock英雄数据
|
||||
|
||||
We already have mock `Hero` data sitting in the `AppComponent`. It doesn't belong there. It doesn't belong *here* either.
|
||||
We'll move the mock data to its own file.
|
||||
|
||||
@ -187,6 +206,7 @@ code-example(language="bash").
|
||||
我们还要把`import {Hero}...`语句拷贝过来,因为我们的英雄数组用到了`Hero`类。
|
||||
|
||||
+makeExample('toh-4/ts/app/mock-heroes.ts', null, 'app/mock-heroes.ts')
|
||||
|
||||
:marked
|
||||
We export the `HEROES` constant so we can import it elsewhere — such as our `HeroService`.
|
||||
|
||||
@ -198,9 +218,12 @@ code-example(language="bash").
|
||||
同时,回到刚剪切出`HEROES`数组的`app.component.ts`文件,我们留下了一个尚未初始化的`heroes`属性:
|
||||
|
||||
+makeExample('toh-4/ts/app/app.component.1.ts', 'heroes-prop', 'app/app.component.ts (heroes property)')(format=".")
|
||||
|
||||
:marked
|
||||
### Return Mocked Heroes
|
||||
|
||||
### 返回模拟的英雄数据
|
||||
|
||||
Back in the `HeroService` we import the mock `HEROES` and return it from the `getHeroes` method.
|
||||
Our `HeroService` looks like this:
|
||||
|
||||
@ -208,9 +231,12 @@ code-example(language="bash").
|
||||
我们的`HeroService`服务现在看起来是这样:
|
||||
|
||||
+makeExample('toh-4/ts/app/hero.service.1.ts', null, 'app/hero.service.ts')(format=".")
|
||||
|
||||
:marked
|
||||
### Use the Hero Service
|
||||
|
||||
### 使用HeroService服务
|
||||
|
||||
We're ready to use the `HeroService` in other components starting with our `AppComponent`.
|
||||
|
||||
我们已经在包括`AppComponent`在内的多个组件中使用了`HeroService`服务。
|
||||
@ -220,6 +246,7 @@ code-example(language="bash").
|
||||
通常,我们会从导入要用的东西开始,比如`HeroService`。
|
||||
|
||||
+makeExcerpt('toh-4/ts/app/app.component.ts', 'hero-service-import')
|
||||
|
||||
:marked
|
||||
Importing the service allows us to *reference* it in our code.
|
||||
How should the `AppComponent` acquire a runtime concrete `HeroService` instance?
|
||||
@ -236,6 +263,7 @@ code-example(language="bash").
|
||||
固然,我们可以使用`new`关键字来创建`HeroService`的实例,就像这样:
|
||||
|
||||
+makeExample('toh-4/ts/app/app.component.1.ts', 'new-service')(format=".")
|
||||
|
||||
:marked
|
||||
That's a bad idea for several reasons including
|
||||
|
||||
@ -269,6 +297,7 @@ code-example(language="bash").
|
||||
比如,能离线操作吗?能在测试时使用不同的模拟版本吗?这可不容易。
|
||||
|
||||
* What if ... what if ... Hey, we've got work to do!*
|
||||
|
||||
* 如果……如果……嘿!这下我们可有得忙了!
|
||||
|
||||
We get it. Really we do.
|
||||
@ -277,6 +306,7 @@ code-example(language="bash").
|
||||
有办法了,真的!这个办法真是简单得不可思议,它能解决这些问题,你就再也没有犯错误的借口了。
|
||||
|
||||
### Inject the *HeroService*
|
||||
|
||||
### 注入 *HeroService*
|
||||
|
||||
Two lines replace the one line that created with *new*:
|
||||
@ -301,6 +331,7 @@ code-example(language="bash").
|
||||
defines a private `heroService` property and identifies it as a `HeroService` injection site.
|
||||
|
||||
构造函数自己什么也不用做,它在参数中定义了一个私有的`heroService`属性,并把它标记为注入`HeroService`的靶点。
|
||||
|
||||
:marked
|
||||
Now Angular will know to supply an instance of the `HeroService` when it creates a new `AppComponent`.
|
||||
|
||||
@ -322,6 +353,7 @@ code-example(language="bash").
|
||||
code-example(format="nocode").
|
||||
EXCEPTION: No provider for HeroService! (AppComponent -> HeroService)
|
||||
(异常:没有HeroService的供应商!(AppComponent -> HeroService))
|
||||
|
||||
:marked
|
||||
We have to teach the *injector* how to make a `HeroService` by registering a `HeroService` **provider**.
|
||||
Do that by adding the following `providers` array property to the bottom of the component metadata
|
||||
@ -331,16 +363,20 @@ code-example(format="nocode").
|
||||
要做到这一点,我们应该在`@Component`组件的元数据底部添加`providers`数组属性如下:
|
||||
|
||||
+makeExcerpt('toh-4/ts/app/app.component.1.ts', 'providers')
|
||||
|
||||
:marked
|
||||
The `providers` array tells Angular to create a fresh instance of the `HeroService` when it creates a new `AppComponent`.
|
||||
The `AppComponent` can use that service to get heroes and so can every child component of its component tree.
|
||||
|
||||
`providers`数组告诉Angular,当它创建新的`AppComponent`组件时,也要创建一个`HeroService`的新实例。
|
||||
`AppComponent`会使用那个服务来获取影响列表,在它组件树中的每一个子组件也同样如此。
|
||||
|
||||
a#child-component
|
||||
:marked
|
||||
### *getHeroes* in the *AppComponent*
|
||||
|
||||
### *AppComponent* 中的 *getHeroes*
|
||||
|
||||
We've got the service in a `heroService` private variable. Let's use it.
|
||||
|
||||
我们已经获得了此服务,并把它存入了私有变量`heroService`中。我们这就开始使用它。
|
||||
@ -348,7 +384,9 @@ a#child-component
|
||||
We pause to think. We can call the service and get the data in one line.
|
||||
|
||||
停下来想一想。我们可以在同一行内调用此服务并获得数据。
|
||||
|
||||
+makeExample('toh-4/ts/app/app.component.1.ts', 'get-heroes')(format=".")
|
||||
|
||||
:marked
|
||||
We don't really need a dedicated method to wrap one line. We write it anyway:
|
||||
|
||||
@ -359,7 +397,9 @@ a#child-component
|
||||
<a id="oninit"></a>
|
||||
:marked
|
||||
### The *ngOnInit* Lifecycle Hook
|
||||
|
||||
### *ngOnInit* 生命周期钩子
|
||||
|
||||
`AppComponent` should fetch and display heroes without a fuss.
|
||||
Where do we call the `getHeroes` method? In a constructor? We do *not*!
|
||||
|
||||
@ -392,17 +432,20 @@ a#child-component
|
||||
Each interface has a single method. When the component implements that method, Angular calls it at the appropriate time.
|
||||
|
||||
每个接口都有唯一的一个方法。只要组件实现了这个方法,Angular就会在合适的时机调用它。
|
||||
|
||||
.l-sub-section
|
||||
:marked
|
||||
Learn more about lifecycle hooks in the [Lifecycle Hooks](../guide/lifecycle-hooks.html) chapter.
|
||||
|
||||
要了解关于生命周期钩子的更多知识,请参见 [生命周期钩子](../guide/lifecycle-hooks.html) 一章。
|
||||
|
||||
:marked
|
||||
Here's the essential outline for the `OnInit` interface:
|
||||
|
||||
这是`OnInit`接口的基本轮廓:
|
||||
|
||||
+makeExample('toh-4/ts/app/app.component.1.ts', 'on-init', 'app/app.component.ts (ngOnInit stub)')(format=".")
|
||||
|
||||
:marked
|
||||
We write an `ngOnInit` method with our initialization logic inside and leave it to Angular to call it
|
||||
at the right time. In our case, we initialize by calling `getHeroes`.
|
||||
@ -410,6 +453,7 @@ a#child-component
|
||||
我们写下带有初始化逻辑的`ngOnInit`方法,然后留给Angular,供其在正确的时机调用。在这个例子中,我们通过调用`getHeroes`来完成初始化。
|
||||
|
||||
+makeExcerpt('toh-4/ts/app/app.component.1.ts', 'ng-on-init')
|
||||
|
||||
:marked
|
||||
Our application should be running as expected, showing a list of heroes and a hero detail view
|
||||
when we click on a hero name.
|
||||
@ -432,6 +476,7 @@ a#child-component
|
||||
我们的`HeroService`立即返回一个模拟的英雄列表,它的`getHeroes`函数签名是同步的。
|
||||
|
||||
+makeExample('toh-4/ts/app/app.component.1.ts', 'get-heroes')(format=".")
|
||||
|
||||
:marked
|
||||
Ask for heroes and they are there in the returned result.
|
||||
|
||||
@ -465,17 +510,20 @@ a#child-component
|
||||
**承诺** 就是 …… 好吧,它就是一个承诺 —— 在有了结果时,它承诺会回调我们。
|
||||
我们请求一个异步服务去做点什么,然后给它一个回调函数。
|
||||
它会去做(无论用哪种方式),一旦完成,它就会调用我们的回调函数,并通过参数把工作成果或者错误信息传给我们。
|
||||
|
||||
.l-sub-section
|
||||
:marked
|
||||
We are simplifying. Learn about ES2015 Promises [here](http://exploringjs.com/es6/ch_promises.html) and elsewhere on the web.
|
||||
|
||||
这里只是粗浅的说说,要了解更多,请参见[这里](http://exploringjs.com/es6/ch_promises.html)或在Web上搜索其它学习资源。
|
||||
|
||||
:marked
|
||||
Update the `HeroService` with this !{_Promise}-returning `getHeroes` method:
|
||||
|
||||
把`HeroService`的`getHeroes`方法改写为返回承诺的形式:
|
||||
|
||||
+makeExample('toh-4/ts/app/hero.service.ts', 'get-heroes', 'app/hero.service.ts (excerpt)')(format=".")
|
||||
|
||||
:marked
|
||||
We're still mocking the data. We're simulating the behavior of an ultra-fast, zero-latency server,
|
||||
by returning an **immediately resolved !{_Promise}** with our mock heroes as the result.
|
||||
@ -491,6 +539,7 @@ a#child-component
|
||||
回到`AppComponent`和它的`getHeroes`方法,我们看到它看起来还是这样的:
|
||||
|
||||
+makeExample('toh-4/ts/app/app.component.1.ts', 'getHeroes', 'app/app.component.ts (getHeroes - old)')(format=".")
|
||||
|
||||
:marked
|
||||
As a result of our change to `HeroService`, we're now setting `this.heroes` to a !{_Promise} rather than an array of heroes.
|
||||
|
||||
@ -507,6 +556,7 @@ a#child-component
|
||||
我们把回调函数作为参数传给承诺对象的**then**函数:
|
||||
|
||||
+makeExample('toh-4/ts/app/app.component.ts', 'get-heroes', 'app/app.component.ts (getHeroes - revised)')(format=".")
|
||||
|
||||
.l-sub-section
|
||||
:marked
|
||||
The [ES2015 arrow function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions)
|
||||
@ -514,6 +564,7 @@ a#child-component
|
||||
|
||||
回调中所用的[ES2015箭头函数](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions)
|
||||
能比等价的函数表达式更加快速、优雅的处理*this*指针。
|
||||
|
||||
:marked
|
||||
Our callback sets the component's `heroes` property to the array of heroes returned by the service. That's all there is to it!
|
||||
|
||||
@ -523,14 +574,18 @@ a#child-component
|
||||
responding to a name selection with a detail view.
|
||||
|
||||
我们的程序仍在运行,仍在显示英雄列表,在选择英雄时,仍然会把他/她显示在详情页面中。
|
||||
|
||||
.l-sub-section
|
||||
:marked
|
||||
Checkout the "[Take it slow](#slow)" appendix to see what the app might be like with a poor connection.
|
||||
|
||||
查看附件中的“[慢一点儿](#slow)”一节,来了解在较差的网络连接中这个应用会是什么样的。
|
||||
|
||||
:marked
|
||||
### Review the App Structure
|
||||
|
||||
### 回顾本应用的结构
|
||||
|
||||
Let’s verify that we have the following structure after all of our good refactoring in this chapter:
|
||||
|
||||
再检查下,经历了本章的所有重构之后,我们应该有了下列文件结构:
|
||||
@ -570,7 +625,9 @@ a#child-component
|
||||
`)
|
||||
:marked
|
||||
## The Road We’ve Travelled
|
||||
|
||||
## 走过的路
|
||||
|
||||
Let’s take stock of what we’ve built.
|
||||
|
||||
来盘点一下我们已经构建完的部分。
|
||||
@ -601,7 +658,9 @@ p 运行这部分的#[+liveExampleLink2('在线例子', 'toh-4')]。
|
||||
|
||||
:marked
|
||||
### The Road Ahead
|
||||
|
||||
### 前方的路
|
||||
|
||||
Our Tour of Heroes has become more reusable using shared components and services.
|
||||
We want to create a dashboard, add menu links that route between the views, and format data in a template.
|
||||
As our app evolves, we’ll learn how to design it to make it easier to grow and maintain.
|
||||
@ -618,6 +677,7 @@ p 运行这部分的#[+liveExampleLink2('在线例子', 'toh-4')]。
|
||||
<a id="slow"></a>
|
||||
:marked
|
||||
### Appendix: Take it slow
|
||||
|
||||
### 附件:慢一点儿
|
||||
|
||||
We can simulate a slow connection.
|
||||
@ -629,6 +689,7 @@ p 运行这部分的#[+liveExampleLink2('在线例子', 'toh-4')]。
|
||||
导入`Hero`类,并且在`HeroService`中添加如下的`getHeroesSlowly`方法:
|
||||
|
||||
+makeExample('toh-4/ts/app/hero.service.ts', 'get-heroes-slowly', 'app/hero.service.ts (getHeroesSlowly)')(format=".")
|
||||
|
||||
:marked
|
||||
Like `getHeroes`, it also returns a !{_Promise}.
|
||||
But this !{_Promise} waits 2 seconds before resolving the !{_Promise} with mock heroes.
|
||||
|
Loading…
x
Reference in New Issue
Block a user