+
- [Guarding against Cross-Site Request Forgery](#xsrf)
- - [防止跨站请求伪造](#xsrf)
+ [防止跨站请求伪造](#xsrf)。
- [Appendix: Tour of Heroes in-memory server](#in-mem-web-api).
- - [附录:英雄指南的内存Web API服务](#in-mem-web-api)
+ [附录:英雄指南的内存 Web API 服务](#in-mem-web-api)
A live example illustrates these topics.
@@ -104,19 +104,19 @@ block demos-list
:marked
- [The Tour of Heroes *HTTP* client demo](#http-client).
- - [英雄指南HTTP客户端](#http-client)
+ [英雄指南 *HTTP* 客户端](#http-client)
- [Fall back to !{_Promise}s](#promises).
- - [回到使用承诺](#promises)
+ [回到使用承诺](#promises)
- [Cross-Origin Requests: Wikipedia example](#cors).
- - [跨站请求:Wikipedia例子](#cors)
+ [跨站请求:Wikipedia 示例](#cors)
- [More fun with observables](#more-observables).
- - [更多可观察对象的探索](#more-observables)
+ [更多可观察对象的探索](#more-observables)
:marked
The root `AppComponent` orchestrates these demos:
@@ -129,23 +129,24 @@ block demos-list
There is nothing remarkable here _except_ for the import of RxJS operators, which is
described [later](#rxjs).
- 这里唯一值得注意的是对RxJS操作符的导入,[后面](#rxjs)有详细介绍。
+ 这里唯一值得注意的是对 RxJS 操作符的导入,[后面](#rxjs)有详细介绍。
+makeExample('server-communication/ts/app/app.component.ts', 'import-rxjs')(format='.')
.l-main-section#http-providers
:marked
# Providing HTTP services
- # 提供HTTP服务
+ # 提供 HTTP 服务
First, configure the application to use server communication facilities.
- 首先,配置应用来使用服务器对话设施。
+ 首先,配置应用来使用服务器通讯设施。
The !{_Angular_Http} client communicates with the server using a familiar HTTP request/response protocol.
The `!{_Http}` client is one of a family of services in the !{_Angular_http_library}.
- 我们通过!{_Angular_Http}客户端,使用熟悉的HTTP请求/回应协议与服务器通讯。`!{_Http}`客户端是!{_Angular_http_libraryCn}所提供的服务大家庭中的一员。
+ 我们通过 !{_Angular_Http}客户端,使用熟悉的 HTTP 请求/回应协议与服务器通讯。
+ `!{_Http}`客户端是!{_Angular_http_libraryCn}所提供的一系列服务之一。
+ifDocsFor('ts')
.l-sub-section
@@ -154,23 +155,24 @@ block demos-list
the !{_Angular_http_library}
because the `systemjs.config.js` file maps to that module name.
- 当我们从`@angular/http`模块中导入服务时,SystemJS知道该如何从!{_Angular_http_libraryCn}中加载它们,这是因为我们已经在`system.config`文件中注册过这个模块名。
+ 当我们从`@angular/http`模块中导入服务时,SystemJS 知道该如何从 !{_Angular_http_libraryCn}中加载它们,
+ 这是因为`systemjs.config.js`文件已经注册过这个模块名。
:marked
Before you can use the `!{_Http}` client, you need to register it as a service provider with the dependency injection system.
- 要想使用`#{_Http}`客户端,我们得先通过依赖注入系统把它注册成一个服务提供商。
+ 要想使用`#{_Http}`客户端,你需要先通过依赖注入系统把它注册成一个服务提供商。
.l-sub-section
:marked
Read about providers in the [Dependency Injection](dependency-injection.html) page.
- 了解关于提供商的更多知识,参见[依赖注入](dependency-injection.html)一章。
+ 关于提供商的更多信息,见[依赖注入](dependency-injection.html)。
:marked
Register providers by importing other NgModules to the root NgModule in `app.module.ts`.
- 在`app.module.ts`中通过导入其他模块来注册提供商到根NgModule。
+ 在`app.module.ts`中通过导入其他模块来注册提供商到根 NgModule。
+makeExample('server-communication/ts/app/app.module.1.ts', null, 'app/app.module.ts (v1)')(format='.')
@@ -179,11 +181,12 @@ block http-providers
Begin by importing the necessary members.
The newcomers are the `HttpModule` and the `JsonpModule` from the !{_Angular_http_library}. For more information about imports and related terminology, see the [MDN reference](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import) on the `import` statement.
- 我们从导入所需的符号开始,它们中的大多数我们都熟悉了,只有`HttpModule`和`JsonpModule`是新面孔。
+ 我们从导入所需的成员开始,它们中的大多数我们都熟悉了,只有`HttpModule`和`JsonpModule`是新面孔。
+ 关于导入和相关术语的更多信息,见 [MDN reference](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import) 中的`import`语句。
To add these modules to the application, pass them to the `imports` array in the root `@NgModule`.
- 只要把它们传给我们这个根模块的`imports`数组,就可以把这些模块加入本应用。
+ 只要把它们传给根模块的`imports`数组,就可以把这些模块加入应用。
.l-sub-section
:marked
@@ -192,22 +195,24 @@ block http-providers
there is a JSONP demo later in this page.
Loading its module now saves time.
- 我们需要HttpModule来发起HTTP调用。普通的HTTP调用并不需要用到JsonpModule,
- 不过稍后我们就会演示对JSONP的支持,所以现在就加载它,免得再回来改浪费时间。
+ 我们需要 HttpModule 来发起 HTTP 调用。
+ 普通的 HTTP 调用并不需要用到 JsonpModule,
+ 不过稍后我们就会演示对 JSONP 的支持,
+ 所以现在就加载它,免得再回来改浪费时间。
.l-main-section#http-client
:marked
## The Tour of Heroes HTTP client demo
- ## 《英雄指南》的HTTP客户端演示
+ ## 《英雄指南》的 HTTP 客户端演示
The first demo is a mini-version of the [tutorial](../tutorial)'s "Tour of Heroes" (ToH) application.
This version gets some heroes from the server, displays them in a list, lets the user add new heroes, and saves them to the server.
The app uses the !{_Angular_Http} client to communicate via `XMLHttpRequest (XHR)`.
- 我们的第一个演示是《英雄指南(TOH)》教程的一个迷你版。
+ 我们的第一个演示是《英雄指南(TOH)》[教程](../tutorial)的一个迷你版。
这个版本从服务器获取一些英雄,把它们显示在列表中,还允许我们添加新的英雄并将其保存到服务器。
- 借助!{_Angular_Http}客户端,我们通过`XMLHttpRequest (XHR)`与服务器通讯。
+ 借助 !{_Angular_Http}客户端,我们通过`XMLHttpRequest (XHR)`与服务器通讯。
It works like this:
@@ -217,7 +222,7 @@ figure.image-display
:marked
This demo has a single component, the `HeroListComponent`. Here's its template:
- 这个范例是一个单一组件`HeroListComponent`,其模板如下:
+ 这个演示是一个单一组件`HeroListComponent`,其模板如下:
+makeExample('server-communication/ts/app/toh/hero-list.component.html', null, 'app/toh/hero-list.component.html (template)')
:marked
It presents the list of heroes with an `ngFor`.
@@ -229,9 +234,10 @@ figure.image-display
the event binding clears it to make it ready for a new hero name.
它使用`ngFor`来展现这个英雄列表。
- 列表的下方是一个输入框和一个*Add Hero*按钮,在那里,我们可以输入新英雄的名字,并把它们加到数据库中。
- 在`(click)`事件绑定中,我们使用[模板引用变量](template-syntax.html#ref-vars)`newHeroName`来访问这个输入框的值。
- 当用户点击此按钮时,我们把这个值传给组件的`addHero`方法,然后清除它,以备输入新英雄的名字。
+ 列表的下方是一个输入框和一个 *Add Hero* 按钮,在那里,我们可以输入新英雄的名字,
+ 并把它们加到数据库中。
+ 在`(click)`事件绑定中,使用[模板引用变量](template-syntax.html#ref-vars)`newHeroName`来访问这个输入框的值。
+ 当用户点击此按钮时,这个值传给组件的`addHero`方法,然后清除它,以备输入新英雄的名字。
Below the button is an area for an error message.
@@ -242,7 +248,8 @@ a#HeroListComponent
:marked
### The *HeroListComponent* class
- ### *HeroListComponent*类
+ ### *HeroListComponent* 类
+
Here's the component class:
下面是这个组件类:
@@ -257,20 +264,22 @@ a#HeroListComponent
The component doesn't know or care how it gets the data.
It delegates to the `HeroService`.
- 这个组件**不会直接和!{_Angular_Http}客户端打交道**!
+ 这个组件**不会直接和 !{_Angular_Http}客户端打交道**!
它既不知道也不关心我们如何获取数据,这些都被委托给了`HeroService`去做。
This is a golden rule: **always delegate data access to a supporting service class**.
- 这是一条“黄金法则”:**总是把数据访问工作委托给一个支持服务类**。
+ 这是一条黄金法则:**总是把数据访问工作委托给一个支持性服务类**。
Although _at runtime_ the component requests heroes immediately after creation,
you **don't** call the service's `get` method in the component's constructor.
Instead, call it inside the `ngOnInit` [lifecycle hook](lifecycle-hooks.html)
and rely on Angular to call `ngOnInit` when it instantiates this component.
- 虽然_在运行期间_,组件会在创建之后立刻请求这些英雄数据,但我们**不**在组件的构造函数中调用此服务的`get`方法。
- 而是在`ngOnInit`[生命周期钩子](lifecycle-hooks.html)中调用它,Angular会在初始化该组件时调用`ngOnInit`方法。
+ 虽然_在运行期间_,组件会在创建之后立刻请求这些英雄数据,
+ 但我们**不**在组件的构造函数中调用此服务的`get`方法。
+ 而是在`ngOnInit`[生命周期钩子](lifecycle-hooks.html)中调用它,
+ Angular 会在初始化该组件时调用`ngOnInit`方法。
.l-sub-section
:marked
This is a *best practice*.
@@ -278,12 +287,14 @@ a#HeroListComponent
(especially calling a remote server) is handled in a separate method.
这是*最佳实践*。
- 当组件的构造函数足够简单,并且所有真实的工作(尤其是调用远端服务器)都在一个独立的方法中处理时,组件会更加容易测试和调试。
+ 当组件的构造函数足够简单,并且所有真实的工作(尤其是调用远端服务器)
+ 都在一个独立的方法中处理时,组件会更加容易测试和调试。
block getheroes-and-addhero
:marked
The service's `getHeroes()` and `addHero()` methods return an `Observable` of hero data that the !{_Angular_Http} client fetched from the server.
- 服务的`getHeroes()`和`addHero()`方法返回一个英雄数据的可观察对象(`Observable`),这些数据是由!{_Angular_Http}从服务器上获取的。
+ 服务的`getHeroes()`和`addHero()`方法返回一个英雄数据的可观察对象 (`Observable`),
+ 这些数据是由 !{_Angular_Http}从服务器上获取的。
Think of an `Observable` as a stream of events published by some source.
To listen for events in this stream, ***subscribe*** to the `Observable`.
@@ -292,19 +303,20 @@ block getheroes-and-addhero
我们可以把可观察对象`Observable`看做一个由某些“源”发布的事件流。
通过***订阅***到可观察对象`Observable`,我们监听这个流中的事件。
- 在这些订阅中,我们指定了当Web请求生成了一个成功事件(有效载荷是英雄数据)或失败事件(有效载荷是错误对象)时该如何采取行动。
+ 在这些订阅中,我们指定了当 Web 请求生成了一个成功事件(有效载荷是英雄数据)
+ 或失败事件(有效载荷是错误对象)时该如何采取行动。
:marked
With a basic understanding of the component, you're ready to look inside the `HeroService`.
- 关于组件的浅显讲解已经结束了,我们可以到`HeroService`的内部实现中看看。
+ 有了对组件的基本理解,我们可以到`HeroService`的内部实现中看看。
a#HeroService
.l-main-section#fetch-data
:marked
## Fetch data with http.get
- ## 通过http.get获取数据
+ ## 通过 http.get 获取数据
In many of the previous samples the app faked the interaction with the server by
returning mock heroes in a service like this one:
@@ -315,14 +327,14 @@ a#HeroService
:marked
You can revise that `HeroService` to get the heroes from the server using the !{_Angular_Http} client service:
- 在本章中,我们会修改`HeroService`,改用“!{_Angular_Http}客户端”服务来从服务器上获取英雄列表:
+ 在本章中,我们会修改`HeroService`,改用 !{_Angular_Http}客户端服务来从服务器上获取英雄列表:
+makeExample('server-communication/ts/app/toh/hero.service.ts', 'v1', 'app/toh/hero.service.ts (revised)')
:marked
Notice that the !{_Angular_Http} client service is
[injected](dependency-injection.html) into the `HeroService` constructor.
- 注意,这个“!{_Angular_Http}客户端”服务[被注入](dependency-injection.html)到了`HeroService`的构造函数中。
+ 注意,这个 !{_Angular_Http}客户端服务[被注入](dependency-injection.html)到了`HeroService`的构造函数中。
+makeExample('server-communication/ts/app/toh/hero.service.ts', 'ctor')
:marked
Look closely at how to call `!{_priv}http.get`:
@@ -333,7 +345,7 @@ a#HeroService
:marked
You pass the resource URL to `get` and it calls the server which returns heroes.
- 我们把资源的URL传进`get`函数,它调用了服务器,而服务器应该返回英雄列表。
+ 我们把资源的 URL 传进`get`函数,它调用了服务器,而服务器应该返回英雄列表。
.l-sub-section
:marked
@@ -341,8 +353,8 @@ a#HeroService
described in the appendix below.
Alternatively, you can temporarily target a JSON file by changing the endpoint URL:
- 一旦我们按附录中所描述的那样准备好了[内存Web API](in-mem-web-api),它*将*返回英雄列表。
- 但目前,我们只能(临时性的)使用一个JSON文件来代替这个“内存Web API”。只要修改下服务器的URL就行了:
+ 一旦我们按附录中所描述的那样准备好了[内存 Web API](in-mem-web-api),它将返回英雄列表。
+ 但目前,你可以临时性地使用一个 JSON 文件,修改一下 URL:
+makeExample('server-communication/ts/app/toh/hero.service.ts', 'endpoint-json')(format=".")
+ifDocsFor('ts')
@@ -356,45 +368,47 @@ a#HeroService
返回值可能会让我们感到意外。
- 对熟悉现代JavaScript中的异步调用方法的人来说,我们期待`get`方法返回一个[承诺(promise)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)。
- 我们期待链接调用`then()`方法,并从中取得英雄列表。取而代之,这里调用了一个`map()`方法。
- 显然,这并不是承诺(Promise)。
+ 对熟悉现代 JavaScript 中的异步调用方法的人来说,我们期待`get`方法返回一个[承诺 (promise)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)。
+ 我们期待链接调用`then()`方法,并从中取得英雄列表。
+ 而这里调用了一个`map()`方法,显然,它不是承诺 (Promise)。
In fact, the `http.get` method returns an **Observable** of HTTP Responses (`Observable`) from the RxJS library
and `map` is one of the RxJS *operators*.
- 事实上,`http.get`方法返回了一个HTTP Response类型的**可观察对象**(`Observable`),这个对象来自RxJS库,而`map`是RxJS的*操作符*之一。
+ 事实上,`http.get`方法返回了一个 HTTP Response 类型的**可观察对象** (`Observable`),这个对象来自 RxJS 库,而`map`是 RxJS 的*操作符*之一。
.l-main-section
:marked
## RxJS library
## RxJS库
+
[RxJS](https://github.com/ReactiveX/RxJS) ("Reactive Extensions") is a 3rd party library, endorsed by Angular,
that implements the [*asynchronous observable*](https://www.youtube.com/watch?v=UHI0AzD_WfY "Rob Wormald on observables") pattern.
- [RxJS](https://github.com/ReactiveX/RxJS)("Reactive Extensions"的缩写)是一个被Angular认可的第三方库,它实现了
- [*异步可观察对象(asynchronous observable)*](https://www.youtube.com/watch?v=UHI0AzD_WfY "Rob Wormald on observables")模式。
+ [RxJS](https://github.com/ReactiveX/RxJS) ("Reactive Extensions" 的缩写) 是一个被 Angular 认可的第三方库,
+ 它实现了[*异步可观察对象 (asynchronous observable)*](https://www.youtube.com/watch?v=UHI0AzD_WfY "Rob Wormald on observables") 模式。
All of the Developer Guide samples have installed the RxJS npm package and loaded via `system.js`
because observables are used widely in Angular applications.
- 本开发指南中的所有例子都安装了RxJS的npm包,而且都被`system.js`加载过了。这是因为可观察对象在Angular应用中使用非常广泛。
+ 开发指南中的所有例子都安装了 RxJS 的 npm 包,而且都被`system.js`加载过了。
+ 这是因为可观察对象在 Angular 应用中使用非常广泛。
The app needs it when working with the HTTP client.
Additionally, you must take a critical extra step to make RxJS observables usable.
- HTTP客户端更需要它。经过一个关键步骤,我们才能让RxJS可观察对象可用。
+ HTTP 客户端更需要它。经过一个关键步骤,我们才能让 RxJS 可观察对象可用。
### Enable RxJS operators
- ### 启用RxJS操作符
+ ### 启用 RxJS 操作符
The RxJS library is large.
Size matters when building a production application and deploying it to mobile devices.
You should include only necessary features.
- RxJS库实在是太大了。
+ RxJS 库实在是太大了。
当构建一个产品级应用,并且把它发布到移动设备上的时候,大小就会成为一个问题。
我们应该只包含那些我们确实需要的特性。
@@ -402,7 +416,7 @@ a#HeroService
module that lacks most of the operators such as the `map` method you
called above in `getHeroes`.
- 因此,Angular在`rxjs/Observable`模块中导出了一个精简版的`Observable`类,这个版本缺少很多操作符,
+ 因此,Angular 在`rxjs/Observable`模块中导出了一个精简版的`Observable`类,这个版本缺少很多操作符,
比如我们在上面的`getHeroes`方法中用过的`map`函数。
It's up to you to add the operators you need.
@@ -413,7 +427,7 @@ a#HeroService
While that is the easiest thing to do, you'd pay a penalty in extended launch time
and application size because the full library is so big.
- 我们可以通过一条import语句把*每个*RxJS操作符都添加进来。
+ 我们可以通过一条 import 语句把*每个* RxJS 操作符都添加进来。
虽然这是最简单的方式,但我们也得付出代价,主要是在启动时间和应用大小上,因为完整的库实在太大了。
而我们其实只需要用到少量操作符。
@@ -423,20 +437,20 @@ a#HeroService
precisely to the app's requirements. Put the `import` statements in one `app/rxjs-operators.ts` file.
因为本应用只使用了少许操作符,所以将一个一个的导入`Observable`的操作符和静态类方法比较合适,
- 直到我们得到了一个精确符合我们需求的自定义*Observable*实现。
+ 直到我们得到了一个精确符合我们需求的自定义 *Observable* 实现。
我们将把这些`import`语句放进一个`app/rxjs-operators.ts`文件里。
+makeExample('server-communication/ts/app/rxjs-operators.ts', null, 'app/rxjs-operators.ts')(format=".")
:marked
If you forget an operator, the TypeScript compiler warns that it's missing and you'll update this file.
- 如果忘了导入某个操作符,TypeScript编译器就会警告说找不到它,那时候我们再来更新此文件。
+ 如果忘了导入某个操作符,TypeScript 编译器就会警告说找不到它,那时候我们再来更新此文件。
.l-sub-section
:marked
The app doesn't need _all_ of these particular operators in the `HeroService` — just `map`, `catch` and `throw`.
The other operators are for later, in the *Wiki* example [below](#more-observables).
- 在`HeroService`中,我们并不需要在这里导入的_全部_操作符 —— 我们只用到了`map`、`catch`和`throw`。
- 我们稍后的[*Wiki*例子](#more-observables)中,还会用到其它操作符。
+ 在`HeroService`中,我们并不需要在这里导入的_全部_操作符 — 我们只用到了`map`、`catch`和`throw`。
+ 我们稍后的 [*Wiki* 例子](#more-observables)中,还会用到其它操作符。
:marked
Finally, import `rxjs-operator` into `app.component.ts`:
@@ -453,7 +467,7 @@ a#extract-data
:marked
## Process the response object
- ## 处理Response响应对象
+ ## 处理响应对象
Remember that the `getHeroes()` method used an `!{_priv}extractData` helper method to map the `!{_priv}http.get` response object to heroes:
@@ -464,18 +478,18 @@ a#extract-data
You must parse the response data into a JSON object.
这个`response`对象并没有以一种我们能直接使用的格式来保存数据。
- 要让它在应用程序中可用,我们就必须把这个响应数据解析成一个JSON对象。
+ 要让它在应用程序中可用,我们就必须把这个响应数据解析成一个 JSON 对象。
### Parse to JSON
- ### 解析成JSON
+ ### 解析成 JSON
block parse-json
:marked
The response data are in JSON string form.
The app must parse that string into JavaScript objects by calling `response.json()`.
- 响应数据是JSON字符串格式的。
- 我们必须把这个字符串解析成JavaScript对象 —— 只要调一下`response.json()`就可以了。
+ 响应数据是 JSON 字符串格式的。
+ 我们必须把这个字符串解析成 JavaScript 对象 —— 只要调一下`response.json()`就可以了。
.l-sub-section
:marked
@@ -484,9 +498,9 @@ block parse-json
[response object](https://fetch.spec.whatwg.org/#response-class) returned by the `Fetch` function.
That spec defines a `json()` method that parses the response body into a JavaScript object.
- 这不是Angular自己的设计。
- Angular HTTP客户端遵循ES2015规范来处理`Fetch`函数返回的[响应对象](https://fetch.spec.whatwg.org/#response-class)。
- 此规范中定义了一个`json()`函数,来把响应体解析成JavaScript对象。
+ 这不是 Angular 自己的设计。
+ Angular HTTP 客户端遵循 ES2015 规范来处理`Fetch`函数返回的[响应对象](https://fetch.spec.whatwg.org/#response-class)。
+ 此规范中定义了一个`json()`函数,来把响应体解析成 JavaScript 对象。
.l-sub-section
:marked
@@ -496,9 +510,9 @@ block parse-json
This is conventional web API behavior, driven by
[security concerns](https://www.owasp.org/index.php/OWASP_AJAX_Security_Guidelines#Always_return_JSON_with_an_Object_on_the_outside).
- 我们不应该期待解码后的JSON直接就是一个英雄数组。
- 调用的这个服务器总会把JSON结果包装进一个带`data`属性的对象中。
- 我们必须解开它才能得到英雄数组。这是一个约定俗成的Web API行为规范,它是出于
+ 我们不应该期待解码后的 JSON 直接就是一个英雄数组。
+ 调用的这个服务器总会把 JSON 结果包装进一个带`data`属性的对象中。
+ 我们必须解开它才能得到英雄数组。这是一个约定俗成的 Web API 行为规范,它是出于
[安全方面的考虑](https://www.owasp.org/index.php/OWASP_AJAX_Security_Guidelines#Always_return_JSON_with_an_Object_on_the_outside)。
.alert.is-important
@@ -506,12 +520,12 @@ block parse-json
Make no assumptions about the server API.
Not all servers return an object with a `data` property.
- 不要对服务端API做任何假设。
+ 不要对服务端 API 做任何假设。
并非所有服务器都会返回一个带`data`属性的对象。
:marked
### Do not return the response object
- ### 不要返回响应(Response)对象
+ ### 不要返回响应对象
The `getHeroes()` method _could_ have returned the HTTP response but this wouldn't
be a best practice.
@@ -519,7 +533,7 @@ block parse-json
The component that calls the `HeroService` only wants heroes and is kept separate
from getting them, the code dealing with where they come from, and the response object.
- `getHeroes()`确实可以返回HTTP响应对象,但这不是最佳实践。
+ `getHeroes()`确实可以返回 HTTP 响应对象,但这不是最佳实践。
数据服务的重点在于,对消费者隐藏与服务器交互的细节。
调用`HeroService`的组件希望得到英雄数组。
它并不关心我们如何得到它们。
@@ -530,7 +544,7 @@ block parse-json
.callout.is-important
header HTTP GET is delayed
- header HTTP的GET方法被推迟执行了
+ header HTTP 的 GET 方法被推迟执行了
:marked
The `!{_priv}http.get` does **not send the request just yet.** This observable is
[*cold*](https://github.com/Reactive-Extensions/RxJS/blob/master/doc/gettingstarted/creating.md#cold-vs-hot-observables),
@@ -538,7 +552,7 @@ block parse-json
That *something* is the [HeroListComponent](#subscribe).
`#{_priv}http.get`**仍然没有发送请求!**这是因为可观察对象是
- [*冷淡的*](https://github.com/Reactive-Extensions/RxJS/blob/master/doc/gettingstarted/creating.md#cold-vs-hot-observables),
+ [*冷的*](https://github.com/Reactive-Extensions/RxJS/blob/master/doc/gettingstarted/creating.md#cold-vs-hot-observables),
也就是说,只有当某人*订阅*了这个可观察对象时,这个请求才会被发出。
这个场景中的*某人*就是[HeroListComponent](#subscribe)。
@@ -553,7 +567,7 @@ a#error-handling
back to the component for presentation to the user,
but only if it says something that the user can understand and act upon.
- 一旦开始与I/O打交道,我们就必须准备好接受墨菲定律:如果一件倒霉事*可能*发生,它就*迟早会*发生。
+ 一旦开始与 I/O 打交道,我们就必须准备好接受墨菲定律:如果一件倒霉事*可能*发生,它就*迟早会*发生。
我们可以在`HeroService`中捕获错误,并对它们做些处理。
只有在用户可以理解并采取相应行动的时候,我们才把错误信息传回到组件,让组件展示给最终用户。
@@ -570,7 +584,7 @@ block error-handling
logs it to the console, and returns the message in a new, failed observable via `Observable.throw`.
`catch`操作符将错误对象传递给`http`的`handleError`方法。
- 服务处理器(`handleError`)把响应对象记录到控制台中,
+ 服务处理器 (`handleError`) 把响应对象记录到控制台中,
把错误转换成对用户友好的消息,并且通过`Observable.throw`来把这个消息放进一个新的、用于表示“失败”的可观察对象。
+makeExample('server-communication/ts/app/toh/hero.service.ts', 'error-handling', 'app/toh/hero.service.ts (excerpt)')(format=".")
@@ -595,7 +609,7 @@ block hlc-error-handling
:marked
Want to see it fail? In the `HeroService`, reset the api endpoint to a bad value. Afterward, remember to restore it.
- 想看到它失败时的情况吗?在`HeroService`中把API的端点设置为一个无效值就行了。但别忘了恢复它。
+ 想看到它失败时的情况吗?在`HeroService`中把 API 的端点设置为一个无效值就行了。但别忘了恢复它。
@@ -608,7 +622,7 @@ block hlc-error-handling
So far you've seen how to retrieve data from a remote location using an HTTP service.
Now you'll add the ability to create new heroes and save them in the backend.
- 前面我们已经看到如何用一个HTTP服务从远端获取数据了。
+ 前面我们已经看到如何用一个 HTTP 服务从远端获取数据了。
但我们还能再给力一点,让它可以创建新的英雄,并把它们保存到后端。
You'll write a method for the `HeroListComponent` to call, an `addHero()` method, that takes
@@ -621,7 +635,7 @@ block hlc-error-handling
:marked
To implement it, you must know the server's API for creating heroes.
- 要实现它,我们得知道关于服务端API如何创建英雄的一些细节。
+ 要实现它,我们得知道关于服务端 API 如何创建英雄的一些细节。
[This sample's data server](#server) follows typical REST guidelines.
It expects a [`POST`](http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.5) request
@@ -630,9 +644,9 @@ block hlc-error-handling
structured like a `Hero` entity but without the `id` property.
The body of the request should look like this:
- 我们的[数据服务器](#server)遵循典型的REST指导原则。
+ 我们的[数据服务器](#server)遵循典型的 REST 指导原则。
它期待在和`GET`英雄列表的同一个端点上存在一个[`POST`](http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.5)请求。
- 它期待从请求体(body)中获得新英雄的数据,数据的结构和`Hero`对象相同,但是不带`id`属性。
+ 它期待从请求体 (body) 中获得新英雄的数据,数据的结构和`Hero`对象相同,但是不带`id`属性。
请求体应该看起来像这样:
code-example(format="." language="javascript").
@@ -642,11 +656,12 @@ code-example(format="." language="javascript").
of the new hero including its generated id. The hero arrives tucked inside a response object
with its own `data` property.
- 服务器将生成`id`,并且返回新英雄的完整`JSON`形式,包括这个生成的id。该英雄的数据被塞进一个响应对象的`data`属性中。
+ 服务器将生成`id`,并且返回新英雄的完整`JSON`形式,包括这个生成的 id。
+ 该英雄的数据被塞进一个响应对象的`data`属性中。
Now that you know how the API works, implement `addHero()`as follows:
- 现在,知道了这个API如何工作,我们就可以像这样实现`addHero()`了:
+ 现在,知道了这个 API 如何工作,我们就可以像这样实现`addHero()`了:
+ifDocsFor('ts')
+makeExample('server-communication/ts/app/toh/hero.service.ts', 'import-request-options', 'app/toh/hero.service.ts (additional imports)')(format=".")
@@ -655,11 +670,11 @@ code-example(format="." language="javascript").
:marked
### Headers
- ### 请求头(Headers)
+ ### 请求头 (headers)
In the `headers` object, the `Content-Type` specifies that the body represents JSON.
- 我们通过`Content-Type`头告诉服务器,body是JSON格式的。
+ 我们通过`Content-Type`头告诉服务器,body 是 JSON 格式的。
+ifDocsFor('ts')
:marked
@@ -669,7 +684,7 @@ code-example(format="." language="javascript").
接下来,使用`headers`对象来配置`options`对象。
`options`对象是`RequestOptions`的新实例,该类允许你在实例化请求时指定某些设置。这样,
- [Headers](../api/http/index/Headers-class.html)是[RequestOptions](../api/http/index/RequestOptions-class.html)中的一员。
+ [Headers](../api/http/index/Headers-class.html) 是 [RequestOptions](../api/http/index/RequestOptions-class.html) 中的一员。
In the `return` statement, `options` is the *third* argument of the `post` method, as shown above.
@@ -678,7 +693,7 @@ code-example(format="." language="javascript").
:marked
### JSON results
- ### JSON结果
+ ### JSON 结果
As with `getHeroes()`, use the `!{_priv}extractData()` helper to [extract the data](#extract-data)
from the response.
@@ -691,22 +706,22 @@ block hero-list-comp-add-hero
When the data arrive it pushes the new hero object into its `heroes` array for presentation to the user.
回到`HeroListComponent`,我们看到*该组件的*`addHero()`方法中订阅了这个由*服务中*的`addHero()`方法返回的可观察对象。
- 当有数据到来时,它就会把这个新的英雄对象追加(push)到`heroes`数组中,以展现给用户。
+ 当有数据到来时,它就会把这个新的英雄对象追加 (push) 到`heroes`数组中,以展现给用户。
+makeExample('server-communication/ts/app/toh/hero-list.component.ts', 'addHero', 'app/toh/hero-list.component.ts (addHero)')(format=".")
+ifDocsFor('ts')
h2#promises Fall back to promises
- h2#promises 倒退为承诺(Promise)
+ h2#promises 倒退为承诺 (Promise)
:marked
Although the Angular `http` client API returns an `Observable` you can turn it into a
[`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).
It's easy to do, and in simple cases, a promise-based version looks much
like the observable-based version.
- 虽然Angular的`http`客户端API返回的是`Observable`类型的对象,但我们也可以把它转成
+ 虽然 Angular 的`http`客户端 API 返回的是`Observable`类型的对象,但我们也可以把它转成
[`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)。
- 这很容易,并且在简单的场景中,一个基于承诺(Promise)的版本看起来很像基于可观察对象(Observable)的版本。
+ 这很容易,并且在简单的场景中,一个基于承诺 (Promise) 的版本看起来很像基于可观察对象 (Observable) 的版本。
.l-sub-section
:marked
While promises may be more familiar, observables have many advantages.
@@ -774,16 +789,16 @@ block hero-list-comp-add-hero
To understand the implications and consequences of subscriptions, watch [Ben Lesh's talk on observables](https://www.youtube.com/watch?v=3LKMwkuK0ZE) or his video course on [egghead.io](https://egghead.io/lessons/rxjs-rxjs-observables-vs-promises).
- 要理解订阅Subscription的实现和效果,请看[Ben Lesh关于可观察对象的演讲](https://www.youtube.com/watch?v=3LKMwkuK0ZE)
- 或者他在[egghead.io](https://egghead.io/lessons/rxjs-rxjs-observables-vs-promises)的课程。
+ 要理解订阅的实现和效果,请看 [Ben Lesh 关于可观察对象的演讲](https://www.youtube.com/watch?v=3LKMwkuK0ZE)
+ 或者他在 [egghead.io](https://egghead.io/lessons/rxjs-rxjs-observables-vs-promises) 的课程。
h2#cors Cross-Origin Requests: Wikipedia example
-h2#cors 跨域请求:Wikipedia范例
+h2#cors 跨域请求:Wikipedia 范例
:marked
You just learned how to make `XMLHttpRequests` using the !{_Angular_Http} service.
This is the most common approach for server communication, but it doesn't work in all scenarios.
- 我们刚刚学习了用!{_Angular_Http}服务发起`XMLHttpRequests`。
+ 我们刚刚学习了用 !{_Angular_Http}服务发起`XMLHttpRequests`。
这是与服务器通讯时最常用的方法。
但它不适合所有场景。
@@ -792,7 +807,7 @@ h2#cors 跨域请求:Wikipedia范例
This is called the [same-origin policy](https://en.wikipedia.org/wiki/Same-origin_policy).
出于安全的考虑,网络浏览器会阻止调用与当前页面不“同源”的远端服务器的`XHR`。
- 所谓*源*就是URI的协议(scheme)、主机名(host)和端口号(port)这几部分的组合。
+ 所谓*源*就是 URI 的协议 (scheme)、主机名 (host) 和端口号 (port) 这几部分的组合。
这被称为[同源策略](https://en.wikipedia.org/wiki/Same-origin_policy)。
.l-sub-section
@@ -801,29 +816,29 @@ h2#cors 跨域请求:Wikipedia范例
[CORS](https://en.wikipedia.org/wiki/Cross-origin_resource_sharing) protocol.
If the server requires user credentials, you'll enable them in the [request headers](#headers).
- 在现代浏览器中,如果服务器支持[CORS](https://en.wikipedia.org/wiki/Cross-origin_resource_sharing)协议,那么也可以向不同源的服务器发起`XHR`请求。
+ 在现代浏览器中,如果服务器支持 [CORS](https://en.wikipedia.org/wiki/Cross-origin_resource_sharing) 协议,那么也可以向不同源的服务器发起`XHR`请求。
如果服务器要请求用户凭证,我们就在[请求头](#headers)中启用它们。
:marked
Some servers do not support CORS but do support an older, read-only alternative called [JSONP](https://en.wikipedia.org/wiki/JSONP).
Wikipedia is one such server.
- 有些服务器不支持CORS,但支持一种老的、只读的(译注:即仅支持GET)备选协议,这就是[JSONP](https://en.wikipedia.org/wiki/JSONP)。
+ 有些服务器不支持 CORS,但支持一种老的、只读的(译注:即仅支持 GET)备选协议,这就是 [JSONP](https://en.wikipedia.org/wiki/JSONP)。
Wikipedia就是一个这样的服务器。
.l-sub-section
:marked
This [Stack Overflow answer](http://stackoverflow.com/questions/2067472/what-is-jsonp-all-about/2067584#2067584) covers many details of JSONP.
- 这个[StackOverflow上的答案](http://stackoverflow.com/questions/2067472/what-is-jsonp-all-about/2067584#2067584)覆盖了关于JSONP的很多细节。
+ 这个 [StackOverflow 上的答案](http://stackoverflow.com/questions/2067472/what-is-jsonp-all-about/2067584#2067584)覆盖了关于 JSONP 的很多细节。
:marked
### Search wikipedia
- ### 搜索Wikipedia
+ ### 搜索 Wikipedia
Here is a simple search that shows suggestions from Wikipedia as the user
types in a text box:
- 我们来构建一个简单的搜索程序,当我们在文本框中输入时,它会从Wikipedia中获取并显示建议的词汇列表:
+ 我们来构建一个简单的搜索程序,当我们在文本框中输入时,它会从 Wikipedia 中获取并显示建议的词汇列表:
figure.image-display
img(src='/resources/images/devguide/server-communication/wiki-1.gif' alt="Wikipedia search app (v.1)" width="250")
@@ -834,13 +849,13 @@ block wikipedia-jsonp+
The Angular `Jsonp` service both extends the `!{_Http}` service for JSONP and restricts you to `GET` requests.
All other HTTP methods throw an error because JSONP is a read-only facility.
- Wikipedia提供了一个现代的`CORS` API和一个传统的`JSONP`搜索API。在这个例子中,我们使用后者。
- Angular的`Jsonp`服务不但通过JSONP扩展了`#{_Http}`服务,而且限制我们只能用`GET`请求。
- 尝试调用所有其它HTTP方法都将抛出一个错误,因为JSONP是只读的。
+ Wikipedia 提供了一个现代的`CORS` API和一个传统的`JSONP`搜索 API。在这个例子中,我们使用后者。
+ Angular 的`Jsonp`服务不但通过 JSONP 扩展了`#{_Http}`服务,而且限制我们只能用`GET`请求。
+ 尝试调用所有其它 HTTP 方法都将抛出一个错误,因为 JSONP 是只读的。
As always, wrap the interaction with an Angular data access client service inside a dedicated service, here called `WikipediaService`.
- 像往常一样,我们把和Angular数据访问服务进行交互的代码全都封装在一个专门的服务中。我们称之为`WikipediaService`。
+ 像往常一样,我们把和 Angular 数据访问服务进行交互的代码全都封装在一个专门的服务中。我们称之为`WikipediaService`。
+makeExample('server-communication/ts/app/wiki/wikipedia.service.ts',null,'app/wiki/wikipedia.service.ts')
:marked
@@ -848,7 +863,7 @@ block wikipedia-jsonp+
is available because `JsonpModule` is in the root `@NgModule` `imports` array
in `app.module.ts`.
- 这个构造函数期望Angular给它注入一个`jsonp`服务。
+ 这个构造函数期望 Angular 给它注入一个`jsonp`服务。
前面我们已经把`JsonpModule`导入到了根模块中,所以这个服务已经可以使用了。
@@ -863,10 +878,10 @@ block wikipedia-jsonp+
The value of the `search` key is the user-supplied search term to find in Wikipedia.
The other three are the fixed values "opensearch", "json", and "JSONP_CALLBACK" respectively.
- [Wikipedia 的 'opensearch' API](https://www.mediawiki.org/wiki/API:Opensearch)期待在所请求的URL中带四个查询参数(键/值对格式)。
- 这些键(key)分别是`search`、`action`、`format`和`callback`。
- `search`的值是用户提供的用于在Wikipedia中查找的关键字。
- 另外三个参数是固定值,分别是"opensearch"、"json"和"JSONP_CALLBACK"。
+ [Wikipedia 的 'opensearch' API](https://www.mediawiki.org/wiki/API:Opensearch) 期待在所请求的 URL 中带四个查询参数(键/值对格式)。
+ 这些键 (key) 分别是`search`、`action`、`format`和`callback`。
+ `search`的值是用户提供的用于在 Wikipedia 中查找的关键字。
+ 另外三个参数是固定值,分别是 "opensearch"、"json" 和 "JSONP_CALLBACK"。
.l-sub-section
:marked
The `JSONP` technique requires that you pass a callback function name to the server in the query string: `callback=JSONP_CALLBACK`.
@@ -874,17 +889,17 @@ block wikipedia-jsonp+
All of this happens under the hood.
`JSONP`技术需要我们通过查询参数传给服务器一个回调函数的名字:`callback=JSONP_CALLBACK`。
- 服务器使用这个名字在它的响应体中构建一个JavaScript包装函数,Angular最终会调用这个包装函数来提取出数据。
- 这些都是Angular在背后默默完成的,你不会感受到它。
+ 服务器使用这个名字在它的响应体中构建一个 JavaScript 包装函数,Angular 最终会调用这个包装函数来提取出数据。
+ 这些都是 Angular 在背后默默完成的,你不会感受到它。
:marked
If you're looking for articles with the word "Angular", you could construct the query string by hand and call `jsonp` like this:
- 如果我们要找那些含有关键字“Angular”的文档,我们可以先手工构造出查询字符串,并像这样调用`jsonp`:
+ 如果我们要找那些含有关键字 “Angular” 的文档,我们可以先手工构造出查询字符串,并像这样调用`jsonp`:
+makeExample('server-communication/ts/app/wiki/wikipedia.service.1.ts','query-string')(format='.')
:marked
In more parameterized examples you could build the query string with the Angular `URLSearchParams` helper:
- 在更加参数化的例子中,我们会首选Angular的`URLSearchParams`辅助类来构建查询字符串,就像这样:
+ 在更加参数化的例子中,我们会首选 Angular 的`URLSearchParams`辅助类来构建查询字符串,就像这样:
+makeExample('server-communication/ts/app/wiki/wikipedia.service.ts','search-parameters','app/wiki/wikipedia.service.ts (search parameters)')(format=".")
:marked
This time you call `jsonp` with *two* arguments: the `wikiUrl` and an options object whose `search` property is the `params` object.
@@ -900,12 +915,12 @@ block wikipedia-jsonp+
:marked
### The WikiComponent
- ### WikiComponent组件
+ ### WikiComponent 组件
Now that you have a service that can query the Wikipedia API
turn to the component (template and class) that takes user input and displays search results.
- 现在,我们有了一个可用于查询Wikpedia API的服务,
+ 现在,我们有了一个可用于查询 Wikpedia API 的服务,
我们重新回到组件中,接收用户输入,并显示搜索结果。
+makeExample('server-communication/ts/app/wiki/wiki.component.html', null, 'app/wiki/wiki.component.html')
@@ -914,7 +929,7 @@ block wikipedia-jsonp+
The template presents an `` element *search box* to gather search terms from the user,
and calls a `search(term)` method after each `keyup` event.
- 该组件有一个``元素,它是用来从用户获取搜索关键词的*搜索框*。
+ 该模板有一个``元素,它是用来从用户获取搜索关键词的*搜索框*。
在每次`keyup`事件被触发时,它调用`search(term)`方法。
+makeExample('server-communication/ts/app/wiki/wiki.component.html', 'keyup', 'wiki/wiki.component.html')(format='.')
@@ -926,18 +941,21 @@ block wikipedia-jsonp+
in the `ngFor` handles the subscription. Read more about [async pipes](pipes.html#async-pipe)
in the [Pipes](pipes.html) page.
- `search(term)`方法委托我们的`WikipediaService`服务来完成实际操作。该服务返回的是一个字符串数组的可观察对象(`Observable`)。
- 该组件的内部订阅了这个可观察对象,就像我们曾在`HeroListComponent`中所做的那样,
- 我们把这个可观察对象作为结果传给模板(通过`items`属性),模板中`ngFor`上的[async(异步)管道](pipes.html#async-pipe)会对这个订阅进行处理。
+ `search(term)`方法委托`WikipediaService`服务来完成实际操作。
+ 该服务返回的是一个字符串数组的可观察对象 (`Observable`)。
+ 没有像`HeroListComponent`那样在组件内部订阅这个可观察对象,
+ 我们把这个可观察对象作为结果传给模板(通过`items`属性),
+ 模板中`ngFor`上的 async(异步)管道会对这个订阅进行处理。
+ 关于[异步管理](pipes.html#async-pipe)的更多信息,见 [Pipes](pipes.html)。
.l-sub-section
:marked
The [async pipe](pipes.html#async-pipe) is a good choice in read-only components where the component has no need to interact with the data.
- 我们通常在只读组件中使用[async管道](pipes.html#async-pipe),这种组件不需要与数据进行互动。
+ 我们通常在只读组件中使用[异步管道](pipes.html#async-pipe),这种组件不需要与数据进行互动。
`HeroListComponent` can't use the pipe because `addHero()` pushes newly created heroes into the list.
- 但我们不能在`HeroListComponent`中使用这个管道,这是因为“添加新英雄”特性会把一个新创建的英雄追加到英雄列表中。
+ 但我们不能在`HeroListComponent`中使用这个管道,这是因为`addHero()`会把一个新创建的英雄追加到英雄列表中。
:marked
## A wasteful app
@@ -947,7 +965,7 @@ block wikipedia-jsonp+
The wikipedia search makes too many calls to the server.
It is inefficient, and potentially expensive on mobile devices with limited data plans.
- 我们这个Wikipedia搜索程序触发了过多的服务器调用(每次按键发一次)。
+ 这个 Wikipedia 搜索程序触发了过多的服务器调用。
这样效率很低,而且在流量受限的移动设备上会显得过于昂贵。
### 1. Wait for the user to stop typing
@@ -960,7 +978,7 @@ block wikipedia-jsonp+
我们目前会在每次按键之后调用服务器。
但合理的方式是只在用户*停止输入*之后才发起请求。
- 这是它*应该*而且*即将使用*的工作方式,我们马上就重构它。
+ 重构之后,它将这样工作:
figure.image-display
img(src='/resources/images/devguide/server-communication/wiki-2.gif' alt="Wikipedia search app (v.2)" width="250")
:marked
@@ -971,14 +989,14 @@ block wikipedia-jsonp+
Suppose a user enters the word *angular* in the search box and pauses for a while.
The application issues a search request for *angular*.
- 假设用户在输入框中输入了单词*angular*,然后稍等片刻。
- 应用程序就会发出一个对*Angular*的搜索请求。
+ 假设用户在输入框中输入了单词 *angular*,然后稍等片刻。
+ 应用程序就会发出一个对 *angular* 的搜索请求。
Then the user backspaces over the last three letters, *lar*, and immediately re-types *lar* before pausing once more.
The search term is still _angular_. The app shouldn't make another request.
- 然后,用户用退格键删除了最后三个字符*lar*,并且毫不停顿的重新输入了*lar*。
- 搜索关键词仍然是“angular”。这时应用程序不应该发起另一个请求。
+ 然后,用户用退格键删除了最后三个字符 *lar*,并且毫不停顿的重新输入了 *lar*。
+ 搜索关键词仍然是 “angular”。这时应用程序不应该发起另一个请求。
### 3. Cope with out-of-order responses
@@ -987,8 +1005,8 @@ block wikipedia-jsonp+
The user enters *angular*, pauses, clears the search box, and enters *http*.
The application issues two search requests, one for *angular* and one for *http*.
- 用户输入了*angular*,暂停,清除搜索框,然后输入*http*。
- 应用程序发起了两个搜索请求,一个搜*angular*,一个搜*http*。
+ 用户输入了 *angular*,暂停,清除搜索框,然后输入 *http*。
+ 应用程序发起了两个搜索请求,一个搜 *angular*,一个搜 *http*。
Which response arrives first? It's unpredictable.
A load balancer could dispatch the requests to two different servers with different response times.
@@ -997,36 +1015,37 @@ block wikipedia-jsonp+
哪一个响应会先回来?我们是没法保证的。
负载均衡器可能把这个请求分发给了响应时间不同的两台服务器。
- 搜*angular*的结果可能晚于稍后搜*http*的结果。
- 用户可能会困惑:为什么搜*http*时显示了关于*angular*的结果。
+ 搜 *angular* 的结果可能晚于稍后搜 *http* 的结果。
+ 用户可能会困惑:为什么搜 *http* 时显示了关于 *angular* 的结果。
When there are multiple requests in-flight, the app should present the responses
in the original request order. That won't happen if *angular* results arrive last.
- 即使有多个尚未返回的请求,应用程序也应该按照原始请求的顺序展示对它们的响应。如果能让*angular*的结果始终在后面返回,就不会发生这样的混乱了。
+ 即使有多个尚未返回的请求,应用程序也应该按照原始请求的顺序展示对它们的响应。
+ 如果能让 *angular* 的结果始终在后面返回,就不会发生这样的混乱了。
## More fun with observables
- ## Observable的更多乐趣
+ ## Observable 的更多乐趣
You can address these problems and improve the app with the help of some nifty observable operators.
- 借助一些漂亮的可观察对象操作符,我们可以解决这些问题,并提升我们的应用程序。
+ 借助一些漂亮的可观察对象操作符,我们可以解决这些问题,并改进我们的应用程序。
You could make changes to the `WikipediaService`, but for a better
user experience, create a copy of the `WikiComponent` instead and make it smarter.
Here's the `WikiSmartComponent` which uses the same template.
我们本可以把这些改动合并进`WikipediaService`中,但是为了更好用户体验,
- 转而拷贝`WikiComponent`,把它变得更智能。
+ 我们创建一个`WikiComponent`的复本,让它变得更智能。
下面是`WikiSmartComponent`,它使用同样的模板:
+makeExample('server-communication/ts/app/wiki/wiki-smart.component.ts', null, 'app/wiki/wiki-smart.component.ts')
:marked
### Create a stream of search terms
- ### 创建一个由搜索关键字组成的“流(Stream)”
+ ### 创建一个搜索关键字流
The template still binds to the search box `keyup` event and passes the complete search box value
into the component's `search` method after every user keystroke.
@@ -1037,7 +1056,7 @@ block wikipedia-jsonp+
The `WikiSmartComponent` turns the search box values into an observable _stream of search terms_
with the help of a `Subject` which you import from the RxJS observable library:
- 利用从RxJS库导入的`Subject`,`WikiSmartComponent`将搜索框的值变为一个*搜索关键词流*可观察对象:
+ 利用从 RxJS 库导入的`Subject`,`WikiSmartComponent`将搜索框的值变为一个_搜索关键词流_可观察对象:
+makeExample('server-communication/ts/app/wiki/wiki-smart.component.ts', 'import-subject', 'app/wiki/wiki-smart.component.ts')
:marked
The component creates a `searchTermStream` as a `Subject` of type `string`.
@@ -1055,11 +1074,11 @@ block wikipedia-jsonp+
Earlier, you passed each search term directly to the service and bound the template to the service results.
- 以前,我们每次都把搜索关键字直接传给服务,并且把模板绑定到服务返回的结果。
+ 之前,我们每次都把搜索关键字直接传给服务,并且把模板绑定到服务返回的结果。
Now you listen to the *stream of search terms*, manipulating the stream before it reaches the `WikipediaService`.
- 而现在我们在监听*关键字组成的流*,并在把它传给`WikipediaService`之前操作这个流。
+ 而现在我们在监听*搜索关键字流*,并在把它传给`WikipediaService`之前操作这个流。
+makeExample('server-communication/ts/app/wiki/wiki-smart.component.ts', 'observable-operators',
'app/wiki/wiki-smart.component.ts')(format='.')
@@ -1069,16 +1088,16 @@ block wikipedia-jsonp+
Only changed search values make it through to the service
([_distinctUntilChanged_](https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/distinctuntilchanged.md)).
- 我们先等待用户停止输入至少300毫秒
+ 我们先等待用户停止输入至少300毫秒
([debounceTime](https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/debounce.md))。
- 只有当搜索关键字变化的时候,才把它传给服务
+ 只有当搜索关键字变化的时候,才把它传给服务
([distinctUntilChanged](https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/distinctuntilchanged.md))。
The `WikipediaService` returns a separate observable of string arrays (`Observable`) for each request.
There could be multiple requests *in-flight*, all awaiting the server's reply,
which means multiple *observables-of-strings* could arrive at any moment in any order.
- `WikipediaService`服务为每个请求返回一个独立的可观察的字符串数组(`Observable`)。
+ `WikipediaService`服务为每个请求返回一个独立的可观察的字符串数组 (`Observable`)。
我们可以同时有多个*发送中*的请求,它们都在等服务器的回复,
这意味着多个*可观察的字符串数组*有可能在任何时刻以任何顺序抵达。
@@ -1087,8 +1106,8 @@ block wikipedia-jsonp+
re-arranges them in their original request order,
and delivers to subscribers only the most recent search results.
- [switchMap](https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/flatmaplatest.md)(以前叫`flatMapLatest`)
- 返回一个新的可观察对象,它组合了所有这些“可观察的字符串数组”,重新按照它们的原始请求顺序进行排列,然后把最近的一个搜索结果交付给调用者。
+ [switchMap](https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/flatmaplatest.md)(以前叫`flatMapLatest`)
+ 返回一个新的可观察对象,它组合了所有这些“可观察的字符串数组”,重新按照它们的原始请求顺序进行排列,然后把最近的一个搜索结果交付给订阅者。
The displayed list of search results stays in sync with the user's sequence of search terms.
@@ -1098,7 +1117,7 @@ block wikipedia-jsonp+
You added the `debounceTime`, `distinctUntilChanged`, and `switchMap` operators to the RxJS `Observable` class
in `rxjs-operators` as [described above](#rxjs).
- 在[前面提过的](#rxjs)`rxjs-operators`文件中,我们把`debounceTime`、`distinctUntilChanged`和`switchMap`操作符加到了RxJS的`Observable`类中。
+ 在[前面提过的](#rxjs)`rxjs-operators`文件中,我们把`debounceTime`、`distinctUntilChanged`和`switchMap`操作符加到了 RxJS 的`Observable`类中。
a#xsrf
.l-main-section
@@ -1110,37 +1129,37 @@ a#xsrf
In a cross-site request forgery (CSRF or XSRF), an attacker tricks the user into visiting
a different web page with malignant code that secretly sends a malicious request to your application's web server,
- 在一个跨站请求伪造攻击(CSRF或XSRF)中,攻击者欺骗用户访问一个不同的网页,它带有恶意代码,秘密向你的应用程序服务器发送恶意请求。
+ 在一个跨站请求伪造攻击(CSRF 或 XSRF)中,攻击者欺骗用户访问一个不同的网页,它带有恶意代码,秘密向你的应用程序服务器发送恶意请求。
The server and client application must work together to thwart this attack.
Angular's `Http` client does its part by applying a default `CookieXSRFStrategy` automatically to all requests.
客户端和服务器必须合作来抵挡这种攻击。
- Angular的`http`客户端自动使用它默认的`XSRFStrategy`来完成客户端的任务。
+ Angular 的`http`客户端自动使用它默认的`CookieXSRFStrategy`来完成客户端的任务。
The `CookieXSRFStrategy` supports a common anti-XSRF technique in which the server sends a randomly
generated authentication token in a cookie named `XSRF-TOKEN`.
The HTTP client adds an `X-XSRF-TOKEN` header with that token value to subsequent requests.
The server receives both the cookie and the header, compares them, and processes the request only if the cookie and header match.
- `CookieXSRFStrategy`支持常见的反XSRF技术,服务端发送一个随机生成的认证令牌到名为`XSRF-TOKEN`的cookie中。
- HTTP客户端使用该令牌的值为所有请求添加一个`X-XSRF-TOKEN`页头。
- 服务器接受这个cookie和页头,比较它们,只有在它们匹配的时候才处理请求。
+ `CookieXSRFStrategy`支持常见的反 XSRF 技术,服务端发送一个随机生成的认证令牌到名为`XSRF-TOKEN`的 cookie 中。
+ HTTP 客户端使用该令牌的值为所有后续请求添加一个`X-XSRF-TOKEN`页头。
+ 服务器接受这个 cookie 和页头,比较它们,只有在它们匹配的时候才处理请求。
See the [XSRF topic on the Security page](security.html#xsrf) for more information about XSRF and Angular's `XSRFStrategy` counter measures.
- 参见[安全章的关于XSRF讨论](security.html#xsrf),学习更多关于XSRF和Angular的`XSRFStrategy`的应对措施。
+ 参见[安全章的关于 XSRF 讨论](security.html#xsrf),学习更多关于 XSRF 和 Angular 的`XSRFStrategy`的应对措施。
a#in-mem-web-api
.l-main-section
:marked
## Appendix: Tour of Heroes in-memory server
- ## 附录:《英雄指南》的内存(in-memory)服务器
+ ## 附录:《英雄指南》的内存 (in-memory) 服务器
If the app only needed to retrieve data, you could get the heroes from a `heroes.json` file:
- 如果我们只关心获取到的数据,我们可以告诉Angular从一个`heroes.json`文件中获取英雄列表,就像这样:
+ 如果我们只关心获取到的数据,我们可以告诉 Angular 从一个`heroes.json`文件中获取英雄列表,就像这样:
- var _heroesJsonPath = (_docsFor == 'dart' ? 'web' : 'app') + '/heroes.json';
+makeJson('server-communication/' + _docsFor + '/' + _heroesJsonPath, null, _heroesJsonPath)(format=".")
@@ -1151,11 +1170,11 @@ a#in-mem-web-api
posed by top-level JSON arrays.
我们把英雄数组包装进一个带`data`属性的对象中,就像一个真正的数据服务器所应该做的那样。
- 这样可以缓解由顶级JSON数组导致的[安全风险](http://stackoverflow.com/questions/3503102/what-are-top-level-json-arrays-and-why-are-they-a-security-risk)。
+ 这样可以缓解由顶级 JSON 数组导致的[安全风险](http://stackoverflow.com/questions/3503102/what-are-top-level-json-arrays-and-why-are-they-a-security-risk)。
:marked
You'd set the endpoint to the JSON file like this:
- 我们要像这样把端点设置为这个JSON文件:
+ 我们要像这样把端点设置为这个 JSON 文件:
+makeExample('server-communication/ts/app/toh/hero.service.ts', 'endpoint-json', 'app/toh/hero.service.ts')(format=".")
- var _a_ca_class_with = _docsFor === 'ts' ? 'a custom application class with' : ''
@@ -1165,9 +1184,10 @@ a#in-mem-web-api
Because there isn't a real server for this demo,
it uses an *in-memory web API simulator* instead.
- 这在*“获取”英雄数据*的场景下确实能工作,但我们还想*保存*数据。我们不能把这些改动保存到JSON文件中,我们需要一个Web API服务器。
- 在本章中,我们不想惹上配置和维护真实服务器的那些麻烦事。
- 所以,我们转而使用一种*内存Web API仿真器*代替它。
+ 这在*获取英雄数据*的场景下确实能工作,
+ 但我们不能把这些改动保存到 JSON 文件中,因此需要一个 Web API 服务器。
+ 因为这个演示程序中并没有一个真实的服务器,
+ 所以,我们使用*内存 Web API 仿真器*代替它。
.l-sub-section
:marked
@@ -1176,25 +1196,25 @@ a#in-mem-web-api
installed with npm (see `package.json`) and
registered for module loading by SystemJS (see `systemjs.config.js`).
- 内存Web API不是Angular内核的一部分。
- 它是一个可选的服务,来自独立的`angular-in-memory-web-api`库。我们可以通过npm(参见`package.json`)来安装它,
- 并且通过SystemJS(参见`systemjs.config.js`)把它注册进模块加载器。
+ 内存 Web API 不是 Angular 内核的一部分。
+ 它是一个可选的服务,来自独立的`angular-in-memory-web-api`库。我们可以通过 npm (参见`package.json`) 来安装它,
+ 并且通过 SystemJS (参见`systemjs.config.js`) 把它注册进模块加载器。
:marked
The in-memory web API gets its data from !{_a_ca_class_with} a `createDb()`
method that returns a map whose keys are collection names and whose values
are !{_array}s of objects in those collections.
- 内存Web API从一个带有`createDb()`方法的自定义类中获取数据,并且返回一个map,它的主键(key)是一组名字,而值(value)是一组与之对应的对象数组。
+ 内存 Web API 从一个带有`createDb()`方法的自定义类中获取数据,并且返回一个 map,它的主键 (key) 是一组名字,而值 (value) 是一组与之对应的对象数组。
Here's the class for this sample, based on the JSON data:
- 这里是与范例中基于JSON的数据源完成相同功能的类:
+ 这里是与范例中基于 JSON 的数据源完成相同功能的类:
+makeExample('server-communication/ts/app/hero-data.ts', null, 'app/hero-data.ts')(format=".")
:marked
Ensure that the `HeroService` endpoint refers to the web API:
- 确保`HeroService`的端点指向了这个Web API:
+ 确保`HeroService`的端点指向了这个 Web API:
+makeExample('server-communication/ts/app/toh/hero.service.ts', 'endpoint', 'app/toh/hero.service.ts')(format=".")
block redirect-to-web-api
@@ -1203,7 +1223,7 @@ block redirect-to-web-api
adding the `InMemoryWebApiModule` to the `AppModule.imports` list.
At the same time, call its `forRoot` configuration method with the `HeroData` class.
- 使用内存Web API服务模块很容易配置重定向,将`InMemoryWebApiModule`添加到`AppModule.imports`列表中,
+ 使用内存 Web API 服务模块很容易配置重定向,将`InMemoryWebApiModule`添加到`AppModule.imports`列表中,
同时在`HeroData`类中调用`forRoot`配置方法。
+makeExample('server-communication/ts/app/app.module.ts', 'in-mem-web-api', 'app/app.module.ts')(format=".")
:marked
@@ -1214,14 +1234,14 @@ block redirect-to-web-api
Angular's `http` service delegates the client/server communication tasks
to a helper service called the `XHRBackend`.
- 这次重定向非常容易配置,这是因为Angular的`http`服务把客户端/服务器通讯的工作委托给了一个叫做`XHRBackend`的辅助服务。
+ 这次重定向非常容易配置,这是因为 Angular 的`http`服务把客户端/服务器通讯的工作委托给了一个叫做`XHRBackend`的辅助服务。
Using standard Angular provider registration techniques, the `InMemoryWebApiModule`
replaces the default `XHRBackend` service with its own in-memory alternative.
At the same time, the `forRoot` method initializes the in-memory web API with the *seed data* from the mock hero dataset.
- 使用标准Angular提供商注册方法,`InMemoryWebApiModule`替代默认的`XHRBackend`服务并使用它自己的内存存储服务。
- `forRoot`方法来自模拟的英雄数据集的*种子数据*初始化了这个内存Web API
+ 使用标准 Angular 提供商注册方法,`InMemoryWebApiModule`替代默认的`XHRBackend`服务并使用它自己的内存存储服务。
+ `forRoot`方法来自模拟的英雄数据集的*种子数据*初始化了这个内存 Web API。
.l-sub-section
:marked
The `forRoot` method name is a strong reminder that you should only call the `InMemoryWebApiModule` _once_,
@@ -1239,6 +1259,8 @@ block redirect-to-web-api
:marked
Import the `InMemoryWebApiModule` _after_ the `HttpModule` to ensure that
the `XHRBackend` provider of the `InMemoryWebApiModule` supersedes all others.
+
+ 在`HttpModule`之后导入`InMemoryWebApiModule`,确保`XHRBackend`的供应商`InMemoryWebApiModule`取代所有其它的供应商。
:marked
See the full source code in the .