Polish toh-pt6.jade (round 2)

This commit is contained in:
Yang Lin 2016-12-02 10:56:05 +08:00
parent 2040b6ecd2
commit 4e49ba9cd0
1 changed files with 97 additions and 86 deletions

View File

@ -21,11 +21,11 @@ block includes
In this chapter we teach our application to make the corresponding HTTP calls to a remote server's web API. In this chapter we teach our application to make the corresponding HTTP calls to a remote server's web API.
在这一章中,我们要让应用程序学会通过HTTP调用来访问远程服务器上相应的Web API。 在这一章中,我们要让应用程序通过 HTTP 调用来访问远程服务器上相应的 Web API。
Run the <live-example></live-example> for this part. Run the <live-example></live-example> for this part.
p 运行这部分的<live-example>在线例子</live-example>。 运行这部分的<live-example>在线例子</live-example>。
.l-main-section .l-main-section
:marked :marked
@ -47,7 +47,7 @@ block start-server-and-watch
Open a terminal/console window and enter the following command to Open a terminal/console window and enter the following command to
start the TypeScript compiler, start the server, and watch for changes: start the TypeScript compiler, start the server, and watch for changes:
打开terminal/console窗口输入下列命令来启动TypeScript编译器,它会启动开发服务器,并监视文件变更: 打开终端/控制台窗口,输入下列命令来启动 TypeScript 编译器,它会启动开发服务器,并监视文件变更:
code-example(language="bash"). code-example(language="bash").
npm start npm start
@ -60,7 +60,7 @@ block start-server-and-watch
.l-main-section#http-providers .l-main-section#http-providers
h1 Providing HTTP Services h1 Providing HTTP Services
h1 准备HTTP服务 h1 准备 HTTP 服务
block http-library block http-library
:marked :marked
@ -68,25 +68,25 @@ block http-library
It's Angular's optional approach to web access and it exists as a separate add-on module called `@angular/http`, It's Angular's optional approach to web access and it exists as a separate add-on module called `@angular/http`,
shipped in a separate script file as part of the Angular npm package. shipped in a separate script file as part of the Angular npm package.
`HttpModule`***并不是***Angular的核心模块。 `HttpModule`***并不是*** Angular 的核心模块。
它是Angular用来进行Web访问的一种可选方式并通过Angular包中一个名叫`@angular/http`的独立附属模块发布了出来。 它是 Angular 用来进行 Web 访问的一种可选方式,并通过 Angular 包中一个名叫`@angular/http`的独立附属模块发布了出来。
Fortunately we're ready to import from `@angular/http` because `systemjs.config` configured *SystemJS* to load that library when we need it. Fortunately we're ready to import from `@angular/http` because `systemjs.config` configured *SystemJS* to load that library when we need it.
幸运的是,`systemjs.config`中已经配置好了*SystemJS*,并在必要时加载它,因此我们已经为从`@angular/http`中导入它做好了准备。 幸运的是,`systemjs.config`中已经配置好了 *SystemJS*,并在必要时加载它,因此我们已经为从`@angular/http`中导入它做好了准备。
:marked :marked
### Register for HTTP services ### Register for HTTP services
### 注册*HTTP*服务 ### 注册 *HTTP* 服务
block http-providers block http-providers
:marked :marked
Our app will depend upon the Angular `http` service which itself depends upon other supporting services. Our app will depend upon the Angular `http` service which itself depends upon other supporting services.
The `HttpModule` from `@angular/http` library holds providers for a complete set of HTTP services. The `HttpModule` from `@angular/http` library holds providers for a complete set of HTTP services.
我们的应用将会依赖于Angular的`http`服务,它本身又依赖于其它支持类服务。 我们的应用将会依赖于 Angular 的`http`服务,它本身又依赖于其它支持类服务。
来自`@angular/http`库中的`HttpModule`保存着这些HTTP相关服务提供商的全集。 来自`@angular/http`库中的`HttpModule`保存着这些 HTTP 相关服务提供商的全集。
We should be able to access these services from anywhere in the application. We should be able to access these services from anywhere in the application.
So we register them all by adding `HttpModule` to the `imports` list of the `AppModule` where we So we register them all by adding `HttpModule` to the `imports` list of the `AppModule` where we
@ -113,14 +113,13 @@ block http-providers
registering in `main` for a special reason.</span> registering in `main` for a special reason.</span>
我们建议在根模块`!{_AppModuleVsAppComp}`的`providers`数组中注册全应用级的服务。 我们建议在根模块`!{_AppModuleVsAppComp}`的`providers`数组中注册全应用级的服务。
<span if-docs="dart">Here we're registering in `main` for a special reason.</span>
Our application is in the early stages of development and far from ready for production. Our application is in the early stages of development and far from ready for production.
We don't even have a web server that can handle requests for heroes. We don't even have a web server that can handle requests for heroes.
Until we do, *we'll have to fake it*. Until we do, *we'll have to fake it*.
我们的应用正处于开发的早期阶段,并且离进入产品阶段还很远。 我们的应用正处于开发的早期阶段,并且离进入产品阶段还很远。
我们甚至都还没有一个用来处理英雄相关请求的Web服务器在此之前*我们将不得不伪造一个*。 我们甚至都还没有一个用来处理英雄相关请求的 Web 服务器,在此之前,*我们将不得不伪造一个*。
We're going to *trick* the HTTP client into fetching and saving data from We're going to *trick* the HTTP client into fetching and saving data from
a mock service, the *in-memory web API*. a mock service, the *in-memory web API*.
@ -128,14 +127,11 @@ block http-providers
shouldn't know about this. So we'll slip the in-memory web API into the shouldn't know about this. So we'll slip the in-memory web API into the
configuration *above* the `AppComponent`.</span> configuration *above* the `AppComponent`.</span>
我们要*耍点小花招*让http客户端从一个Mock服务*内存(in-memory)Web API*)中获取和保存数据。 我们要*耍点小花招*,让 HTTP 客户端从一个模拟服务(*内存 Web API*)中获取和保存数据。
<span if-docs="dart"> The application itself doesn't need to know and
shouldn't know about this. So we'll slip the in-memory web API into the
configuration *above* the `AppComponent`.</span>
Here is a version of <span ngio-ex>!{_appModuleTsVsMainTs}</span> that performs this trick: Here is a version of <span ngio-ex>!{_appModuleTsVsMainTs}</span> that performs this trick:
这个版本的<span ngio-ex>!{_appModuleTsVsMainTs}</span>就是用来实现这个小花招的: 这个版本的<span ngio-ex>!{_appModuleTsVsMainTs}</span>就是用来实现这个小花招的:
+makeExcerpt(_appModuleTsVsMainTs, 'v2') +makeExcerpt(_appModuleTsVsMainTs, 'v2')
@ -147,7 +143,9 @@ block backend
with an _in-memory web API alternative service_. with an _in-memory web API alternative service_.
导入`InMemoryWebApiModule`并将其加入到模块的`imports`数组。 导入`InMemoryWebApiModule`并将其加入到模块的`imports`数组。
`InMemoryWebApiModule`将`Http`客户端默认的后端服务(这是一个辅助服务,负责与远程服务器对话)替换成了*内存Web API*服务: `InMemoryWebApiModule`将`Http`客户端默认的后端服务 &mdash;
这是一个辅助服务,负责与远程服务器对话 &mdash;
替换成了*内存 Web API*服务:
+makeExcerpt(_appModuleTsVsMainTs, 'in-mem-web-api', '') +makeExcerpt(_appModuleTsVsMainTs, 'in-mem-web-api', '')
@ -169,21 +167,22 @@ block dont-be-distracted-by-backend-subst
This chapter is an introduction to the !{_Angular_http_library}. This chapter is an introduction to the !{_Angular_http_library}.
Please don't be distracted by the details of this backend substitution. Just follow along with the example. Please don't be distracted by the details of this backend substitution. Just follow along with the example.
本章主要是介绍Angular的HTTP库不要因为这种“替换后端”的细节而分心。先不要管为什么只管照着这个例子做就可以了。 本章主要是介绍 Angular 的 HTTP 库,不要因为这种“替换后端”的细节而分心。
先不要管为什么,只管照着这个例子做就可以了。
Learn more later about the in-memory web API in the [HTTP client chapter](../guide/server-communication.html#in-mem-web-api). Learn more later about the in-memory web API in the [HTTP client chapter](../guide/server-communication.html#in-mem-web-api).
Remember, the in-memory web API is only useful in the early stages of development and for demonstrations such as this Tour of Heroes. Remember, the in-memory web API is only useful in the early stages of development and for demonstrations such as this Tour of Heroes.
Skip it when you have a real web API server. Skip it when you have a real web API server.
要学习关于*内存Web API*的更多知识,请参阅[HTTP客户端](../guide/server-communication.html#in-mem-web-api)一章 关于*内存 Web API* 的更多信息,见 [HTTP 客户端](../guide/server-communication.html#in-mem-web-api)。
记住,*内存Web API*主要用于开发的早期阶段或《英雄指南》这样的演示程序。 记住,*内存 Web API* 主要用于开发的早期阶段或《英雄指南》这样的演示程序。
如果你已经有了一个真实的Web API服务器尽管跳过它。 如果你已经有了一个真实的 Web API 服务器,尽管跳过它。
.l-main-section .l-main-section
:marked :marked
## Heroes and HTTP ## Heroes and HTTP
## 英雄与HTTP ## 英雄与 HTTP
Look at our current `HeroService` implementation Look at our current `HeroService` implementation
@ -196,12 +195,13 @@ block dont-be-distracted-by-backend-subst
It may have seemed like overkill at the time, but we were anticipating the It may have seemed like overkill at the time, but we were anticipating the
day when we fetched heroes with an HTTP client and we knew that would have to be an asynchronous operation. day when we fetched heroes with an HTTP client and we knew that would have to be an asynchronous operation.
我们返回一个承诺Promise)它用mock版的英雄列表进行解析。 我们返回一个承诺 (Promise),它用模拟版的英雄列表进行解析。
它当时可能看起来显得有点过于复杂不过我们早就预料到总有这么一天会通过一个HTTP客户端来获取英雄数据而且我们知道那一定是一个异步操作。 它当时可能看起来显得有点过于复杂,不过我们预料到总有这么一天会通过 HTTP 客户端来获取英雄数据,
而且我们知道,那一定是一个异步操作。
That day has arrived! Let's convert `getHeroes()` to use HTTP. That day has arrived! Let's convert `getHeroes()` to use HTTP.
这一天到来了!我们把`getHeroes()`换成用HTTP。 这一天到来了!我们把`getHeroes()`换成用 HTTP。
+makeExcerpt('app/hero.service.ts (updated getHeroes and new class members)', 'getHeroes') +makeExcerpt('app/hero.service.ts (updated getHeroes and new class members)', 'getHeroes')
@ -217,7 +217,7 @@ block dont-be-distracted-by-backend-subst
Refresh the browser, and the hero data should be successfully loaded from the Refresh the browser, and the hero data should be successfully loaded from the
mock server. mock server.
刷新浏览器后,英雄数据就会从Mock服务器被成功读取。 刷新浏览器后,英雄数据就会从模拟服务器被成功读取。
<h3 id="!{_h3id}">HTTP !{_Promise}</h3> <h3 id="!{_h3id}">HTTP !{_Promise}</h3>
@ -233,8 +233,8 @@ block get-heroes-details
*Observables* are a powerful way to manage asynchronous data flows. *Observables* are a powerful way to manage asynchronous data flows.
We'll learn about [Observables](#observables) later in this chapter. We'll learn about [Observables](#observables) later in this chapter.
Angular的`http.get`返回一个RxJS的`Observable`对象。 Angular 的`http.get`返回一个 RxJS 的`Observable`对象。
*Observable(可观察对象)*是一个管理异步数据流的强力方式。 *Observable(可观察对象)*是一个管理异步数据流的强力方式。
后面我们还会进一步学习[可观察对象](#observables)。 后面我们还会进一步学习[可观察对象](#observables)。
For *now* we get back on familiar ground by immediately For *now* we get back on familiar ground by immediately
@ -249,7 +249,7 @@ block get-heroes-details
not out of the box. not out of the box.
The Angular `Observable` is a bare-bones implementation. The Angular `Observable` is a bare-bones implementation.
不幸的是Angular的`Observable`并没有一个`toPromise`操作符... 没有打包在一起发布。 不幸的是Angular 的`Observable`并没有一个`toPromise`操作符... 没有打包在一起发布。
Angular的`Observable`只是一个骨架实现。 Angular的`Observable`只是一个骨架实现。
There are scores of operators like `toPromise` that extend `Observable` with useful capabilities. There are scores of operators like `toPromise` that extend `Observable` with useful capabilities.
@ -258,19 +258,19 @@ block get-heroes-details
有很多像`toPromise`这样的操作符,用于扩展`Observable`,为其添加有用的能力。 有很多像`toPromise`这样的操作符,用于扩展`Observable`,为其添加有用的能力。
如果我们希望得到那些能力,就得自己添加那些操作符。 如果我们希望得到那些能力,就得自己添加那些操作符。
那很容易只要从RxJS库中导入它们就可以了就像这样 那很容易,只要从 RxJS 库中导入它们就可以了,就像这样:
+makeExcerpt('app/hero.service.ts', 'rxjs', '') +makeExcerpt('app/hero.service.ts', 'rxjs', '')
:marked :marked
### Extracting the data in the *then* callback ### Extracting the data in the *then* callback
### 在*then*回调中提取出数据 ### 在 *then* 回调中提取出数据
In the *promise*'s `then` callback we call the `json` method of the HTTP `Response` to extract the In the *promise*'s `then` callback we call the `json` method of the HTTP `Response` to extract the
data within the response. data within the response.
在*promise*的`then`回调中我们调用HTTP的`Reponse`对象的`json`方法,以提取出其中的数据。 *promise* 的`then`回调中,我们调用 HTTP 的`Reponse`对象的`json`方法,以提取出其中的数据。
+makeExcerpt('app/hero.service.ts', 'to-data', '') +makeExcerpt('app/hero.service.ts', 'to-data', '')
@ -290,8 +290,8 @@ block get-heroes-details
Your API might return something else. Adjust the code to match *your web API*. Your API might return something else. Adjust the code to match *your web API*.
仔细看看这个由服务器返回的数据的形态。 仔细看看这个由服务器返回的数据的形态。
这个*内存Web API*的范例中所做的是返回一个带有`data`属性的对象。 这个*内存 Web API* 的范例中所做的是返回一个带有`data`属性的对象。
你的API也可以返回其它东西。请调整这些代码以匹配*你的Web API*。 你的 API 也可以返回其它东西。请调整这些代码以匹配*你的 Web API*。
:marked :marked
The caller is unaware of these machinations. It receives a !{_Promise} of *heroes* just as it did before. The caller is unaware of these machinations. It receives a !{_Promise} of *heroes* just as it did before.
@ -301,7 +301,7 @@ block get-heroes-details
调用者并不知道这些实现机制,它仍然像以前那样接收一个包含*英雄数据*的承诺。 调用者并不知道这些实现机制,它仍然像以前那样接收一个包含*英雄数据*的承诺。
它也不知道我们已经改成了从服务器获取英雄数据。 它也不知道我们已经改成了从服务器获取英雄数据。
它也不必了解把HTTP响应转换成英雄数据时所作的这些复杂变换。 它也不必了解把 HTTP 响应转换成英雄数据时所作的这些复杂变换。
看到美妙之处了吧,这正是将数据访问委托组一个服务的目的。 看到美妙之处了吧,这正是将数据访问委托组一个服务的目的。
### Error Handling ### Error Handling
@ -319,7 +319,7 @@ block get-heroes-details
We must anticipate HTTP failures as they happen frequently for reasons beyond our control. We must anticipate HTTP failures as they happen frequently for reasons beyond our control.
这是一个关键的步骤! 这是一个关键的步骤!
我们必须预料到http请求会失败,因为有太多我们无法控制的原因可能导致它们频繁出现各种错误。 我们必须预料到 HTTP 请求会失败,因为有太多我们无法控制的原因可能导致它们频繁出现各种错误。
+makeExcerpt('app/hero.service.ts', 'handleError', '') +makeExcerpt('app/hero.service.ts', 'handleError', '')
@ -332,11 +332,12 @@ block get-heroes-details
We've also decided to return a user friendly form of the error to We've also decided to return a user friendly form of the error to
the caller in a !{rejected_promise} so that the caller can display a proper error message to the user. the caller in a !{rejected_promise} so that the caller can display a proper error message to the user.
我们还要通过一个被拒绝(rejected)的承诺(promise)来把该错误用一个用户友好的格式返回给调用者,以便调用者能把一个合适的错误信息显示给用户。 我们还要通过一个被拒绝 (rejected) 的承诺来把该错误用一个用户友好的格式返回给调用者,
以便调用者能把一个合适的错误信息显示给用户。
### Unchanged `getHeroes` API ### Unchanged `getHeroes` API
### `getHeroes` API没变 ### `getHeroes` API 没变
Although we made significant *internal* changes to `getHeroes()`, the public signature did not change. Although we made significant *internal* changes to `getHeroes()`, the public signature did not change.
We still return a !{_Promise}. We won't have to update any of the components that call `getHeroes()`. We still return a !{_Promise}. We won't have to update any of the components that call `getHeroes()`.
@ -347,7 +348,7 @@ block get-heroes-details
Our stakeholders are thrilled with the added flexibility from the API integration. Our stakeholders are thrilled with the added flexibility from the API integration.
Now they want the ability to create and delete heroes. Now they want the ability to create and delete heroes.
我们的客户很欣赏这种富有弹性的API集成方式。 我们的客户很欣赏这种富有弹性的 API 集成方式。
现在它们想增加创建和删除英雄的功能。 现在它们想增加创建和删除英雄的功能。
Let's see first what happens when we try to update a hero's details. Let's see first what happens when we try to update a hero's details.
@ -376,7 +377,7 @@ block get-heroes-details
the server. the server.
以前是不会丢失更新的,现在是怎么回事? 以前是不会丢失更新的,现在是怎么回事?
当该应用使用mock出来的英雄列表时,修改的是一份全局共享的英雄列表,而现在改成了从服务器获取数据。 当该应用使用模拟出来的英雄列表时,修改的是一份全局共享的英雄列表,而现在改成了从服务器获取数据。
如果我们希望这些更改被持久化,我们就得把它们写回服务器。 如果我们希望这些更改被持久化,我们就得把它们写回服务器。
:marked :marked
@ -396,20 +397,20 @@ block get-heroes-details
The `save` method persists hero name changes using the hero service The `save` method persists hero name changes using the hero service
`update` method and then navigates back to the previous view: `update` method and then navigates back to the previous view:
`save`方法使用hero服务的`update`方法来持久化对英雄名字的修改,然后导航回前一个视图: `save`方法使用 hero 服务的`update`方法来持久化对英雄名字的修改,然后导航回前一个视图:
+makeExcerpt('app/hero-detail.component.ts', 'save') +makeExcerpt('app/hero-detail.component.ts', 'save')
:marked :marked
### Hero service `update` method ### Hero service `update` method
### hero服务的`update`方法 ### hero 服务的`update`方法
The overall structure of the `update` method is similar to that of The overall structure of the `update` method is similar to that of
`getHeroes`, although we'll use an HTTP _put_ to persist changes `getHeroes`, although we'll use an HTTP _put_ to persist changes
server-side: server-side:
`update`方法的大致结构与`getHeroes`类似不过我们使用HTTP的*put*方法来把修改持久化到服务端: `update`方法的大致结构与`getHeroes`类似,不过我们使用 HTTP *put* 方法来把修改持久化到服务端:
+makeExcerpt('app/hero.service.ts', 'update') +makeExcerpt('app/hero.service.ts', 'update')
@ -419,8 +420,8 @@ block get-heroes-details
calling `!{_JSON_stringify}`. We identify the body content type calling `!{_JSON_stringify}`. We identify the body content type
(`application/json`) in the request header. (`application/json`) in the request header.
我们通过一个编码在URL中的英雄id来告诉服务器应该更新哪个英雄。put的body是该英雄的JSON字符串它是通过调用`!{_JSON_stringify}`得到的。 我们通过一个编码在 URL 中的英雄 id 来告诉服务器应该更新哪个英雄。put body 是该英雄的 JSON 字符串,它是通过调用`!{_JSON_stringify}`得到的。
并且在请求头中标记出的body的内容类型`application/json`)。 并且在请求头中标记出的 body 的内容类型(`application/json`)。
Refresh the browser and give it a try. Changes to hero names should now persist. Refresh the browser and give it a try. Changes to hero names should now persist.
@ -435,12 +436,12 @@ block get-heroes-details
To add a new hero we need to know the hero's name. Let's use an input To add a new hero we need to know the hero's name. Let's use an input
element for that, paired with an add button. element for that, paired with an add button.
要添加一个新的英雄我们得先知道英雄的名字。我们使用一个input元素和一个添加按钮来实现。 要添加一个新的英雄,我们得先知道英雄的名字。我们使用一个 input 元素和一个添加按钮来实现。
Insert the following into the heroes component HTML, first thing after Insert the following into the heroes component HTML, first thing after
the heading: the heading:
把下列代码插入heroes组件的HTML中放在标题的下面 把下列代码插入 heroes 组件的 HTML 中,放在标题的下面:
+makeExcerpt('app/heroes.component.html', 'add') +makeExcerpt('app/heroes.component.html', 'add')
@ -448,7 +449,7 @@ block get-heroes-details
In response to a click event, we call the component's click handler and then In response to a click event, we call the component's click handler and then
clear the input field so that it will be ready to use for another name. clear the input field so that it will be ready to use for another name.
click事件触发时我们调用组件的click处理器,然后清空这个输入框,以便用来输入另一个名字。 点击事件触发时,我们调用组件的点击处理器,然后清空这个输入框,以便用来输入另一个名字。
+makeExcerpt('app/heroes.component.ts', 'add') +makeExcerpt('app/heroes.component.ts', 'add')
@ -456,7 +457,8 @@ block get-heroes-details
When the given name is non-blank, the handler delegates creation of the When the given name is non-blank, the handler delegates creation of the
named hero to the hero service, and then adds the new hero to our !{_array}. named hero to the hero service, and then adds the new hero to our !{_array}.
当指定的名字不为空的时候click处理器就会委托hero服务来创建一个具有此名字的英雄并把这个新的英雄添加到我们的数组中。 当指定的名字不为空的时候,点击处理器就会委托 hero 服务来创建一个具有此名字的英雄,
并把这个新的英雄添加到我们的数组中。
Finally, we implement the `create` method in the `HeroService` class. Finally, we implement the `create` method in the `HeroService` class.
@ -484,7 +486,7 @@ block get-heroes-details
Add this button element to the heroes component HTML, right after the hero Add this button element to the heroes component HTML, right after the hero
name in the repeated `<li>` tag: name in the repeated `<li>` tag:
把这个button元素添加到英雄列表组件的HTML中,把它放在`<li>`标签中的英雄名的后面: 把这个按钮元素添加到英雄列表组件的 HTML 中,把它放在`<li>`标签中的英雄名的后面:
+makeExcerpt('app/heroes.component.html', 'delete', '') +makeExcerpt('app/heroes.component.html', 'delete', '')
@ -501,7 +503,8 @@ block get-heroes-details
don't want the `<li>` click handler to be triggered because that would don't want the `<li>` click handler to be triggered because that would
select the hero that we are going to delete! select the hero that we are going to delete!
除了调用组件的`delete`方法之外,这个`delete`按钮的click处理器还应该阻止click事件向上冒泡 —— 我们并不希望触发`<li>`的事件处理器,否则它会选中我们要删除的这位英雄。 除了调用组件的`delete`方法之外,这个`delete`按钮的点击处理器还应该阻止点击事件向上冒泡 &mdash;
我们并不希望触发`<li>`的事件处理器,否则它会选中我们要删除的这位英雄。
The logic of the `delete` handler is a bit trickier: The logic of the `delete` handler is a bit trickier:
@ -514,25 +517,26 @@ block get-heroes-details
is still responsible for updating the display: it removes the deleted hero is still responsible for updating the display: it removes the deleted hero
from the !{_array} and resets the selected hero if necessary. from the !{_array} and resets the selected hero if necessary.
当然我们仍然把删除英雄的操作委托给了hero服务不过该组件仍然负责更新显示它从数组中移除了被删除的英雄如果删除的是正选中的英雄还会清空选择。 当然,我们仍然把删除英雄的操作委托给了 hero 服务,
不过该组件仍然负责更新显示:它从数组中移除了被删除的英雄,如果删除的是正选中的英雄,还会清空选择。
:marked :marked
We want our delete button to be placed at the far right of the hero entry. We want our delete button to be placed at the far right of the hero entry.
This extra CSS accomplishes that: This extra CSS accomplishes that:
我们希望删除按钮被放在英雄条目的最右边。 我们希望删除按钮被放在英雄条目的最右边。
于是CSS变成了这样 于是 CSS 变成了这样:
+makeExcerpt('app/heroes.component.css', 'additions') +makeExcerpt('app/heroes.component.css', 'additions')
:marked :marked
### Hero service `delete` method ### Hero service `delete` method
### hero服务的`delete`方法 ### hero 服务的`delete`方法
The hero service's `delete` method uses the _delete_ HTTP method to remove the hero from the server: The hero service's `delete` method uses the _delete_ HTTP method to remove the hero from the server:
hero服务的`delete`方法使用HTTP的*delete*方法来从服务器上移除该英雄: hero 服务的`delete`方法使用 HTTP *delete* 方法来从服务器上移除该英雄:
+makeExcerpt('app/hero.service.ts', 'delete') +makeExcerpt('app/hero.service.ts', 'delete')
@ -545,13 +549,13 @@ block get-heroes-details
:marked :marked
## !{_Observable}s ## !{_Observable}s
## 可观察对象Observable ## 可观察对象 (Observable)
block observables-section-intro block observables-section-intro
:marked :marked
Each `Http` service method returns an `Observable` of HTTP `Response` objects. Each `Http` service method returns an `Observable` of HTTP `Response` objects.
`Http`服务中的每个方法都返回一个HTTP `Response`对象的`Observable`实例。 `Http`服务中的每个方法都返回一个 HTTP `Response`对象的`Observable`实例。
Our `HeroService` converts that `Observable` into a `Promise` and returns the promise to the caller. Our `HeroService` converts that `Observable` into a `Promise` and returns the promise to the caller.
In this section we learn to return the `Observable` directly and discuss when and why that might be In this section we learn to return the `Observable` directly and discuss when and why that might be
@ -566,13 +570,13 @@ block observables-section-intro
An *observable* is a stream of events that we can process with array-like operators. An *observable* is a stream of events that we can process with array-like operators.
一个*可观察对象*是一个事件流,我们可以用数组型操作符(函数)来处理它。 一个*可观察对象*是一个事件流,我们可以用数组型操作符来处理它。
Angular core has basic support for observables. We developers augment that support with Angular core has basic support for observables. We developers augment that support with
operators and extensions from the [RxJS Observables](http://reactivex.io/rxjs/) library. operators and extensions from the [RxJS Observables](http://reactivex.io/rxjs/) library.
We'll see how shortly. We'll see how shortly.
Angular内核中提供了对可观察对象的基本支持。而我们这些开发人员可以自己从[RxJS可观察对象](http://reactivex.io/rxjs/)库中引入操作符和扩展。 Angular 内核中提供了对可观察对象的基本支持。而我们这些开发人员可以自己从 [RxJS 可观察对象](http://reactivex.io/rxjs/)库中引入操作符和扩展。
我们会简短的讲解下如何做。 我们会简短的讲解下如何做。
Recall that our `HeroService` quickly chained the `toPromise` operator to the `Observable` result of `http.get`. Recall that our `HeroService` quickly chained the `toPromise` operator to the `Observable` result of `http.get`.
@ -587,7 +591,7 @@ block observables-section-intro
and it helps that promises are widely understood by JavaScript programmers. and it helps that promises are widely understood by JavaScript programmers.
转换成承诺通常是更好地选择,我们通常会要求`http.get`获取单块数据。只要接收到数据,就算完成。 转换成承诺通常是更好地选择,我们通常会要求`http.get`获取单块数据。只要接收到数据,就算完成。
使用承诺这种形式的结果是让调用方更容易写并且承诺已经在JavaScript程序员中被广泛接受了。 使用承诺这种形式的结果是让调用方更容易写,并且承诺已经在 JavaScript 程序员中被广泛接受了。
:marked :marked
But requests aren't always "one and done". We may start one request, But requests aren't always "one and done". We may start one request,
@ -595,7 +599,8 @@ block observables-section-intro
Such a _request-cancel-new-request_ sequence is difficult to implement with *!{_Promise}s*. Such a _request-cancel-new-request_ sequence is difficult to implement with *!{_Promise}s*.
It's easy with *!{_Observable}s* as we'll see. It's easy with *!{_Observable}s* as we'll see.
但是请求并非总是“一次性”的。我们可以开始一个请求,并且取消它,再开始另一个不同的请求 —— 在服务器对第一个请求作出回应之前。 但是请求并非总是“一次性”的。我们可以开始一个请求,
并且取消它,在服务器对第一个请求作出回应之前,再开始另一个不同的请求 。
像这样一个_请求-取消-新请求_的序列用*承诺*是很难实现的,但接下来我们会看到,它对于*可观察对象*却很简单。 像这样一个_请求-取消-新请求_的序列用*承诺*是很难实现的,但接下来我们会看到,它对于*可观察对象*却很简单。
### Search-by-name ### Search-by-name
@ -606,11 +611,11 @@ block observables-section-intro
As the user types a name into a search box, we'll make repeated HTTP requests for heroes filtered by that name. As the user types a name into a search box, we'll make repeated HTTP requests for heroes filtered by that name.
我们要为《英雄指南》添加一个*英雄搜索*特性。 我们要为《英雄指南》添加一个*英雄搜索*特性。
当用户在搜索框中输入一个名字时我们将不断发起HTTP请求以获得按名字过滤的英雄。 当用户在搜索框中输入一个名字时,我们将不断发起 HTTP 请求,以获得按名字过滤的英雄。
We start by creating `HeroSearchService` that sends search queries to our server's web api. We start by creating `HeroSearchService` that sends search queries to our server's web api.
我们先创建`HeroSearchService`服务它会把搜索请求发送到我们服务器上的Web API。 我们先创建`HeroSearchService`服务,它会把搜索请求发送到我们服务器上的 Web API。
+makeExample('app/hero-search.service.ts') +makeExample('app/hero-search.service.ts')
@ -647,11 +652,11 @@ block observables-section-intro
:marked :marked
As the user types in the search box, a *keyup* event binding calls the component's `search` method with the new search box value. As the user types in the search box, a *keyup* event binding calls the component's `search` method with the new search box value.
当用户在搜索框中输入时,一个*keyup*事件绑定会调用该组件的`search`方法,并传入新的搜索框的值。 当用户在搜索框中输入时,一个 *keyup* 事件绑定会调用该组件的`search`方法,并传入新的搜索框的值。
The `*ngFor` repeats *hero* objects from the component's `heroes` property. No surprise there. The `*ngFor` repeats *hero* objects from the component's `heroes` property. No surprise there.
`*ngFor`从该组件的`heroes`属性重复获取*hero*对象。这也没啥特别的。 `*ngFor`从该组件的`heroes`属性重复获取 *hero* 对象。这也没啥特别的。
But, as we'll soon see, the `heroes` property is now !{_an} *!{_Observable}* of hero !{_array}s, rather than just a hero !{_array}. But, as we'll soon see, the `heroes` property is now !{_an} *!{_Observable}* of hero !{_array}s, rather than just a hero !{_array}.
The `*ngFor` can't do anything with !{_an} `!{_Observable}` until we flow it through the `async` pipe (`AsyncPipe`). The `*ngFor` can't do anything with !{_an} `!{_Observable}` until we flow it through the `async` pipe (`AsyncPipe`).
@ -694,7 +699,7 @@ block search-criteria-intro
<a id="ngoninit"></a> <a id="ngoninit"></a>
#### Initialize the _**heroes**_ property (_**ngOnInit**_) #### Initialize the _**heroes**_ property (_**ngOnInit**_)
#### 初始化_**heroes**_属性(_**ngOnInit**_) #### 初始化 _**heroes**_ 属性(_**ngOnInit**_)
<span if-docs="ts">A `Subject` is also an `Observable`.</span> <span if-docs="ts">A `Subject` is also an `Observable`.</span>
We're going to turn the stream We're going to turn the stream
@ -709,7 +714,7 @@ block search-criteria-intro
If we passed every user keystroke directly to the `HeroSearchService`, we'd unleash a storm of HTTP requests. If we passed every user keystroke directly to the `HeroSearchService`, we'd unleash a storm of HTTP requests.
Bad idea. We don't want to tax our server resources and burn through our cellular network data plan. Bad idea. We don't want to tax our server resources and burn through our cellular network data plan.
如果我们直接把每一次用户按键都直接传给`HeroSearchService`就会发起一场HTTP请求风暴。 如果我们直接把每一次用户按键都直接传给`HeroSearchService`,就会发起一场 HTTP 请求风暴。
这可不好玩。我们不希望占用服务器资源,也不想耗光蜂窝移动网络的流量。 这可不好玩。我们不希望占用服务器资源,也不想耗光蜂窝移动网络的流量。
block observable-transformers block observable-transformers
@ -723,17 +728,20 @@ block observable-transformers
* `debounceTime(300)` waits until the flow of new string events pauses for 300 milliseconds * `debounceTime(300)` waits until the flow of new string events pauses for 300 milliseconds
before passing along the latest string. We'll never make requests more frequently than 300ms. before passing along the latest string. We'll never make requests more frequently than 300ms.
* 在传出最终字符串之前,`debounceTime(300)`将会等待直到新增字符串的事件暂停了300毫秒。我们实际发起请求的间隔永远不会小于300ms。 在传出最终字符串之前,`debounceTime(300)`将会等待,直到新增字符串的事件暂停了 300 毫秒。
我们实际发起请求的间隔永远不会小于 300ms。
* `distinctUntilChanged` ensures that we only send a request if the filter text changed. * `distinctUntilChanged` ensures that we only send a request if the filter text changed.
There's no point in repeating a request for the same search term. There's no point in repeating a request for the same search term.
* `distinctUntilChanged`确保只在过滤条件变化时才发送请求,这样就不会重复请求同一个搜索词了。 `distinctUntilChanged`确保只在过滤条件变化时才发送请求,
这样就不会重复请求同一个搜索词了。
* `switchMap` calls our search service for each search term that makes it through the `debounce` and `distinctUntilChanged` gauntlet. * `switchMap` calls our search service for each search term that makes it through the `debounce` and `distinctUntilChanged` gauntlet.
It cancels and discards previous search observables, returning only the latest search service observable. It cancels and discards previous search observables, returning only the latest search service observable.
* `switchMap`会为每个从`debounce`和`distinctUntilChanged`中通过的搜索词调用搜索服务。它会取消并丢弃以前的搜索可观察对象,只保留最近的。 `switchMap`会为每个从`debounce`和`distinctUntilChanged`中通过的搜索词调用搜索服务。
它会取消并丢弃以前的搜索可观察对象,只保留最近的。
.l-sub-section .l-sub-section
:marked :marked
@ -747,13 +755,14 @@ block observable-transformers
Even with a 300ms pause between requests, we could have multiple HTTP requests in flight Even with a 300ms pause between requests, we could have multiple HTTP requests in flight
and they may not return in the order sent. and they may not return in the order sent.
每次符合条件的按键事件都会触发一次对`http`方法的调用。即使在发送每个请求前都有300毫秒的延迟我们仍然可能同时拥有多个在途的HTTP请求并且它们返回的顺序未必就是发送时的顺序。 每次符合条件的按键事件都会触发一次对`http`方法的调用。即使在发送每个请求前都有 300 毫秒的延迟,
我们仍然可能同时拥有多个在途的 HTTP 请求,并且它们返回的顺序未必就是发送时的顺序。
`switchMap` preserves the original request order while returning `switchMap` preserves the original request order while returning
only the observable from the most recent `http` method call. only the observable from the most recent `http` method call.
Results from prior calls are canceled and discarded. Results from prior calls are canceled and discarded.
`switchMap`保留了原始的请求顺序并且只返回最近一次http调用返回的可观察对象。 `switchMap`保留了原始的请求顺序,并且只返回最近一次 `http` 调用返回的可观察对象。
这是因为以前的调用都被取消或丢弃了。 这是因为以前的调用都被取消或丢弃了。
We also short-circuit the `http` method call and return an observable containing an empty array We also short-circuit the `http` method call and return an observable containing an empty array
@ -765,7 +774,8 @@ block observable-transformers
until the service supports that feature, a topic for another day. until the service supports that feature, a topic for another day.
We are content for now to discard unwanted results. We are content for now to discard unwanted results.
注意_取消_`HeroSearchService`的可观察对象并不会实际中止abort一个未完成的HTTP请求除非服务支持这个特性这个问题我们以后再讨论。 注意_取消_`HeroSearchService`的可观察对象并不会实际中止 (abort) 一个未完成的 HTTP 请求,
除非服务支持这个特性,这个问题我们以后再讨论。
目前我们的做法只是丢弃不希望的结果。 目前我们的做法只是丢弃不希望的结果。
:marked :marked
@ -777,18 +787,19 @@ block observable-transformers
### Import RxJS operators ### Import RxJS operators
### 导入RxJS操作符 ### 导入 RxJS 操作符
The RxJS operators are not available in Angular's base `Observable` implementation. The RxJS operators are not available in Angular's base `Observable` implementation.
We have to extend `Observable` by *importing* them. We have to extend `Observable` by *importing* them.
Angular的基本版`Observable`实现中RxJS操作符是不可用的。 Angular 的基本版`Observable`实现中RxJS 操作符是不可用的。
我们得*导入*它们,以扩展`Observable`。 我们得*导入*它们,以扩展`Observable`。
We could extend `Observable` with just the operators we need here by We could extend `Observable` with just the operators we need here by
including the pertinent `import` statements at the top of this file. including the pertinent `import` statements at the top of this file.
通过在本文件的顶部写上适当的`import`语句,我们可以为`Observable`扩展出这里用到的那些操作符。 通过在本文件的顶部写上适当的`import`语句,
我们可以为`Observable`扩展出这里用到的那些操作符。
.l-sub-section .l-sub-section
:marked :marked
@ -801,7 +812,7 @@ block observable-transformers
We combine all of the RxJS `Observable` extensions that _our entire app_ requires into a single RxJS imports file. We combine all of the RxJS `Observable` extensions that _our entire app_ requires into a single RxJS imports file.
在这个例子中,我们使用一些不同的方法。 在这个例子中,我们使用一些不同的方法。
我们把整个应用中要用的那些RxJS `Observable`扩展组合在一起放在一个单独的RxJS导入文件中。 我们把整个应用中要用的那些 RxJS `Observable`扩展组合在一起,放在一个单独的 RxJS 导入文件中。
+makeExample('app/rxjs-extensions.ts')(format='.') +makeExample('app/rxjs-extensions.ts')(format='.')
@ -819,7 +830,7 @@ block observable-transformers
We add the hero search HTML element to the bottom of the `DashboardComponent` template. We add the hero search HTML element to the bottom of the `DashboardComponent` template.
将表示“英雄搜索”组件的HTML元素添加到`DashboardComponent`模版的最后面。 将表示“英雄搜索”组件的 HTML 元素添加到`DashboardComponent`模版的最后面。
+makeExample('app/dashboard.component.html')(format='.') +makeExample('app/dashboard.component.html')(format='.')
@ -903,27 +914,27 @@ block filetree
- We added the necessary dependencies to use HTTP in our application. - We added the necessary dependencies to use HTTP in our application.
- 我们添加了在应用程序中使用HTTP的必备依赖。 我们添加了在应用程序中使用 HTTP 的必备依赖。
- We refactored `HeroService` to load heroes from a web API. - We refactored `HeroService` to load heroes from a web API.
- 我们重构了`HeroService`以通过web API来加载英雄数据。 我们重构了`HeroService`,以通过 web API 来加载英雄数据。
- We extended `HeroService` to support post, put and delete methods. - We extended `HeroService` to support post, put and delete methods.
- 我们扩展了`HeroService`来支持post、put和delete方法。 我们扩展了`HeroService`来支持 post、put 和 delete 方法。
- We updated our components to allow adding, editing and deleting of heroes. - We updated our components to allow adding, editing and deleting of heroes.
- 我们更新了组件,以允许用户添加、编辑和删除英雄。 我们更新了组件,以允许用户添加、编辑和删除英雄。
- We configured an in-memory web API. - We configured an in-memory web API.
- 我们配置了一个内存Web API。 我们配置了一个内存 Web API。
- We learned how to use !{_Observable}s. - We learned how to use !{_Observable}s.
- 我们学会了如何使用“可观察对象”。 我们学会了如何使用“可观察对象”。
Here are the files we _added or changed_ in this chapter. Here are the files we _added or changed_ in this chapter.
@ -975,4 +986,4 @@ block file-summary
Return to the [learning path](../guide/learning-angular.html#architecture) where Return to the [learning path](../guide/learning-angular.html#architecture) where
you can read about the concepts and practices you discovered in this tutorial. you can read about the concepts and practices you discovered in this tutorial.
返回[学习路径](../guide/learning-angular.html#architecture),你可以阅读在本教程中探索到的概念和实践。 返回[学习路径](../guide/learning-angular.html#architecture),你可以阅读在本教程中探索到的概念和实践。