review for dependency-injection.jade in guide.

This commit is contained in:
Rex Ye 2016-05-17 12:08:00 +01:00
parent 3ba0e541e1
commit 990ecd8f70
1 changed files with 99 additions and 46 deletions

View File

@ -82,8 +82,8 @@ include ../_util-fns
to share services that have been created previously for other consumers. to share services that have been created previously for other consumers.
现在,每辆车都有它自己的引擎。它不能和其它车辆共享引擎。 现在,每辆车都有它自己的引擎。它不能和其它车辆共享引擎。
虽然这对于汽车来说还算可以理解,但是我们设想一下那些应该被共享的依赖,比如到厂家服务中心的车载无线(译注:汽车的一种设备,用于联系厂家) 虽然这对于汽车来说还算可以理解,但是我们设想一下那些应该被共享的依赖,比如用来联系厂家服务中心的车载无线
我们的车缺乏必要的弹性,共享当初给其他消费者创建的车载无线。 我们的车缺乏必要的弹性,无法共享当初给其他消费者创建的车载无线。
When we write tests for our `Car` we're at the mercy of its hidden dependencies. When we write tests for our `Car` we're at the mercy of its hidden dependencies.
Is it even possible to create a new `Engine` in a test environment? Is it even possible to create a new `Engine` in a test environment?
@ -91,7 +91,7 @@ include ../_util-fns
Will a new instance of `Engine` make an asynchronous call to the server? Will a new instance of `Engine` make an asynchronous call to the server?
We certainly don't want that going on during our tests. We certainly don't want that going on during our tests.
当我们给`Car`类写测试的时候,我们得自己摆弄它那些隐藏的依赖 当我们给`Car`类写测试的时候,我们被它那些隐藏的依赖所摆布
你以为能在测试环境中成功创建一个新的`Engine`吗? 你以为能在测试环境中成功创建一个新的`Engine`吗?
`Engine`自己又依赖什么?那些依赖本身又依赖什么? `Engine`自己又依赖什么?那些依赖本身又依赖什么?
`Engine`的新实例会发起一个到服务器的异步调用吗? `Engine`的新实例会发起一个到服务器的异步调用吗?
@ -139,12 +139,13 @@ include ../_util-fns
:marked :marked
We also leverage TypeScript's constructor syntax for declaring parameters and properties simultaneously. We also leverage TypeScript's constructor syntax for declaring parameters and properties simultaneously.
我们又一次借助TypeScript的构造器语法来同时定义参数和属性。 我们同时借助TypeScript的构造器语法来同时定义参数和属性。
// #docregion why-3-2 // #docregion why-3-2
:marked :marked
Now we create a car by passing the engine and tires to the constructor. Now we create a car by passing the engine and tires to the constructor.
现在,我们通过往构造函数中传入引擎和轮胎来创建一辆车。 现在,我们通过往构造函数中传入引擎和轮胎来创建一辆车。
// #enddocregion why-3-2 // #enddocregion why-3-2
- var stylePattern = { otl: /(new Car.*$)/gm }; - var stylePattern = { otl: /(new Car.*$)/gm };
+makeExample('dependency-injection/ts/app/car/car-creations.ts', 'car-ctor-instantiation', '', stylePattern)(format=".") +makeExample('dependency-injection/ts/app/car/car-creations.ts', 'car-ctor-instantiation', '', stylePattern)(format=".")
@ -162,6 +163,7 @@ include ../_util-fns
If someone extends the `Engine` class, that is not `Car`'s problem. If someone extends the `Engine` class, that is not `Car`'s problem.
如果有人扩展了`Engine`类,那就不再是`Car`类的烦恼了。 如果有人扩展了`Engine`类,那就不再是`Car`类的烦恼了。
// #enddocregion why-4 // #enddocregion why-4
// Must copy the following, due to indented +make. // Must copy the following, due to indented +make.
.l-sub-section .l-sub-section
@ -244,7 +246,7 @@ include ../_util-fns
When we need a `Car`, we simply ask the injector to get it for us and we're good to go. When we need a `Car`, we simply ask the injector to get it for us and we're good to go.
当我们需要一个`Car`时,就简单的请求注入器取得它,然后直接去提车 当我们需要一个`Car`时,就简单的找注入器取车就可以了
// #enddocregion why-8 // #enddocregion why-8
+makeExample('dependency-injection/ts/app/car/car-injector.ts','injector-call')(format=".") +makeExample('dependency-injection/ts/app/car/car-injector.ts','injector-call')(format=".")
// #docregion why-9 // #docregion why-9
@ -261,7 +263,7 @@ include ../_util-fns
This is what a **dependency injection framework** is all about. This is what a **dependency injection framework** is all about.
这就是“什么是**依赖注入框架**”问题的答案 这就是“**依赖注入框架**”存在的原因
Now that we know what dependency injection is and appreciate its benefits, Now that we know what dependency injection is and appreciate its benefits,
let's see how it is implemented in Angular. let's see how it is implemented in Angular.
@ -283,13 +285,14 @@ include ../_util-fns
That sounds nice. What does it do for us when building components in Angular? That sounds nice. What does it do for us when building components in Angular?
Let's see, one step at a time. Let's see, one step at a time.
看起来很美。当我们在Angular中构建组件的时候它能为我们做什么 听起来很好。当我们在Angular中构建组件的时候到底能为我们做什么?
让我们看看,一次一步儿。 让我们看看,一次一步儿。
We'll begin with a simplified version of the `HeroesComponent` We'll begin with a simplified version of the `HeroesComponent`
that we built in the [The Tour of Heroes](../tutorial/). that we built in the [The Tour of Heroes](../tutorial/).
我们从当初在[英雄指南](../tutorial/)中构建过的`HeroesComponent`的一个简化版本开始。 我们从当初在[英雄指南](../tutorial/)中构建过的`HeroesComponent`的一个简化版本开始。
// #enddocregion di-1 // #enddocregion di-1
+makeTabs( +makeTabs(
`dependency-injection/ts/app/heroes/heroes.component.1.ts, `dependency-injection/ts/app/heroes/heroes.component.1.ts,
@ -308,8 +311,8 @@ include ../_util-fns
Our stripped down version has only one child, `HeroListComponent`, Our stripped down version has only one child, `HeroListComponent`,
which displays a list of heroes. which displays a list of heroes.
`HeroesComponent`是*英雄*特性分区中的根组件。它管理着本分区的所有子组件。 `HeroesComponent`是*英雄*特性区域的根组件。它管理本分区的所有子组件。
我们简化后的版本只有一个子组件`HeroListComponent`显示一个英雄列表。 我们简化后的版本只有一个子组件`HeroListComponent`用来显示一个英雄列表。
// #enddocregion di-2 // #enddocregion di-2
// #docregion di-3 // #docregion di-3
:marked :marked
@ -335,6 +338,7 @@ include ../_util-fns
Write this service in its own file. See [this note](#forward-ref) to understand why. Write this service in its own file. See [this note](#forward-ref) to understand why.
把这个服务写在一个独立的文件中。参见[这里的说明](#forward-ref)来理解为什么要这样。 把这个服务写在一个独立的文件中。参见[这里的说明](#forward-ref)来理解为什么要这样。
+makeExample('dependency-injection/ts/app/heroes/hero.service.1.ts',null, 'app/heroes/hero.service.ts' ) +makeExample('dependency-injection/ts/app/heroes/hero.service.1.ts',null, 'app/heroes/hero.service.ts' )
// #docregion di-4 // #docregion di-4
:marked :marked
@ -342,6 +346,7 @@ include ../_util-fns
the same mock data as before, but none of its consumers need to know that. the same mock data as before, but none of its consumers need to know that.
我们的`HeroService`暴露了`getHeroes`方法,用于返回跟以前一样的模拟数据,但它的消费者不需要知道这一点。 我们的`HeroService`暴露了`getHeroes`方法,用于返回跟以前一样的模拟数据,但它的消费者不需要知道这一点。
// #enddocregion di-4 // #enddocregion di-4
// #docregion di-5 // #docregion di-5
.l-sub-section .l-sub-section
@ -353,7 +358,7 @@ include ../_util-fns
We'd also have to rewrite the way components consume our service. We'd also have to rewrite the way components consume our service.
This is important in general, but not to our current story. This is important in general, but not to our current story.
我们甚至不能说这是一个真实的服务。 我们甚至没有假装这是一个真实的服务。
如果我们真的从一个远端服务器获取数据这个API必须是异步的可能得返回 如果我们真的从一个远端服务器获取数据这个API必须是异步的可能得返回
[ES2015 承诺Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)。 [ES2015 承诺Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)。
// #enddocregion di-5 // #enddocregion di-5
@ -372,10 +377,12 @@ include ../_util-fns
We don't have to create an Angular injector. We don't have to create an Angular injector.
Angular creates an application-wide injector for us during the bootstrap process. Angular creates an application-wide injector for us during the bootstrap process.
<a id="bootstrap"></a> <a id="bootstrap"></a>
我们并不需要自己创建一个Angular注入器。 我们并不需要自己创建一个Angular注入器。
Angular在启动期间会自动为我们创建一个全应用级注入器。 Angular在启动期间会自动为我们创建一个全应用级注入器。
<a id="bootstrap"></a>
// #enddocregion di-configure-injector-1 // #enddocregion di-configure-injector-1
+makeExample('dependency-injection/ts/app/main.ts', 'bootstrap', 'app/main.ts (节选)')(format='.') +makeExample('dependency-injection/ts/app/main.ts', 'bootstrap', 'app/main.ts (节选)')(format='.')
// #docregion di-configure-injector-2 // #docregion di-configure-injector-2
@ -418,6 +425,7 @@ include ../_util-fns
:marked :marked
### Registering providers in a component ### Registering providers in a component
### 在组件中注册供应商 ### 在组件中注册供应商
Here's a revised `HeroesComponent` that registers the `HeroService`. Here's a revised `HeroesComponent` that registers the `HeroService`.
这里是注册了`HeroService`的修改版`HeroesComponent`。 这里是注册了`HeroService`的修改版`HeroesComponent`。
@ -428,6 +436,7 @@ include ../_util-fns
Look closely at the `providers` part of the `@Component` metadata: Look closely at the `providers` part of the `@Component` metadata:
仔细看`@Component`元数据中的`providers`部分: 仔细看`@Component`元数据中的`providers`部分:
// #enddocregion di-register-providers-2 // #enddocregion di-register-providers-2
+makeExample('dependency-injection/ts/app/heroes/heroes.component.1.ts','providers')(format='.') +makeExample('dependency-injection/ts/app/heroes/heroes.component.1.ts','providers')(format='.')
// #docregion di-register-providers-3 // #docregion di-register-providers-3
@ -441,6 +450,7 @@ include ../_util-fns
But its child `HeroListComponent` does, so we head there next. But its child `HeroListComponent` does, so we head there next.
`HeroesComponent`本身不需要`HeroService`,但它的子组件`HeroListComponent`需要,所以我们再往下看。 `HeroesComponent`本身不需要`HeroService`,但它的子组件`HeroListComponent`需要,所以我们再往下看。
// #enddocregion di-register-providers-3 // #enddocregion di-register-providers-3
// #docregion di-prepare-for-injection-1 // #docregion di-prepare-for-injection-1
:marked :marked
@ -480,7 +490,7 @@ include ../_util-fns
We're writing in TypeScript and have followed the parameter name with a type annotation, `:HeroService`. We're writing in TypeScript and have followed the parameter name with a type annotation, `:HeroService`.
The class is also decorated with the `@Component` decorator (scroll up to confirm that fact). The class is also decorated with the `@Component` decorator (scroll up to confirm that fact).
我们正在写TypeScript并且在参数名后面带有一个类型注解:`:HeroService`。 我们利用TypeScript编程在参数名后面带有一个类型注解:`:HeroService`。
这个类还有一个`@Component`的装饰器(往上翻翻就知道了)。 这个类还有一个`@Component`的装饰器(往上翻翻就知道了)。
When the TypeScript compiler evaluates this class, it sees the `@Component` decorator and adds class metadata When the TypeScript compiler evaluates this class, it sees the `@Component` decorator and adds class metadata
@ -493,12 +503,13 @@ include ../_util-fns
That's how the Angular injector knows to inject an instance of the `HeroService` when it That's how the Angular injector knows to inject an instance of the `HeroService` when it
creates a new `HeroListComponent`. creates a new `HeroListComponent`.
为什么Angular的注入器会知道当创建`HeroListComponent`时需要注入一个`HeroService`的实例?这就是原理。 Angular的注入器是怎么知道在创建`HeroListComponent`时注入一个`HeroService`的实例的?这就是原理。
// #docregion di-create-injector-implicitly-1 // #docregion di-create-injector-implicitly-1
:marked :marked
<a id="di-metadata"></a> <a id="di-metadata"></a>
### Creating the injector (implicitly) ### Creating the injector (implicitly)
### 创建注入器(隐式的) ### 创建注入器(隐式的)
When we introduced the idea of an injector above, we showed how to create When we introduced the idea of an injector above, we showed how to create
an injector and use it to create a new `Car`. an injector and use it to create a new `Car`.
@ -523,6 +534,7 @@ include ../_util-fns
:marked :marked
### Singleton services ### Singleton services
### 单例服务 ### 单例服务
Dependencies are singletons within the scope of an injector. Dependencies are singletons within the scope of an injector.
In our example, a single `HeroService` instance is shared among the In our example, a single `HeroService` instance is shared among the
`HeroesComponent` and its `HeroListComponent` children. `HeroesComponent` and its `HeroListComponent` children.
@ -543,16 +555,18 @@ include ../_util-fns
:marked :marked
### Testing the component ### Testing the component
### 测试组件 ### 测试组件
We emphasized earlier that designing a class for dependency injection makes the class easier to test. We emphasized earlier that designing a class for dependency injection makes the class easier to test.
Listing dependencies as constructor parameters may be all we need to test application parts effectively. Listing dependencies as constructor parameters may be all we need to test application parts effectively.
我们前面强调过,设计一个适合依赖注入的类,可以让这个类更容易测试。 我们前面强调过,设计一个适合依赖注入的类,可以让这个类更容易测试。
在构造函数的参数中列出依赖,就是当我们要对应用的一部分进行有效测试时所要做的一切了 要有效的测试应用的一部分,所有我们所需要做的,就只是在构造函数的参数中列出依赖。
For example, we can create a new `HeroListComponent` with a mock service that we can manipulate For example, we can create a new `HeroListComponent` with a mock service that we can manipulate
under test: under test:
比如我们可以使用一个mock服务来创建新的`HeroListComponent`实例,以便我们可以在测试中操纵它: 比如我们可以使用一个mock服务来创建新的`HeroListComponent`实例,以便我们可以在测试中操纵它:
// #enddocregion di-testing-component-1 // #enddocregion di-testing-component-1
+makeExample('dependency-injection/ts/app/test.component.ts', 'spec')(format='.') +makeExample('dependency-injection/ts/app/test.component.ts', 'spec')(format='.')
// #docregion di-testing-component-2 // #docregion di-testing-component-2
@ -565,7 +579,8 @@ include ../_util-fns
// #docregion di-service-service-1 // #docregion di-service-service-1
:marked :marked
### When the service needs a service ### When the service needs a service
### 如果此服务需要别的服务 ### 服务需要别的服务
Our `HeroService` is very simple. It doesn't have any dependencies of its own. Our `HeroService` is very simple. It doesn't have any dependencies of its own.
我们的`HeroService`非常简单。它本身不需要任何依赖。 我们的`HeroService`非常简单。它本身不需要任何依赖。
@ -579,7 +594,7 @@ include ../_util-fns
Here is the revision compared to the original. Here is the revision compared to the original.
下面是相对于原始类的修改: 下面是在原来的类的基础上做的修改:
// #enddocregion di-service-service-1 // #enddocregion di-service-service-1
+makeTabs( +makeTabs(
`dependency-injection/ts/app/heroes/hero.service.2.ts, `dependency-injection/ts/app/heroes/hero.service.2.ts,
@ -594,6 +609,7 @@ include ../_util-fns
现在,这个构造函数会要求一个`Logger`类的实例注入进来,并且把它存到一个名为`_logger`的私有属性中。 现在,这个构造函数会要求一个`Logger`类的实例注入进来,并且把它存到一个名为`_logger`的私有属性中。
当别人要求获得英雄数据时,我们会在`getHeroes`方法中使用这个属性。 当别人要求获得英雄数据时,我们会在`getHeroes`方法中使用这个属性。
// #enddocregion di-service-service-2 // #enddocregion di-service-service-2
// #docregion di-injectable-1 // #docregion di-injectable-1
- var lang = current.path[1] - var lang = current.path[1]
@ -605,13 +621,14 @@ include ../_util-fns
<a id="injectable"></a> <a id="injectable"></a>
### Why @Injectable? ### Why @Injectable?
### 为什么要加@Injectable ### 为什么要加@Injectable
Notice the `@Injectable()` #{decoration} above the service class. Notice the `@Injectable()` #{decoration} above the service class.
We haven't seen `@Injectable()` before. We haven't seen `@Injectable()` before.
As it happens, we could have added it to our first version of `HeroService`. As it happens, we could have added it to our first version of `HeroService`.
We didn't bother because we didn't need it then. We didn't bother because we didn't need it then.
注意上面这个服务类的 `@Injectable()` #{decorationCn}。但我们以前从没见过`@Injectable()`。 注意上面这个服务类的 `@Injectable()` #{decorationCn}。但我们以前从没见过`@Injectable()`。
当初,我们本可以把它加到第一版的`HeroService`上。 其实我们可以把它加到第一版的`HeroService`上。
但我们没有那么做,因为那时候还不需要它。 但我们没有那么做,因为那时候还不需要它。
We need it now... now that our service has an injected dependency. We need it now... now that our service has an injected dependency.
@ -631,12 +648,14 @@ include ../_util-fns
- var any_decoratorCn = lang == 'dart' ? '' : 'TypeScript会为任何带有装饰器的类生成元数据而且会为任何装饰器都生成。' - var any_decoratorCn = lang == 'dart' ? '' : 'TypeScript会为任何带有装饰器的类生成元数据而且会为任何装饰器都生成。'
.callout.is-helpful .callout.is-helpful
header Suggestion: add @Injectable() to every service class header Suggestion: add @Injectable() to every service class
header 建议:为每一个服务类添加@Injectable() header 建议:为每一个服务类添加@Injectable()
:marked :marked
We recommend adding `@Injectable()` to every service class, even those that don't have dependencies We recommend adding `@Injectable()` to every service class, even those that don't have dependencies
and, therefore, do not technically require it. Here's why: and, therefore, do not technically require it. Here's why:
我们建议为每个服务类都添加`@Injectable()`装饰器,即使它们因为目前没有任何依赖,在技术上并不需要它。这是因为: 我们建议为每个服务类都添加`@Injectable()`装饰器,即使它们因为目前没有任何依赖,在技术上并不需要它。这是因为:
ul(style="font-size:inherit") ul(style="font-size:inherit")
li <b>Future proofing:</b> No need to remember <code>@Injectable()</code> when we add a dependency later. li <b>Future proofing:</b> No need to remember <code>@Injectable()</code> when we add a dependency later.
li <b>面向未来:</b> 在我们将来添加依赖时,不用怕忘了添加<code>@Injectable()</code>。 li <b>面向未来:</b> 在我们将来添加依赖时,不用怕忘了添加<code>@Injectable()</code>。
@ -664,6 +683,7 @@ include ../_util-fns
.callout.is-critical .callout.is-critical
header Always include the parentheses header Always include the parentheses
header 总要带着括号 header 总要带着括号
:marked :marked
Always use `@Injectable()`, not just `@Injectable`. Always use `@Injectable()`, not just `@Injectable`.
Our application will fail mysteriously if we forget the parentheses. Our application will fail mysteriously if we forget the parentheses.
@ -676,12 +696,17 @@ include ../_util-fns
:marked :marked
## Creating and registering a logger service ## Creating and registering a logger service
## 创建和注册日志服务 ## 创建和注册日志服务
We're injecting a logger into our `HeroService` in two steps: We're injecting a logger into our `HeroService` in two steps:
要把日志服务注入到`HeroService`中需要两步: 要把日志服务注入到`HeroService`中需要两步:
1. Create the logger service. 1. Create the logger service.
1. 创建日志服务。 1. 创建日志服务。
1. Register it with the application. 1. Register it with the application.
1. 把它注册到应用中。 1. 把它注册到应用中。
The logger service implementation is no big deal. The logger service implementation is no big deal.
@ -699,12 +724,14 @@ include ../_util-fns
我们很可能在应用的任何地方都使用同一个日志服务的实例。 我们很可能在应用的任何地方都使用同一个日志服务的实例。
所以,我们把它放到`app/`目录下,也就是应用的顶级,并把它注册到我们的根组件`AppComponent`上,放到元数据中的`providers`数组里。 所以,我们把它放到`app/`目录下,也就是应用的顶级,并把它注册到我们的根组件`AppComponent`上,放到元数据中的`providers`数组里。
+makeExample('dependency-injection/ts/app/providers.component.ts','providers-logger', 'app/app.component.ts (节选)') +makeExample('dependency-injection/ts/app/providers.component.ts','providers-logger', 'app/app.component.ts (节选)')
// #docregion logger-service-3 // #docregion logger-service-3
:marked :marked
If we forget to register the logger, Angular throws an exception when it first looks for the logger: If we forget to register the logger, Angular throws an exception when it first looks for the logger:
如果我们忘了注册这个日志服务Angular会在首次查找这个日志服务时抛出一个异常。 如果我们忘了注册这个日志服务Angular会在首次查找这个日志服务时抛出一个异常。
code-example(format, language="html"). code-example(format, language="html").
EXCEPTION: No provider for Logger! (HeroListComponent -> HeroService -> Logger) EXCEPTION: No provider for Logger! (HeroListComponent -> HeroService -> Logger)
@ -720,8 +747,8 @@ code-example(format, language="html").
create and inject into a new `HeroListComponent`. create and inject into a new `HeroListComponent`.
Angular这是在告诉我们依赖注入器找不到日志服务的*供应商*。 Angular这是在告诉我们依赖注入器找不到日志服务的*供应商*。
它需要这个供应商来创建一个`Logger`实例,以便注入到一个`HeroService`的新实例中, 它需要这个供应商来创建一个`Logger`实例,以便注入到一个`HeroService`的新实例中,
而`HeroService`会在创建`HeroListComponent`的新实例时被创建和注入进去 而`HeroService`的新实例在创建`HeroListComponent`的新实例时需要被创建和注入
The chain of creations started with the `Logger` provider. The *provider* is the subject of our next section. The chain of creations started with the `Logger` provider. The *provider* is the subject of our next section.
@ -730,15 +757,18 @@ code-example(format, language="html").
But wait! What if the logger is optional? But wait! What if the logger is optional?
但是,等一下,如果这个日志服务是可选的呢? 但是,等一下,如果这个日志服务是可选的呢?
<a id="optional"></a> <a id="optional"></a>
### Optional dependencies ### Optional dependencies
### 可选的依赖 ### 可选的依赖
Our `HeroService` currently requires a `Logger`. What if we could get by without a logger? Our `HeroService` currently requires a `Logger`. What if we could get by without a logger?
We'd use it if we had it, ignore it if we didn't. We can do that. We'd use it if we had it, ignore it if we didn't. We can do that.
我们的`HeroService`目前需要`Logger`。如果我们想单独获取它而不用带着日志服务呢? 我们的`HeroService`目前需要`Logger`。如果我们在没有日志服务的时候可以照样继续工作?
我们期望的是:有就用,没有就忽略。这也好办。 我们有它就用,没它就忽略。这也好办。
// #enddocregion logger-service-4 // #enddocregion logger-service-4
// TypeScript only? // TypeScript only?
@ -746,6 +776,7 @@ code-example(format, language="html").
First import the `@Optional()` decorator. First import the `@Optional()` decorator.
首先引入`@Optional()`装饰器。 首先引入`@Optional()`装饰器。
+makeExample('dependency-injection/ts/app/providers.component.ts','import-optional')(format='.') +makeExample('dependency-injection/ts/app/providers.component.ts','import-optional')(format='.')
// #docregion logger-service-5 // #docregion logger-service-5
@ -804,7 +835,7 @@ code-example(format, language="html").
The injector relies on **providers** to create instances of the services The injector relies on **providers** to create instances of the services
that the injector injects into components and other services. that the injector injects into components and other services.
供应商*供*所依赖值的一个具体的运行期版本。 供应商*供*所依赖值的一个具体的运行期版本。
注入器依靠**供应商们**来创建服务的实例,它会被注入器注入到组件或其它服务中。 注入器依靠**供应商们**来创建服务的实例,它会被注入器注入到组件或其它服务中。
We must register a service *provider* with the injector, or it won't know how to create the service. We must register a service *provider* with the injector, or it won't know how to create the service.
@ -814,6 +845,7 @@ code-example(format, language="html").
Earlier we registered the `Logger` service in the `providers` array of the metadata for the `AppComponent` like this: Earlier we registered the `Logger` service in the `providers` array of the metadata for the `AppComponent` like this:
以前,我们通过`AppComponent`元数据中的`providers`数组注册过`Logger`服务,就像这样: 以前,我们通过`AppComponent`元数据中的`providers`数组注册过`Logger`服务,就像这样:
// #enddocregion providers-1 // #enddocregion providers-1
+makeExample('dependency-injection/ts/app/providers.component.ts','providers-logger') +makeExample('dependency-injection/ts/app/providers.component.ts','providers-logger')
// #docregion providers-2 // #docregion providers-2
@ -852,6 +884,7 @@ code-example(format, language="html").
What matters is that the injector has a provider to go to when it needs a `Logger`. What matters is that the injector has a provider to go to when it needs a `Logger`.
问题是:当注入器需要一个`Logger`时,它得先有一个供应商。 问题是:当注入器需要一个`Logger`时,它得先有一个供应商。
// #enddocregion providers-2 // #enddocregion providers-2
// #docregion providers-provide-1 // #docregion providers-provide-1
:marked :marked
@ -861,7 +894,9 @@ code-example(format, language="html").
// Don't mention provide function in Dart // Don't mention provide function in Dart
:marked :marked
### The *Provider* class and *provide* function ### The *Provider* class and *provide* function
### *Provider*类和*provide*函数 ### *Provider*类和*provide*函数
// #docregion providers-provide-1-1 // #docregion providers-provide-1-1
:marked :marked
We wrote the `providers` array like this: We wrote the `providers` array like this:
@ -891,14 +926,14 @@ code-example(format, language="html").
In both approaches &mdash; `Provider` class and `provide` function &mdash; In both approaches &mdash; `Provider` class and `provide` function &mdash;
we supply two arguments. we supply two arguments.
在这两种方式 —— `Provider`类和`provide`函数 —— 中,我们提供了两个参数。 在这两种方式里——在`Provider`类和`provide`函数中——我们提供了两个参数。
// #enddocregion providers-provide-4-1 // #enddocregion providers-provide-4-1
// #docregion providers-provide-4-2 // #docregion providers-provide-4-2
:marked :marked
The first is the [token](#token) that serves as the key for both locating a dependency value The first is the [token](#token) that serves as the key for both locating a dependency value
and registering the provider. and registering the provider.
第一个是[token](#token),它作为键值(key)使用,用于定位依赖值,以及注册这个供应商。 第一个是[令牌token](#token),它作为键值(key)使用,用于定位依赖值,以及注册这个供应商。
// #enddocregion providers-provide-4-2 // #enddocregion providers-provide-4-2
// Dart is different here (uses an optional parameter) // Dart is different here (uses an optional parameter)
@ -914,26 +949,30 @@ code-example(format, language="html").
:marked :marked
<a id="class-provider"></a> <a id="class-provider"></a>
### Alternative class providers ### Alternative class providers
### 另外的“类”供应商
### 其他的“类”供应商
Occasionally we'll ask a different class to provide the service. Occasionally we'll ask a different class to provide the service.
The following code tells the injector The following code tells the injector
to return a `BetterLogger` when something asks for the `Logger`. to return a `BetterLogger` when something asks for the `Logger`.
某些时候,我们会请求一个类来供应此服务。 某些时候,我们会请求一个不同的类来供服务。
下列代码告诉注入器:当有人请求一个`Logger`时,请返回一个`BetterLogger`。 下列代码告诉注入器:当有人请求一个`Logger`时,请返回一个`BetterLogger`。
// #enddocregion providers-alternative-1 // #enddocregion providers-alternative-1
+makeExample('dependency-injection/ts/app/providers.component.ts','providers-4') +makeExample('dependency-injection/ts/app/providers.component.ts','providers-4')
// #docregion providers-alternative-2 // #docregion providers-alternative-2
:marked :marked
### Class provider with dependencies ### Class provider with dependencies
### 带依赖的类供应商 ### 带依赖的类供应商
Maybe an `EvenBetterLogger` could display the user name in the log message. Maybe an `EvenBetterLogger` could display the user name in the log message.
This logger gets the user from the injected `UserService`, This logger gets the user from the injected `UserService`,
which happens also to be injected at the application level. which happens also to be injected at the application level.
也许一个`EvenBetterLogger`(更好的日志)可以在日志消息中显示用户名。 也许一个`EvenBetterLogger`(更好的日志)可以在日志消息中显示用户名。
这个日志服务从一个注入进来的`UserService`中取得用户,`UserService`通常也会在应用级被注入。 这个日志服务从一个注入进来的`UserService`中取得用户,`UserService`通常也会在应用级被注入。
// #enddocregion providers-alternative-2 // #enddocregion providers-alternative-2
+makeExample('dependency-injection/ts/app/providers.component.ts','EvenBetterLogger') +makeExample('dependency-injection/ts/app/providers.component.ts','EvenBetterLogger')
// #docregion providers-alternative-3 // #docregion providers-alternative-3
@ -946,6 +985,7 @@ code-example(format, language="html").
// #docregion providers-aliased-1 // #docregion providers-aliased-1
:marked :marked
### Aliased class providers ### Aliased class providers
### 别名类供应商 ### 别名类供应商
Suppose an old component depends upon an `OldLogger` class. Suppose an old component depends upon an `OldLogger` class.
@ -964,7 +1004,7 @@ code-example(format, language="html").
when a component asks for either the new or the old logger. when a component asks for either the new or the old logger.
The `OldLogger` should be an alias for `NewLogger`. The `OldLogger` should be an alias for `NewLogger`.
当组件请求无论是新的还是老的日志服务时,依赖注入器注入的都应该是同一个单例对象。 不管组件请求的是新的还是老的日志服务,依赖注入器注入的都应该是同一个单例对象。
也就是说,`OldLogger`应该是`NewLogger`的一个别名。 也就是说,`OldLogger`应该是`NewLogger`的一个别名。
We certainly do not want two different `NewLogger` instances in our app. We certainly do not want two different `NewLogger` instances in our app.
@ -979,13 +1019,16 @@ code-example(format, language="html").
The solution: Alias with the `useExisting` option. The solution: Alias with the `useExisting` option.
解决方案:使用`useExisting`选项指定别名。 解决方案:使用`useExisting`选项指定别名。
// #enddocregion providers-aliased-2 // #enddocregion providers-aliased-2
+makeExample('dependency-injection/ts/app/providers.component.ts','providers-6b')(format=".") +makeExample('dependency-injection/ts/app/providers.component.ts','providers-6b')(format=".")
// #docregion providers-value-1 // #docregion providers-value-1
<a id="value-provider"></a> <a id="value-provider"></a>
:marked :marked
### Value providers ### Value providers
### 值供应商 ### 值供应商
// #enddocregion providers-value-1 // #enddocregion providers-value-1
// Typescript only // Typescript only
@ -993,6 +1036,7 @@ code-example(format, language="html").
Sometimes it's easier to provide a ready-made object rather than ask the injector to create it from a class. Sometimes it's easier to provide a ready-made object rather than ask the injector to create it from a class.
有时,提供一个预先做好的对象会比请求注入器从类中创建它更容易。 有时,提供一个预先做好的对象会比请求注入器从类中创建它更容易。
+makeExample('dependency-injection/ts/app/providers.component.ts','silent-logger')(format=".") +makeExample('dependency-injection/ts/app/providers.component.ts','silent-logger')(format=".")
:marked :marked
Then we register a provider with the `useValue` option, Then we register a provider with the `useValue` option,
@ -1005,6 +1049,7 @@ code-example(format, language="html").
<a id="factory-provider"></a> <a id="factory-provider"></a>
:marked :marked
### Factory providers ### Factory providers
### 工厂供应商 ### 工厂供应商
Sometimes we need to create the dependent value dynamically, Sometimes we need to create the dependent value dynamically,
@ -1049,11 +1094,12 @@ code-example(format, language="html").
:marked :marked
Why? We don't know either. Stuff like this happens. Why? We don't know either. Stuff like this happens.
为什么?我们也不知道。事实就是这样。 为什么?我们也不知道。这样的事经常发生
:marked :marked
Instead the `HeroService` constructor takes a boolean flag to control display of secret heroes. Instead the `HeroService` constructor takes a boolean flag to control display of secret heroes.
让`HeroService`的构造函数带上一个布尔型的标志,来控制是否显示隐藏的英雄。 让`HeroService`的构造函数带上一个布尔型的标志,来控制是否显示隐藏的英雄。
// #enddocregion providers-factory-1 // #enddocregion providers-factory-1
+makeExample('dependency-injection/ts/app/heroes/hero.service.ts','internals', 'app/heroes/hero.service.ts (节选)')(format='.') +makeExample('dependency-injection/ts/app/heroes/hero.service.ts','internals', 'app/heroes/hero.service.ts (节选)')(format='.')
// #docregion providers-factory-2 // #docregion providers-factory-2
@ -1067,6 +1113,7 @@ code-example(format, language="html").
A factory provider needs a factory function: A factory provider needs a factory function:
工厂供应商需要一个工厂方法: 工厂供应商需要一个工厂方法:
// #enddocregion providers-factory-2 // #enddocregion providers-factory-2
+makeExample('dependency-injection/ts/app/heroes/hero.service.provider.ts','factory', 'app/heroes/hero.service.provider.ts (节选)')(format='.') +makeExample('dependency-injection/ts/app/heroes/hero.service.provider.ts','factory', 'app/heroes/hero.service.provider.ts (节选)')(format='.')
// #docregion providers-factory-3 // #docregion providers-factory-3
@ -1078,6 +1125,7 @@ code-example(format, language="html").
We inject both the `Logger` and the `UserService` into the factory provider and let the injector pass them along to the factory function: We inject both the `Logger` and the `UserService` into the factory provider and let the injector pass them along to the factory function:
我们同时把`Logger`和`UserService`注入到工厂供应商中,并且让注入器把它们传给工厂方法: 我们同时把`Logger`和`UserService`注入到工厂供应商中,并且让注入器把它们传给工厂方法:
// #enddocregion providers-factory-3 // #enddocregion providers-factory-3
+makeExample('dependency-injection/ts/app/heroes/hero.service.provider.ts','provider', 'app/heroes/hero.service.provider.ts (节选)')(format='.') +makeExample('dependency-injection/ts/app/heroes/hero.service.provider.ts','provider', 'app/heroes/hero.service.provider.ts (节选)')(format='.')
// #docregion providers-factory-4 // #docregion providers-factory-4
@ -1092,9 +1140,9 @@ code-example(format, language="html").
The `Logger` and `UserService` classes serve as tokens for their own class providers. The `Logger` and `UserService` classes serve as tokens for their own class providers.
The injector resolves these tokens and injects the corresponding services into the matching factory function parameters. The injector resolves these tokens and injects the corresponding services into the matching factory function parameters.
`deps`属性是一个[供应商Token](#token)的数组。 `deps`属性是一个[供应商令牌](#token)数组。
`Logger`和`UserService`类作为它们自身供应商的token `Logger`和`UserService`类作为它们自身供应商的令牌
注入器解析这些Token,并且把相应的服务注入到工厂函数参数中所对应的参数中去。 注入器解析这些令牌,并且把相应的服务注入到工厂函数参数中所对应的参数中去。
// #enddocregion providers-factory-4 // #enddocregion providers-factory-4
// #docregion providers-factory-5 // #docregion providers-factory-5
- var lang = current.path[1] - var lang = current.path[1]
@ -1118,6 +1166,7 @@ code-example(format, language="html").
在这个例子中,我们只在`HeroesComponent`中需要它, 在这个例子中,我们只在`HeroesComponent`中需要它,
这里,它代替了元数据`providers`数组中原来的`HeroService`注册。 这里,它代替了元数据`providers`数组中原来的`HeroService`注册。
我们来对比一下新的和老的实现: 我们来对比一下新的和老的实现:
// #enddocregion providers-factory-5 // #enddocregion providers-factory-5
+makeTabs( +makeTabs(
`dependency-injection/ts/app/heroes/heroes.component.ts, `dependency-injection/ts/app/heroes/heroes.component.ts,
@ -1131,22 +1180,22 @@ code-example(format, language="html").
.l-main-section .l-main-section
:marked :marked
## Dependency injection tokens ## Dependency injection tokens
## 依赖注入Token ## 依赖注入令牌
When we register a provider with an injector, we associate that provider with a dependency injection token. When we register a provider with an injector, we associate that provider with a dependency injection token.
The injector maintains an internal *token-provider* map that it references when The injector maintains an internal *token-provider* map that it references when
asked for a dependency. The token is the key to the map. asked for a dependency. The token is the key to the map.
当我们使用注入器注册一个供应商时实际上是把这个供应商和一个DI Token关联起来了。 当我们使用注入器注册一个供应商时实际上是把这个供应商和一个DI令牌关联起来了。
注入器维护一个内部的*Token-供应商*映射表,这个映射表会在请求一个依赖时被引用到。 注入器维护一个内部的*令牌-供应商*映射表,这个映射表会在请求一个依赖时被引用到。
Token就是这个映射表中的键值key 令牌就是这个映射表中的键值key
In all previous examples, the dependency value has been a class *instance*, and In all previous examples, the dependency value has been a class *instance*, and
the class *type* served as its own lookup key. the class *type* served as its own lookup key.
Here we get a `HeroService` directly from the injector by supplying the `HeroService` type as the token: Here we get a `HeroService` directly from the injector by supplying the `HeroService` type as the token:
在以前的所有范例中,依赖值都是一个类*实例*,并且类的*类型*作为它自己的查找键值。 在以前的所有范例中,依赖值都是一个类*实例*,并且类的*类型*作为它自己的查找键值。
这种情况下,我们实际上是直接从注入器中以`HeroService`类型作为Token,来获取一个`HeroService` 实例。 这种情况下,我们实际上是直接从注入器中以`HeroService`类型作为令牌,来获取一个`HeroService` 实例。
// #enddocregion tokens-1 // #enddocregion tokens-1
+makeExample('dependency-injection/ts/app/injector.component.ts','get-hero-service')(format='.') +makeExample('dependency-injection/ts/app/injector.component.ts','get-hero-service')(format='.')
// #docregion tokens-2 // #docregion tokens-2
@ -1156,8 +1205,8 @@ code-example(format, language="html").
and Angular knows to inject the and Angular knows to inject the
service associated with that `HeroService` class token: service associated with that `HeroService` class token:
当我们写一个请求注入基于类的依赖的构造函数时,我们是幸运的。 写一个需要基于类的依赖注入的构造函数对我们来说是很幸运的。
我们只要以`HeroService`类为类型定义一个构造函数参数Angular就会知道把跟`HeroService`类这个Token关联的服务注入进来: 我们只要以`HeroService`类为类型定义一个构造函数参数Angular就会知道把跟`HeroService`类令牌关联的服务注入进来:
// #enddocregion tokens-2 // #enddocregion tokens-2
+makeExample('dependency-injection/ts/app/providers.component.ts','provider-8-ctor')(format=".") +makeExample('dependency-injection/ts/app/providers.component.ts','provider-8-ctor')(format=".")
// #docregion tokens-3 // #docregion tokens-3
@ -1165,6 +1214,7 @@ code-example(format, language="html").
This is especially convenient when we consider that most dependency values are provided by classes. This is especially convenient when we consider that most dependency values are provided by classes.
这是一个特殊的规约,因为我们考虑到大多数依赖值都是以类的形式提供的。 这是一个特殊的规约,因为我们考虑到大多数依赖值都是以类的形式提供的。
// #enddocregion tokens-3 // #enddocregion tokens-3
// #docregion tokens-non-class-deps-1 // #docregion tokens-non-class-deps-1
@ -1201,8 +1251,8 @@ code-example(format, language="html").
我们想让这个`config`对象在注入时可用。 我们想让这个`config`对象在注入时可用。
我们已经知道可以使用一个[值供应商](#value-provider)来注册一个对象。 我们已经知道可以使用一个[值供应商](#value-provider)来注册一个对象。
但是这种情况下我们要把什么用作Token呢? 但是这种情况下我们要把什么用作令牌呢?
我们没办法找一个类来当做Token,因为没有`Config`类。 我们没办法找一个类来当做令牌,因为没有`Config`类。
// Typescript only // Typescript only
<a id="interface"></a> <a id="interface"></a>
@ -1214,7 +1264,7 @@ code-example(format, language="html").
The `CONFIG` constant has an interface, `Config`. Unfortunately, we The `CONFIG` constant has an interface, `Config`. Unfortunately, we
cannot use a TypeScript interface as a token: cannot use a TypeScript interface as a token:
`CONFIG`常量有一个接口:`Config`。不幸的是我们不能把TypeScript接口用作token `CONFIG`常量有一个接口:`Config`。不幸的是我们不能把TypeScript接口用作令牌
+makeExample('dependency-injection/ts/app/providers.component.ts','providers-9a-interface')(format=".") +makeExample('dependency-injection/ts/app/providers.component.ts','providers-9a-interface')(format=".")
+makeExample('dependency-injection/ts/app/providers.component.ts','provider-9a-ctor-interface')(format=".") +makeExample('dependency-injection/ts/app/providers.component.ts','provider-9a-ctor-interface')(format=".")
:marked :marked
@ -1240,7 +1290,8 @@ h3 OpaqueToken
p. p.
The solution is to define and use an !{opaquetoken}. The solution is to define and use an !{opaquetoken}.
The definition looks like this: The definition looks like this:
p 解决方案是定义和使用用一个!{opaquetoken}不透明的Token。定义方式类似于这样
p 解决方案是定义和使用用一个!{opaquetoken}(不透明的令牌)。定义方式类似于这样:
// #enddocregion tokens-opaque-1 // #enddocregion tokens-opaque-1
+makeExample('dependency-injection/ts/app/app.config.ts','token')(format='.') +makeExample('dependency-injection/ts/app/app.config.ts','token')(format='.')
:marked :marked
@ -1286,12 +1337,13 @@ p 解决方案是定义和使用用一个!{opaquetoken}不透明的Token
:marked :marked
## Summary ## Summary
## 总结 ## 总结
We learned the basics of Angular dependency injection in this chapter. We learned the basics of Angular dependency injection in this chapter.
We can register various kinds of providers, We can register various kinds of providers,
and we know how to ask for an injected object (such as a service) by and we know how to ask for an injected object (such as a service) by
adding a parameter to a constructor. adding a parameter to a constructor.
在本章中,我们学了Angular依赖注入的基础。 在本章中,我们学了Angular依赖注入的基础。
我们可以注册很多种类的供应商,还知道了该如何通过添加构造函数的参数来请求一个被注入对象(比如服务)。 我们可以注册很多种类的供应商,还知道了该如何通过添加构造函数的参数来请求一个被注入对象(比如服务)。
Angular dependency injection is more capable than we've described. Angular dependency injection is more capable than we've described.
@ -1309,11 +1361,12 @@ p 解决方案是定义和使用用一个!{opaquetoken}不透明的Token
:marked :marked
### Appendix: Working with injectors directly ### Appendix: Working with injectors directly
### 附录:直接使用注入器工作 ### 附录:直接使用注入器工作
We rarely work directly with an injector. We rarely work directly with an injector.
Here's an `InjectorComponent` that does. Here's an `InjectorComponent` that does.
我们很少直接使用注入器工作。 我们很少直接使用注入器工作。
这里是一个`InjectorComponent`,它用到了 这里的`InjectorComponent`直接使用了注入器
// #enddocregion appendix-explicit-injector-1 // #enddocregion appendix-explicit-injector-1
+makeExample('dependency-injection/ts/app/injector.component.ts', 'injector', 'app/injector.component.ts') +makeExample('dependency-injection/ts/app/injector.component.ts', 'injector', 'app/injector.component.ts')
// #docregion appendix-explicit-injector-2 // #docregion appendix-explicit-injector-2