翻译完了一部分新增英文内容

This commit is contained in:
Zhicheng Wang 2016-07-24 21:00:20 +08:00
parent 28fe7d848f
commit 5532c54ff7
2 changed files with 149 additions and 17 deletions

View File

@ -322,7 +322,7 @@ code-example(format="", language="html").
We bind each `RouterLink` to a string containing the path of a route. We bind each `RouterLink` to a string containing the path of a route.
'/crisis-center' and '/heroes' are the paths of the `Routes` we configured above. '/crisis-center' and '/heroes' are the paths of the `Routes` we configured above.
我们用`RouterLink`指令添加了两个A标签。每个`RouterLink`都绑定到了一个包含路由路径的数组上。 我们用`RouterLink`指令添加了两个带`RouterLink`和`RouterLinkActive`指令的A标签。每个`RouterLink`都绑定到了一个包含路由路径的字符串上。
'/crisis-center'和'/heroes'都是我们前面配置过的`Routes`中的路径。 '/crisis-center'和'/heroes'都是我们前面配置过的`Routes`中的路径。
We'll learn to write link expressions — and why they are arrays — We'll learn to write link expressions — and why they are arrays —
@ -462,7 +462,7 @@ table
The directive for adding/removing classes from an HTML element when an associated The directive for adding/removing classes from an HTML element when an associated
routerLink contained on or inside the element becomes active/inactive. routerLink contained on or inside the element becomes active/inactive.
p. p.
TODO: 翻译完。当HTML元素的相关routerLink。 当HTML元素上或元素内的routerLink变为激活或非激活状态时该指令为这个HTML元素添加或移除CSS类
tr tr
td td
p <code>RouterState</code> p <code>RouterState</code>
@ -963,7 +963,7 @@ h3#router-link <i>RouterLink</i>绑定
Above the outlet, within the anchor tags, we see [Property Bindings](template-syntax.html#property-binding) to Above the outlet, within the anchor tags, we see [Property Bindings](template-syntax.html#property-binding) to
the `RouterLink` directive that look like `routerLink="..."`. We imported `RouterLink` from the router library. the `RouterLink` directive that look like `routerLink="..."`. We imported `RouterLink` from the router library.
在插座上方的A标签中有一个绑定`RouterLink`指令的[属性绑定](template-syntax.html#property-binding),就像这样:`[routerLink]="[...]"`。我们从路由库中导入了`RouterLink`。 在插座上方的A标签中有一个绑定`RouterLink`指令的[属性绑定](template-syntax.html#property-binding),就像这样:`routerLink="..."`。我们从路由库中导入了`RouterLink`。
The links in this example each have a string path, the path of a route that The links in this example each have a string path, the path of a route that
we configured earlier. We don't have route parameters yet. we configured earlier. We don't have route parameters yet.
@ -982,23 +982,36 @@ h3#router-link <i>RouterLink</i>绑定
:marked :marked
Learn about the how we can also use the **link parameters array** in the [appendix below](#link-parameters-array). Learn about the how we can also use the **link parameters array** in the [appendix below](#link-parameters-array).
还可以到[后面的附录](#link-parameters-array)中学习如何使用**链接参数数组**。
a#router-link-active a#router-link-active
h3#router-link <i>RouterLinkActive</i> binding h3#router-link <i>RouterLinkActive</i> binding
h3#router-link <i>RouterLinkActive</i>绑定
:marked :marked
On each anchor tag, we also see [Property Bindings](template-syntax.html#property-binding) to On each anchor tag, we also see [Property Bindings](template-syntax.html#property-binding) to
the `RouterLinkActive` directive that look like `routerLinkActive="..."`. the `RouterLinkActive` directive that look like `routerLinkActive="..."`.
每个A标签还有一个到`RouterLinkActive`指令的[属性绑定](template-syntax.html#property-binding),就像`routerLinkActive="..."`。
The template expression to the right of the equals (=) contains our space-delimited string of CSS classes. The template expression to the right of the equals (=) contains our space-delimited string of CSS classes.
We can also bind to the `RouterLinkActive` directive using an array of classes We can also bind to the `RouterLinkActive` directive using an array of classes
such as `[routerLinkActive]="['...']"`. such as `[routerLinkActive]="['...']"`.
等号(=右侧的模板表达式包含用空格分隔的一些CSS类。我们还可以把`RouterLinkActive`指令绑定到一个CSS类组成的数组如`[routerLinkActive]="['...']"`。
The `RouterLinkActive` directive toggles css classes for active `RouterLink`s based on the current `RouterState`. The `RouterLinkActive` directive toggles css classes for active `RouterLink`s based on the current `RouterState`.
This cascades down through each level in our route tree, so parent and child router links can be active at the same time. This cascades down through each level in our route tree, so parent and child router links can be active at the same time.
To override this behavior, we can bind to the `[routerLinkActiveOptions]` input binding with the `{ exact: true }` expression. To override this behavior, we can bind to the `[routerLinkActiveOptions]` input binding with the `{ exact: true }` expression.
By using `{ exact: true }`, a given `RouterLink` will only be active if its URL is an exact match to the current URL. By using `{ exact: true }`, a given `RouterLink` will only be active if its URL is an exact match to the current URL.
要学习关于链接参数数组的更多知识,参见[下面的附录](#link-parameters-array)。 `RouterLinkActive`指令会基于当前的`RouterState`对象来为激活的`RouterLink`切换CSS类。
这会一直沿着路由树往下进行级联处理,所以父路由链接和子路由链接可能会同时激活。
要改变这种行为,可以把`[routerLinkActiveOptions]`绑定到`{exact: true}`表达式。
如果使用了`{ exact: true }`那么只有在其URL与当前URL精确匹配时才会激活指定的`RouterLink`。
h3#router-directives <i>ROUTER_DIRECTIVES</i> h3#router-directives <i>ROUTER_DIRECTIVES</i>
@ -1008,7 +1021,7 @@ h3#router-directives <i>ROUTER_DIRECTIVES</i>(路由指令集)
`RouterLink`, `RouterLinkActive` and `RouterOutlet` are directives in the `ROUTER_DIRECTIVES` collection. `RouterLink`, `RouterLinkActive` and `RouterOutlet` are directives in the `ROUTER_DIRECTIVES` collection.
Remember to add them to the `directives` array of the `@Component` metadata. Remember to add them to the `directives` array of the `@Component` metadata.
`RouterLink`和`RouterOutlet`是`ROUTER_DIRECTIVES`集合中的指令。 `RouterLink`、`RouterLinkActive`和`RouterOutlet`是`ROUTER_DIRECTIVES`集合中的指令。
记住把它们加入`@Component`元数据的`directives`数组中。 记住把它们加入`@Component`元数据的`directives`数组中。
+makeExample('router/ts/app/app.component.1.ts','directives')(format=".") +makeExample('router/ts/app/app.component.1.ts','directives')(format=".")
@ -1380,7 +1393,7 @@ h3#navigate 命令式地导航到英雄详情
with a `RouterLink` if we want to use it in HTML rather than code. with a `RouterLink` if we want to use it in HTML rather than code.
它用一个**链接参数数组**调用路由器的**`navigate`**方法。 它用一个**链接参数数组**调用路由器的**`navigate`**方法。
该数组与我们[以前](#shell-template)在A标签中用来绑定到`RouterLink`指令的链接参数数组很相似。只是这次它出现在代码而不是HTML中。 如果我们想把它用在HTML中那么也可以把相同的语法用在`RouterLink`中。
h3#route-parameters Setting the route parameters in the list view h3#route-parameters Setting the route parameters in the list view
@ -1582,7 +1595,7 @@ h3#nav-to-list 导航回列表组件
that we can bind to a `[routerLink]` directive. that we can bind to a `[routerLink]` directive.
It holds the **path to the `HeroListComponent`**: It holds the **path to the `HeroListComponent`**:
路由的`navigate`方法同样接受一个单条目的*链接参数数组*,我们曾把它绑定到应用壳中“英雄”区的`[routerLink]`指令上。 路由的`navigate`方法同样接受一个单条目的*链接参数数组*,我们也可以把它绑定到`[routerLink]`指令上。
它保存着**到`HeroListComponent`组件的路径** 它保存着**到`HeroListComponent`组件的路径**
+makeExample('router/ts/app/heroes/hero-detail.component.1.ts','gotoHeroes')(format=".") +makeExample('router/ts/app/heroes/hero-detail.component.1.ts','gotoHeroes')(format=".")
@ -1902,7 +1915,7 @@ h3#child-routing-component 子路由组件
These two routes navigate to the two *Crisis Center* child components, These two routes navigate to the two *Crisis Center* child components,
`CrisisListComponent` and `CrisisDetailComponent`. `CrisisListComponent` and `CrisisDetailComponent`.
注意,父路由`/crisis-center`有一个`children`属性,它是一个带两个路由的数组。 注意,父路由`crisis-center`有一个`children`属性,它是一个带两个路由的数组。
There are some *important differences* in the treatment of these routes. There are some *important differences* in the treatment of these routes.
@ -2232,7 +2245,7 @@ h3#can-activate-guard <i>CanActivate</i>: 要求认证
This is a general purpose guard &mdash; we can imagine other features that require authenticated users &mdash; This is a general purpose guard &mdash; we can imagine other features that require authenticated users &mdash;
so we create an `auth-guard.service.ts` in the application root folder. so we create an `auth-guard.service.ts` in the application root folder.
这是一种具有通用性的守护目标(通常会有其它特性需要登录用户才能访问),所以我们在应用的根目录下创建一个`auth.guard.ts`文件。 这是一种具有通用性的守护目标(通常会有其它特性需要登录用户才能访问),所以我们在应用的根目录下创建一个`auth-guard.ts`文件。
At the moment we're interested in seeing how guards work so our first version does nothing useful. At the moment we're interested in seeing how guards work so our first version does nothing useful.
It simply logs to console and `returns` true immediately, allowing navigation to proceed: It simply logs to console and `returns` true immediately, allowing navigation to proceed:
@ -2278,6 +2291,7 @@ h3#can-activate-guard <i>CanActivate</i>: 要求认证
虽然它不会真的进行登录,但足够让我们进行这个讨论了。 虽然它不会真的进行登录,但足够让我们进行这个讨论了。
它有一个`isLoggedIn`标志,用来标识是否用户已经登录过了。 它有一个`isLoggedIn`标志,用来标识是否用户已经登录过了。
它的`login`方法会仿真一个对外部服务的API调用返回一个可观察对象observable。在短暂的停顿之后这个可观察对象就会解析成功。 它的`login`方法会仿真一个对外部服务的API调用返回一个可观察对象observable。在短暂的停顿之后这个可观察对象就会解析成功。
`redirectUrl`属性将会保存在URL中以便认证完之后导航到它。
Let's revise our `AuthGuard` to call it. Let's revise our `AuthGuard` to call it.
@ -2584,7 +2598,8 @@ figure.image-display
The Component Router supports navigation with query strings as well as route parameters. The Component Router supports navigation with query strings as well as route parameters.
We define _optional_ query string parameters in an *object* after we define our required route parameters. We define _optional_ query string parameters in an *object* after we define our required route parameters.
像路由参数一样,组件路由器也支持使用查询字符串进行导航。同样,我们也在*路由参数对象*中定义查询字符串参数。 像路由参数一样,组件路由器也支持使用查询字符串进行导航。
在定义了必须的路由参数之后,我们还可以在*路由参数对象*中定义_可选的_查询字符串参数。
<a id="route-or-query-parameter"></a> <a id="route-or-query-parameter"></a>
@ -2759,7 +2774,6 @@ code-example(format="." language="bash").
This time we'll be navigating in the opposite direction, from the `HeroDetailComponent` to the `HeroListComponent`. This time we'll be navigating in the opposite direction, from the `HeroDetailComponent` to the `HeroListComponent`.
现在就要进行反向导航了 —— 从`HeroDetailComponent`到`HeroListComponent`。 现在就要进行反向导航了 —— 从`HeroDetailComponent`到`HeroListComponent`。
这次我们把`Router`服务注入到`HeroListComponent`的构造函数中。
First we extend the router import statement to include the `ActivatedRoute` service symbol; First we extend the router import statement to include the `ActivatedRoute` service symbol;
@ -2771,7 +2785,7 @@ code-example(format="." language="bash").
Then we use the `ActivatedRoute` to access the `params` _Observable_ so we can subscribe Then we use the `ActivatedRoute` to access the `params` _Observable_ so we can subscribe
and extract the `id` parameter as the `selectedId`: and extract the `id` parameter as the `selectedId`:
然后,使用`routerState`来访问全局可用的查询参数`Observable`,以便我们能订阅,并把`id`参数提取为`selectedId`属性: 然后,使用`ActivatedRoute`来访问可观察对象`params`,以便我们能订阅它,并把`id`参数提取成`selectedId`属性:
+makeExample('router/ts/app/heroes/hero-list.component.ts','ctor', 'hero-list.component.ts (constructor)')(format=".") +makeExample('router/ts/app/heroes/hero-list.component.ts','ctor', 'hero-list.component.ts (constructor)')(format=".")
@ -2824,35 +2838,58 @@ figure.image-display
our route, but what if we wanted optional parameters available to all routes? This is where our our route, but what if we wanted optional parameters available to all routes? This is where our
query parameters come into play and serve a special purpose in our application. query parameters come into play and serve a special purpose in our application.
TODO: 翻译 在这个[查询参数](#query-parameters)例子中,我们只为路由指定了参数,但是该如何定义一些所有路由中都可用的可选参数呢?
要达到这个目的,该“查询参数”大显身手了。
Traditional query string parameters (?name=value) **persist** across route navigations. This means we can pass these query params Traditional query string parameters (?name=value) **persist** across route navigations. This means we can pass these query params
around without having to specify them in each navigation method whether it be declaratively or imperatively. around without having to specify them in each navigation method whether it be declaratively or imperatively.
传统的查询字符串参数(?name=value在跨路由导航时会**始终存在**。这意味着我们可以传入这些查询参数,而不用被迫在每个导航方法中都指定它们 —— 无论是声明式的(链接)还是命令式的(程序中调用)。
[Fragments](https://en.wikipedia.org/wiki/Fragment_identifier) refer to certain elements on the page [Fragments](https://en.wikipedia.org/wiki/Fragment_identifier) refer to certain elements on the page
identified with an `id` attribute. identified with an `id` attribute.
[片段](https://en.wikipedia.org/wiki/Fragment_identifier)可以引用页面中带有特定`id`属性的元素.
We'll update our `AuthGuard` to provide a `session_id` query that will remain after navigating to another route. We'll update our `AuthGuard` to provide a `session_id` query that will remain after navigating to another route.
接下来,我们将更新`AuthGuard`来提供`session_id`查询参数,在导航到其它路由后,它还会存在。
We'll also provide an arbitrary `anchor` fragment, which we would use to jump to a certain point on our page. We'll also provide an arbitrary `anchor` fragment, which we would use to jump to a certain point on our page.
我们还将随意提供一个锚点片段,它用来跳转到页面中指定的位置。
We'll add the extra navigation object to our `router.navigate` method that navigates us to our `/login` route. We'll add the extra navigation object to our `router.navigate` method that navigates us to our `/login` route.
我们还将为`router.nativate`方法传入一个额外的导航对象参数,用来导航到`/login`路由。
+makeExample('router/ts/app/auth-guard.service.ts','', 'auth-guard.service.ts (v.3)') +makeExample('router/ts/app/auth-guard.service.ts','', 'auth-guard.service.ts (v.3)')
:marked :marked
Since we'll be navigating to our *Crisis Admin* route after logging in, we'll update it to handle our global Since we'll be navigating to our *Crisis Admin* route after logging in, we'll update it to handle our global
query parameters and fragment. query parameters and fragment.
由于要在登录后导航到*危机管理*功能区的路由,所以我们还得更新它,来处理这些全局查询参数和片段。
+makeExample('router/ts/app/crisis-center/crisis-admin.component.ts','', 'crisis-admin.component.ts (v.2)') +makeExample('router/ts/app/crisis-center/crisis-admin.component.ts','', 'crisis-admin.component.ts (v.2)')
:marked :marked
*Query Parameters* and *Fragments* are available through the `routerState` property in our `Router` service. *Query Parameters* and *Fragments* are available through the `routerState` property in our `Router` service.
Just like our *route parameters*, global query parameters and fragments are provided as an `Observable`. Just like our *route parameters*, global query parameters and fragments are provided as an `Observable`.
For our updated *Crisis Admin* component we'll feed the `Observable` directly into our template using the `AsyncPipe`, which For our updated *Crisis Admin* component we'll feed the `Observable` directly into our template using the `AsyncPipe`, which
will handle _unsubscribing_ from the `Observable` for us when the component is destroyed. will handle _unsubscribing_ from the `Observable` for us when the component is destroyed.
*查询参数*和*片段*可通过`Router`服务的`routerState`属性使用。和*路由参数*类似,全局查询参数和片段也是`Observable`对象。
在更新过的*英雄管理*组件中,我们将直接把`Observable`传给模板,借助`AsyncPipe`在组件被销毁时自动_取消_对`Observable`的订阅。
.l-sub-section .l-sub-section
img(src='/resources/images/devguide/plunker-separate-window-button.png' alt="pop out the window" align="right" style="margin-right:-20px") img(src='/resources/images/devguide/plunker-separate-window-button.png' alt="pop out the window" align="right" style="margin-right:-20px")
:marked :marked
When running in plunker, pop out the preview window by clicking the blue 'X' button in the upper right corner. When running in plunker, pop out the preview window by clicking the blue 'X' button in the upper right corner.
当在plunker中运行时可以点击右上角的蓝色'X'按钮来弹出预览窗口。
:marked :marked
Following the steps in this process, we can click on the *Crisis Admin* button, that takes us to the *Login* Following the steps in this process, we can click on the *Crisis Admin* button, that takes us to the *Login*
@ -2860,6 +2897,10 @@ figure.image-display
we have been redirected to the `Crisis Admin` page with our `query params` and `fragment` still intact. We can use we have been redirected to the `Crisis Admin` page with our `query params` and `fragment` still intact. We can use
these persistent bits of information for things that need to be provided with every page interaction like these persistent bits of information for things that need to be provided with every page interaction like
authentication tokens or session ids. authentication tokens or session ids.
按照下列步骤试验下:点击*Crisis Admin*按钮,它会带着我们提供的“查询参数”和“片段”跳转到登录页。
点击登录按钮,我们就会被带到`Crisis Admin`页,仍然带着上一步提供的“查询参数”和“片段”。
我们可以用这些持久化信息来携带需要为每个页面都提供的信息如认证令牌或会话的ID等。
<a id="final-app"></a> <a id="final-app"></a>
@ -2903,12 +2944,25 @@ figure.image-display
## 附录:链接参数数组 ## 附录:链接参数数组
We've mentioned the *Link Parameters Array* several times. We've used it several times. We've mentioned the *Link Parameters Array* several times. We've used it several times.
我们已经数次提及*链接参数数组*,也用过好几次了。
A link parameters array holds the ingredients for router navigation: A link parameters array holds the ingredients for router navigation:
链接参数数组保存路由导航时所需的成分:
* the *path* of the route to the destination component * the *path* of the route to the destination component
* 指向目标组件的那个路由的*路径path*
* required route parameters and optional query parameters that go into the route URL * required route parameters and optional query parameters that go into the route URL
* 必须的路由参数和可选的查询参数它们将进入该路由的URL
We can bind the `RouterLink` directive to such an array like this: We can bind the `RouterLink` directive to such an array like this:
我们可以把`RouterLink`指令绑定到一个数组,就像这样:
+makeExample('router/ts/app/app.component.3.ts', 'h-anchor')(format=".") +makeExample('router/ts/app/app.component.3.ts', 'h-anchor')(format=".")
:marked :marked
@ -2920,12 +2974,15 @@ figure.image-display
:marked :marked
We can provide optional query parameters in an object like this: We can provide optional query parameters in an object like this:
我们可以在对象中提供可选的查询参数,就像这样:
+makeExample('router/ts/app/app.component.3.ts', 'cc-query-params')(format=".") +makeExample('router/ts/app/app.component.3.ts', 'cc-query-params')(format=".")
:marked :marked
These three examples cover our needs for an app with one level routing. These three examples cover our needs for an app with one level routing.
The moment we add a child router, such as the *Crisis Center*, we create new link array possibilities. The moment we add a child router, such as the *Crisis Center*, we create new link array possibilities.
个例子覆盖了我们在单级路由的应用中所需的一切。在添加一个像*危机中心*一样的子路由时,我们创建新链接数组组合。 个例子覆盖了我们在单级路由的应用中所需的一切。在添加一个像*危机中心*一样的子路由时,我们创建新链接数组组合。
Recall that we specified a default child route for *Crisis Center* so this simple `RouterLink` is fine. Recall that we specified a default child route for *Crisis Center* so this simple `RouterLink` is fine.
@ -2980,7 +3037,6 @@ figure.image-display
* 详细的子路由需要一个`id`路由参数。 * 详细的子路由需要一个`id`路由参数。
* We add `id` of the *Dragon Crisis* as the third item in the array (`1`)
* We add `id` of the *Dragon Crisis* as the second item in the array (`1`) * We add `id` of the *Dragon Crisis* as the second item in the array (`1`)
* 我们把*巨龙危机*的`id`添加为该数组中的第二个条目(`1`)。 * 我们把*巨龙危机*的`id`添加为该数组中的第二个条目(`1`)。

View File

@ -604,38 +604,70 @@ block review
block observables-section block observables-section
:marked :marked
## Observables ## Observables
## 可观察对象Observable
Each `Http` method returns an `Observable` of HTTP `Response` objects. Each `Http` method returns an `Observable` of HTTP `Response` objects.
每个`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
a good thing to do. a good thing to do.
我们的`HeroService`中把那个`Observable`对象转换成了`Promise`(承诺),并把这个承诺返回给了调用者。
这一节,我们将学会直接返回`Observable`,并且讨论何时以及为何那样做会更好。
### Background ### Background
### 背景
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/)库中引入操作符和扩展。
我们会简短的讲解下如何做。
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`.
That operator converted the `Observable` into a `Promise` and we passed that promise back to the caller. That operator converted the `Observable` into a `Promise` and we passed that promise back to the caller.
快速回忆一下`HeroService`,它在`http.get`返回的`Observable`后面串联了一个`toPromise`操作符。
该操作符把`Observable`转换成了`Promise`(承诺),并且我们把那个“承诺”返回给了调用者。
Converting to a promise is often a good choice. We typically ask `http` to fetch a single chunk of data. Converting to a promise is often a good choice. We typically ask `http` to fetch a single chunk of data.
When we receive the data, we're done. When we receive the data, we're done.
A single result in the form of a promise is easy for the calling component to consume A single result in the form of a promise is easy for the calling component to consume
and it helps that promises are widely understood by JavaScript programmers. and it helps that promises are widely understood by JavaScript programmers.
转换成承诺通常是更好地选择,我们通常要求`http`获取单块数据。只要接收到数据,就算完成。
使用承诺这种形式的结果是让调用方更容易写并且承诺已经在JavaScript程序员中被广泛接受了。
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,
then cancel it, and make a different request ... before the server has responded to the first request. then cancel it, and make a different request ... before the server has responded to the first request.
Such a _request-cancel-new-request_ sequence is difficult to implement with *promises*. Such a _request-cancel-new-request_ sequence is difficult to implement with *promises*.
It's easy with *observables* as we'll see. It's easy with *observables* as we'll see.
但是请求并非总是“一次性”的。我们可以开始一个请求,并且取消它,再开始另一个不同的请求 —— 在服务器对第一个请求作出回应之前。
像这样一个_请求-取消-新请求_的序列用*承诺*是很难实现的,但接下来我们会看到,它对于*可观察对象*却很简单。
### Search-by-name ### Search-by-name
### 按名搜索
We're going to add a *hero search* feature to the Tour of Heroes. We're going to add a *hero search* feature to the Tour of Heroes.
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请求以获得按名字过滤的英雄。
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。
+makeExample('toh-6/ts/app/hero-search.service.ts', null, 'app/hero-search.service.ts')(format=".") +makeExample('toh-6/ts/app/hero-search.service.ts', null, 'app/hero-search.service.ts')(format=".")
@ -643,52 +675,96 @@ block observables-section
The `http.get` call in `HeroSearchService` is similar to the `http.get` call in the `HeroService`. The `http.get` call in `HeroSearchService` is similar to the `http.get` call in the `HeroService`.
The notable difference: we no longer call `toPromise`. The notable difference: we no longer call `toPromise`.
We simply return the *observable* instead. We simply return the *observable* instead.
`HeroSearchService`中的`http.get`调用和`HeroService`中的很相似。
显著的不同是:我们不再调用`toPromise`,而是直接返回*可观察对象*。
### HeroSearchComponent ### HeroSearchComponent
### HeroSearchComponent
Let's create a new `HeroSearchComponent` that calls this new `HeroSearchService`. Let's create a new `HeroSearchComponent` that calls this new `HeroSearchService`.
我们再创建一个新的`HeroSearchComponent`来调用这个新的`HeroSearchService`。
The component template is simple - just a textbox and a list of matching search results. The component template is simple - just a textbox and a list of matching search results.
组件模板很简单,就是一个输入框和一个相匹配的搜索结果列表。
+makeExample('toh-6/ts/app/hero-search.component.html', null,'hero-search.component.html') +makeExample('toh-6/ts/app/hero-search.component.html', null,'hero-search.component.html')
:marked :marked
As the user types in the search box, a *keyup* event binding calls the component's `search` with the new search box value. As the user types in the search box, a *keyup* event binding calls the component's `search` with the new search box value.
当用户在搜索框中输入时,一个*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*对象。这也没啥特别的。
But, as we'll soon see, the `heroes` property returns an `Observable` of heroes, not an array of heroes. But, as we'll soon see, the `heroes` property returns an `Observable` of heroes, not an array of heroes.
The `*ngFor` can't do anything with an observable until we flow it through the `AsyncPipe` (`heroes | async`). The `*ngFor` can't do anything with an observable until we flow it through the `AsyncPipe` (`heroes | async`).
The `AsyncPipe` subscribes to the observable and produces the array of heroes to `*ngFor`. The `AsyncPipe` subscribes to the observable and produces the array of heroes to `*ngFor`.
但是,接下来我们看到`heroes`属性返回了一个英雄们的`Observable`对象,不是英雄们的数组。
`*ngFor`不能利用可观察对象做任何事,除非我们在它后面跟一个`AsyncPipe``heroes | async`)。
`AsyncPipe`会订阅到这个可观察对象,并且为`*ngFor`生产一个英雄们的数组。
Time to create the `HeroSearchComponent` class and metadata. Time to create the `HeroSearchComponent` class and metadata.
该创建`HeroSearchComponent`类及其元数据了。
+makeExample('toh-6/ts/app/hero-search.component.ts', null,'hero-search.component.ts') +makeExample('toh-6/ts/app/hero-search.component.ts', null,'hero-search.component.ts')
:marked :marked
Focus on the `searchSubject`. Focus on the `searchSubject`.
仔细看`searchSubject`。
+makeExample('toh-6/ts/app/hero-search.component.ts', 'searchSubject')(format=".") +makeExample('toh-6/ts/app/hero-search.component.ts', 'searchSubject')(format=".")
:marked :marked
A `Subject` is a producer of an _observable_ event stream. A `Subject` is a producer of an _observable_ event stream.
This `searchSubject` produces an `Observable` of strings, the filter criteria for the name search. This `searchSubject` produces an `Observable` of strings, the filter criteria for the name search.
`Subject`主体是一个_可观察的_事件流中的生产者。
这里的`searchSubject`生产一些字符串的`Observable`,用于作为按名搜索时的过滤条件。
Each call to `search` puts a new string into this subject's _observable_ stream by calling `next`. Each call to `search` puts a new string into this subject's _observable_ stream by calling `next`.
每次到`search`的调用都会调用`next`来把新的字符串放进该主体的_可观察_流中。
A `Subject` is also an `Observable`. A `Subject` is also an `Observable`.
We're going to access that `Observable` and turn the stream We're going to access that `Observable` and turn the stream
of strings into a stream of `Hero[]` arrays, the `heroes` property. of strings into a stream of `Hero[]` arrays, the `heroes` property.
`Subject`也是一个`Observable`对象。
我们将访问`Observable`并且把字符串数组组成的流转换成`Hero[]`数组组成的流,也就是`heroes`属性。
+makeExample('toh-6/ts/app/hero-search.component.ts', 'search')(format=".") +makeExample('toh-6/ts/app/hero-search.component.ts', 'search')(format=".")
:marked :marked
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请求的风暴。
这可不好玩。我们不希望占用服务器资源,不想也耗尽网络带宽。
Fortunately we can chain `Observable` operators to the string `Observable` that reduce the request flow. Fortunately we can chain `Observable` operators to the string `Observable` that reduce the request flow.
We'll make fewer calls to the `HeroSearchService` and still get timely results. Here's how: We'll make fewer calls to the `HeroSearchService` and still get timely results. Here's how:
幸运的是,我们可以在字符串的`Observable`后面串联一个`Observable`操作符,来归并这些请求。
我们将对`HeroSearchService`发起更少的调用,并且仍然获得足够及时的响应。做法如下:
* The `asObservable` operator casts the `Subject` as an `Observable` of filter strings. * The `asObservable` operator casts the `Subject` as an `Observable` of filter strings.
* `asObservable`操作符把`Subject`转换成过滤字符串组成的`Observable`。
* `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。
* `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.
* `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.