Merge pull request #167 from todoubaba/toh-pt6
Polish toh-pt6.jade (round 2)
This commit is contained in:
commit
9161d9c57e
|
@ -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.
|
||||
|
||||
在这一章中,我们要让应用程序学会通过HTTP调用来访问远程服务器上相应的Web API。
|
||||
在这一章中,我们要让应用程序通过 HTTP 调用来访问远程服务器上相应的 Web API。
|
||||
|
||||
Run the <live-example></live-example> for this part.
|
||||
|
||||
p 运行这部分的<live-example>在线例子</live-example>。
|
||||
运行这部分的<live-example>在线例子</live-example>。
|
||||
|
||||
.l-main-section
|
||||
:marked
|
||||
|
@ -47,7 +47,7 @@ block start-server-and-watch
|
|||
Open a terminal/console window and enter the following command to
|
||||
start the TypeScript compiler, start the server, and watch for changes:
|
||||
|
||||
打开terminal/console窗口,输入下列命令来启动TypeScript编译器,它会启动开发服务器,并监视文件变更:
|
||||
打开终端/控制台窗口,输入下列命令来启动 TypeScript 编译器,它会启动开发服务器,并监视文件变更:
|
||||
|
||||
code-example(language="bash").
|
||||
npm start
|
||||
|
@ -113,7 +113,6 @@ block http-providers
|
|||
registering in `main` for a special reason.</span>
|
||||
|
||||
我们建议在根模块`!{_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.
|
||||
We don't even have a web server that can handle requests for heroes.
|
||||
|
@ -128,10 +127,7 @@ block http-providers
|
|||
shouldn't know about this. So we'll slip the in-memory web API into the
|
||||
configuration *above* the `AppComponent`.</span>
|
||||
|
||||
我们要*耍点小花招*,让http客户端从一个Mock服务(*内存(in-memory)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>
|
||||
我们要*耍点小花招*,让 HTTP 客户端从一个模拟服务(*内存 Web API*)中获取和保存数据。
|
||||
|
||||
Here is a version of <span ngio-ex>!{_appModuleTsVsMainTs}</span> that performs this trick:
|
||||
|
||||
|
@ -147,7 +143,9 @@ block backend
|
|||
with an _in-memory web API alternative service_.
|
||||
|
||||
导入`InMemoryWebApiModule`并将其加入到模块的`imports`数组。
|
||||
`InMemoryWebApiModule`将`Http`客户端默认的后端服务(这是一个辅助服务,负责与远程服务器对话)替换成了*内存Web API*服务:
|
||||
`InMemoryWebApiModule`将`Http`客户端默认的后端服务 —
|
||||
这是一个辅助服务,负责与远程服务器对话 —
|
||||
替换成了*内存 Web API*服务:
|
||||
|
||||
+makeExcerpt(_appModuleTsVsMainTs, 'in-mem-web-api', '')
|
||||
|
||||
|
@ -169,15 +167,16 @@ block dont-be-distracted-by-backend-subst
|
|||
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.
|
||||
|
||||
本章主要是介绍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).
|
||||
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.
|
||||
|
||||
要学习关于*内存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 服务器,尽管跳过它。
|
||||
|
||||
.l-main-section
|
||||
:marked
|
||||
|
@ -196,8 +195,9 @@ block dont-be-distracted-by-backend-subst
|
|||
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.
|
||||
|
||||
我们返回一个承诺(Promise),它用mock版的英雄列表进行解析。
|
||||
它当时可能看起来显得有点过于复杂,不过我们早就预料到总有这么一天会通过一个HTTP客户端来获取英雄数据,而且我们知道,那一定是一个异步操作。
|
||||
我们返回一个承诺 (Promise),它用模拟版的英雄列表进行解析。
|
||||
它当时可能看起来显得有点过于复杂,不过我们预料到总有这么一天会通过 HTTP 客户端来获取英雄数据,
|
||||
而且我们知道,那一定是一个异步操作。
|
||||
|
||||
That day has arrived! Let's convert `getHeroes()` to use HTTP.
|
||||
|
||||
|
@ -217,7 +217,7 @@ block dont-be-distracted-by-backend-subst
|
|||
Refresh the browser, and the hero data should be successfully loaded from the
|
||||
mock server.
|
||||
|
||||
刷新浏览器后,英雄数据就会从Mock服务器被成功读取。
|
||||
刷新浏览器后,英雄数据就会从模拟服务器被成功读取。
|
||||
|
||||
<h3 id="!{_h3id}">HTTP !{_Promise}</h3>
|
||||
|
||||
|
@ -234,7 +234,7 @@ block get-heroes-details
|
|||
We'll learn about [Observables](#observables) later in this chapter.
|
||||
|
||||
Angular 的`http.get`返回一个 RxJS 的`Observable`对象。
|
||||
*Observable(可观察对象)*是一个管理异步数据流的强力方式。
|
||||
*Observable(可观察对象)*是一个管理异步数据流的强力方式。
|
||||
后面我们还会进一步学习[可观察对象](#observables)。
|
||||
|
||||
For *now* we get back on familiar ground by immediately
|
||||
|
@ -319,7 +319,7 @@ block get-heroes-details
|
|||
We must anticipate HTTP failures as they happen frequently for reasons beyond our control.
|
||||
|
||||
这是一个关键的步骤!
|
||||
我们必须预料到http请求会失败,因为有太多我们无法控制的原因可能导致它们频繁出现各种错误。
|
||||
我们必须预料到 HTTP 请求会失败,因为有太多我们无法控制的原因可能导致它们频繁出现各种错误。
|
||||
|
||||
+makeExcerpt('app/hero.service.ts', 'handleError', '')
|
||||
|
||||
|
@ -332,7 +332,8 @@ block get-heroes-details
|
|||
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.
|
||||
|
||||
我们还要通过一个被拒绝(rejected)的承诺(promise)来把该错误用一个用户友好的格式返回给调用者,以便调用者能把一个合适的错误信息显示给用户。
|
||||
我们还要通过一个被拒绝 (rejected) 的承诺来把该错误用一个用户友好的格式返回给调用者,
|
||||
以便调用者能把一个合适的错误信息显示给用户。
|
||||
|
||||
### Unchanged `getHeroes` API
|
||||
|
||||
|
@ -376,7 +377,7 @@ block get-heroes-details
|
|||
the server.
|
||||
|
||||
以前是不会丢失更新的,现在是怎么回事?
|
||||
当该应用使用mock出来的英雄列表时,修改的是一份全局共享的英雄列表,而现在改成了从服务器获取数据。
|
||||
当该应用使用模拟出来的英雄列表时,修改的是一份全局共享的英雄列表,而现在改成了从服务器获取数据。
|
||||
如果我们希望这些更改被持久化,我们就得把它们写回服务器。
|
||||
|
||||
:marked
|
||||
|
@ -448,7 +449,7 @@ block get-heroes-details
|
|||
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.
|
||||
|
||||
当click事件触发时,我们调用组件的click处理器,然后清空这个输入框,以便用来输入另一个名字。
|
||||
当点击事件触发时,我们调用组件的点击处理器,然后清空这个输入框,以便用来输入另一个名字。
|
||||
|
||||
+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
|
||||
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.
|
||||
|
||||
|
@ -484,7 +486,7 @@ block get-heroes-details
|
|||
Add this button element to the heroes component HTML, right after the hero
|
||||
name in the repeated `<li>` tag:
|
||||
|
||||
把这个button元素添加到英雄列表组件的HTML中,把它放在`<li>`标签中的英雄名的后面:
|
||||
把这个按钮元素添加到英雄列表组件的 HTML 中,把它放在`<li>`标签中的英雄名的后面:
|
||||
|
||||
+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
|
||||
select the hero that we are going to delete!
|
||||
|
||||
除了调用组件的`delete`方法之外,这个`delete`按钮的click处理器还应该阻止click事件向上冒泡 —— 我们并不希望触发`<li>`的事件处理器,否则它会选中我们要删除的这位英雄。
|
||||
除了调用组件的`delete`方法之外,这个`delete`按钮的点击处理器还应该阻止点击事件向上冒泡 —
|
||||
我们并不希望触发`<li>`的事件处理器,否则它会选中我们要删除的这位英雄。
|
||||
|
||||
The logic of the `delete` handler is a bit trickier:
|
||||
|
||||
|
@ -514,7 +517,8 @@ block get-heroes-details
|
|||
is still responsible for updating the display: it removes the deleted hero
|
||||
from the !{_array} and resets the selected hero if necessary.
|
||||
|
||||
当然,我们仍然把删除英雄的操作委托给了hero服务,不过该组件仍然负责更新显示:它从数组中移除了被删除的英雄,如果删除的是正选中的英雄,还会清空选择。
|
||||
当然,我们仍然把删除英雄的操作委托给了 hero 服务,
|
||||
不过该组件仍然负责更新显示:它从数组中移除了被删除的英雄,如果删除的是正选中的英雄,还会清空选择。
|
||||
|
||||
:marked
|
||||
We want our delete button to be placed at the far right of the hero entry.
|
||||
|
@ -545,7 +549,7 @@ block get-heroes-details
|
|||
:marked
|
||||
## !{_Observable}s
|
||||
|
||||
## 可观察对象(Observable)
|
||||
## 可观察对象 (Observable)
|
||||
|
||||
block observables-section-intro
|
||||
:marked
|
||||
|
@ -566,7 +570,7 @@ block observables-section-intro
|
|||
|
||||
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
|
||||
operators and extensions from the [RxJS Observables](http://reactivex.io/rxjs/) library.
|
||||
|
@ -595,7 +599,8 @@ block observables-section-intro
|
|||
Such a _request-cancel-new-request_ sequence is difficult to implement with *!{_Promise}s*.
|
||||
It's easy with *!{_Observable}s* as we'll see.
|
||||
|
||||
但是请求并非总是“一次性”的。我们可以开始一个请求,并且取消它,再开始另一个不同的请求 —— 在服务器对第一个请求作出回应之前。
|
||||
但是请求并非总是“一次性”的。我们可以开始一个请求,
|
||||
并且取消它,在服务器对第一个请求作出回应之前,再开始另一个不同的请求 。
|
||||
像这样一个_请求-取消-新请求_的序列用*承诺*是很难实现的,但接下来我们会看到,它对于*可观察对象*却很简单。
|
||||
|
||||
### Search-by-name
|
||||
|
@ -723,17 +728,20 @@ block observable-transformers
|
|||
* `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.
|
||||
|
||||
* 在传出最终字符串之前,`debounceTime(300)`将会等待,直到新增字符串的事件暂停了300毫秒。我们实际发起请求的间隔永远不会小于300ms。
|
||||
在传出最终字符串之前,`debounceTime(300)`将会等待,直到新增字符串的事件暂停了 300 毫秒。
|
||||
我们实际发起请求的间隔永远不会小于 300ms。
|
||||
|
||||
* `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.
|
||||
|
||||
* `distinctUntilChanged`确保只在过滤条件变化时才发送请求,这样就不会重复请求同一个搜索词了。
|
||||
`distinctUntilChanged`确保只在过滤条件变化时才发送请求,
|
||||
这样就不会重复请求同一个搜索词了。
|
||||
|
||||
* `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.
|
||||
|
||||
* `switchMap`会为每个从`debounce`和`distinctUntilChanged`中通过的搜索词调用搜索服务。它会取消并丢弃以前的搜索可观察对象,只保留最近的。
|
||||
`switchMap`会为每个从`debounce`和`distinctUntilChanged`中通过的搜索词调用搜索服务。
|
||||
它会取消并丢弃以前的搜索可观察对象,只保留最近的。
|
||||
|
||||
.l-sub-section
|
||||
:marked
|
||||
|
@ -747,13 +755,14 @@ block observable-transformers
|
|||
Even with a 300ms pause between requests, we could have multiple HTTP requests in flight
|
||||
and they may not return in the order sent.
|
||||
|
||||
每次符合条件的按键事件都会触发一次对`http`方法的调用。即使在发送每个请求前都有300毫秒的延迟,我们仍然可能同时拥有多个在途的HTTP请求,并且它们返回的顺序未必就是发送时的顺序。
|
||||
每次符合条件的按键事件都会触发一次对`http`方法的调用。即使在发送每个请求前都有 300 毫秒的延迟,
|
||||
我们仍然可能同时拥有多个在途的 HTTP 请求,并且它们返回的顺序未必就是发送时的顺序。
|
||||
|
||||
`switchMap` preserves the original request order while returning
|
||||
only the observable from the most recent `http` method call.
|
||||
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
|
||||
|
@ -765,7 +774,8 @@ block observable-transformers
|
|||
until the service supports that feature, a topic for another day.
|
||||
We are content for now to discard unwanted results.
|
||||
|
||||
注意,_取消_`HeroSearchService`的可观察对象并不会实际中止(abort)一个未完成的HTTP请求,除非服务支持这个特性,这个问题我们以后再讨论。
|
||||
注意,_取消_`HeroSearchService`的可观察对象并不会实际中止 (abort) 一个未完成的 HTTP 请求,
|
||||
除非服务支持这个特性,这个问题我们以后再讨论。
|
||||
目前我们的做法只是丢弃不希望的结果。
|
||||
|
||||
:marked
|
||||
|
@ -788,7 +798,8 @@ block observable-transformers
|
|||
We could extend `Observable` with just the operators we need here by
|
||||
including the pertinent `import` statements at the top of this file.
|
||||
|
||||
通过在本文件的顶部写上适当的`import`语句,我们可以为`Observable`扩展出这里用到的那些操作符。
|
||||
通过在本文件的顶部写上适当的`import`语句,
|
||||
我们可以为`Observable`扩展出这里用到的那些操作符。
|
||||
|
||||
.l-sub-section
|
||||
:marked
|
||||
|
@ -903,27 +914,27 @@ block filetree
|
|||
|
||||
- We added the necessary dependencies to use HTTP in our application.
|
||||
|
||||
- 我们添加了在应用程序中使用HTTP的必备依赖。
|
||||
我们添加了在应用程序中使用 HTTP 的必备依赖。
|
||||
|
||||
- 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.
|
||||
|
||||
- 我们扩展了`HeroService`来支持post、put和delete方法。
|
||||
我们扩展了`HeroService`来支持 post、put 和 delete 方法。
|
||||
|
||||
- We updated our components to allow adding, editing and deleting of heroes.
|
||||
|
||||
- 我们更新了组件,以允许用户添加、编辑和删除英雄。
|
||||
我们更新了组件,以允许用户添加、编辑和删除英雄。
|
||||
|
||||
- We configured an in-memory web API.
|
||||
|
||||
- 我们配置了一个内存Web API。
|
||||
我们配置了一个内存 Web API。
|
||||
|
||||
- We learned how to use !{_Observable}s.
|
||||
|
||||
- 我们学会了如何使用“可观察对象”。
|
||||
我们学会了如何使用“可观察对象”。
|
||||
|
||||
Here are the files we _added or changed_ in this chapter.
|
||||
|
||||
|
|
Loading…
Reference in New Issue