基础知识-依赖注入 一审完毕
This commit is contained in:
parent
68631c58f5
commit
8e84ec2c15
|
@ -45,14 +45,14 @@ include ../_util-fns
|
|||
|
||||
The problem is that our `Car` class is brittle, inflexible, and hard to test.
|
||||
|
||||
问题是,我们这个`Car`类过于脆弱、缺乏弹性并且难以测试。
|
||||
问题在于,我们这个`Car`类过于脆弱、缺乏弹性并且难以测试。
|
||||
|
||||
Our `Car` needs an engine and tires. Instead of asking for them,
|
||||
the `Car` constructor creates its own copies by "new-ing" them from
|
||||
the very specific classes, `Engine` and `Tires`.
|
||||
|
||||
我们的汽车(`Car`)类需要一个引擎(`Engine`)和轮胎(`Tires`),它没有去请求一个现成的实例,
|
||||
而是在构造函数中用具体的类`Engine`和`Tires`新创建了一份只给自己用的副本。
|
||||
而是在构造函数中用具体的类`Engine`和`Tires`新创建了一份只供自己用的副本。
|
||||
|
||||
What if the `Engine` class evolves and its constructor requires a parameter?
|
||||
Our `Car` is broken and stays broken until we rewrite it along the lines of
|
||||
|
@ -65,7 +65,7 @@ include ../_util-fns
|
|||
|
||||
如果`Engine`类升级了,并且它的构造函数要求传入一个参数了怎么办?
|
||||
我们这个`Car`类就被破坏了,并且直到我们把创建代码重写为`#{prefix}engine = new Engine(theNewParameter)`之前,它都坏着。
|
||||
当我们首次写`Car`类时,我们不会在乎`Engine`构造函数的参数。其实现在我们也不想在乎。
|
||||
当我们首次写`Car`类时,我们不会在乎`Engine`构造函数的参数。就算现在我们也不想在乎。
|
||||
但当`Engine`类的定义发生变化时,我们就不得不在乎了,`Car`类也不得不跟着改变。
|
||||
这就会让`Car`类过于脆弱。
|
||||
|
||||
|
@ -101,14 +101,14 @@ include ../_util-fns
|
|||
How do we confirm that it actually does flash a warning
|
||||
if we can't swap in low-pressure tires during the test?
|
||||
|
||||
如果我们的`Car`应该在轮胎气压低的时候闪一个警示灯该怎么办?
|
||||
如果我们没法在测试期间换上一个低气压的轮胎,我们该如何确认它确实能正确的闪警示灯?
|
||||
如果我们的`Car`应该在轮胎气压低的时候闪动一个警示灯该怎么办?
|
||||
如果我们没法在测试期间换上一个低气压的轮胎,我们该如何确认它能正确的闪警示灯?
|
||||
|
||||
We have no control over the car's hidden dependencies.
|
||||
When we can't control the dependencies, a class becomes difficult to test.
|
||||
|
||||
我们没法控制这辆车背后隐藏的依赖。
|
||||
而如果我们不能控制这些依赖,类就会变得难于测试。
|
||||
而如果不能控制这些依赖,类就会变得难以测试。
|
||||
|
||||
How can we make `Car` more robust, flexible, and testable?
|
||||
|
||||
|
@ -132,14 +132,14 @@ include ../_util-fns
|
|||
|
||||
发生了什么?我们把依赖的定义移到了构造函数中。
|
||||
我们的`Car`类不再创建引擎或者轮胎。
|
||||
它仅仅“消费”它们。(译注:指直接使用成品)
|
||||
它仅仅“消费”它们。
|
||||
// #enddocregion why-3-1
|
||||
// TypeScript only
|
||||
.l-sub-section
|
||||
:marked
|
||||
We also leverage TypeScript's constructor syntax for declaring parameters and properties simultaneously.
|
||||
|
||||
我们还借助了TypeScript的构造语法来同时定义参数和属性。
|
||||
我们又一次借助TypeScript的构造器语法来同时定义参数和属性。
|
||||
// #docregion why-3-2
|
||||
:marked
|
||||
Now we create a car by passing the engine and tires to the constructor.
|
||||
|
@ -156,12 +156,12 @@ include ../_util-fns
|
|||
We can pass in any kind of engine or tires we like, as long as they
|
||||
conform to the general API requirements of an engine or tires.
|
||||
|
||||
酷!引擎和轮胎这两个依赖的定义从`Car`类本身解耦开了。
|
||||
只要喜欢,我们就可以传入任何类型的引擎或轮胎,只要它们能满足引擎或轮胎的普遍API需求。
|
||||
酷!引擎和轮胎这两个“依赖”的定义从`Car`类本身解耦开了。
|
||||
只要喜欢,我们就可以传入任何类型的引擎或轮胎,只要它们能满足引擎或轮胎的通用API需求。
|
||||
|
||||
If someone extends the `Engine` class, that is not `Car`'s problem.
|
||||
|
||||
如果有人扩展了`Engine`类,那就不再是`Car`类的烦恼。
|
||||
如果有人扩展了`Engine`类,那就不再是`Car`类的烦恼了。
|
||||
// #enddocregion why-4
|
||||
// Must copy the following, due to indented +make.
|
||||
.l-sub-section
|
||||
|
@ -200,7 +200,7 @@ include ../_util-fns
|
|||
It's a coding pattern in which a class receives its dependencies from external
|
||||
sources rather than creating them itself.
|
||||
|
||||
它是一个编程模式,该模式可以让一个类从外部源中获得它的依赖,而不必自己创建它们。
|
||||
它是一种编程模式,该模式可以让一个类从外部源中获得它的依赖,而不必亲自创建它们。
|
||||
|
||||
Cool! But what about that poor consumer?
|
||||
Anyone who wants a `Car` must now
|
||||
|
@ -227,20 +227,20 @@ include ../_util-fns
|
|||
|
||||
现在只需要三个创建方法,这还不算太坏。
|
||||
但是当应用规模变大之后,维护它将变得惊险重重。
|
||||
这个工厂类将变成一个由相互依赖的工厂方法构成的矩形蜘蛛网。
|
||||
这个工厂类将变成一个由相互依赖的工厂方法构成的巨型蜘蛛网。
|
||||
|
||||
Wouldn't it be nice if we could simply list the things we want to build without
|
||||
having to define which dependency gets injected into what?
|
||||
|
||||
如果我们能简单的列出我们想建造的东西,而不用定义要把哪些依赖注入哪些对象,是不是会很美妙?
|
||||
如果我们能简单的列出我们想建造的东西,而不用定义要把哪些依赖注入哪些对象,那该多好!
|
||||
|
||||
This is where the dependency injection framework comes into play.
|
||||
Imagine the framework had something called an _injector_.
|
||||
We register some classes with this injector, and it figures out how to create them.
|
||||
|
||||
到了让依赖注入框架一展身手的时候了!
|
||||
想象框架中有一个叫做 _注入器(injector)_ 的东西。
|
||||
我们使用这个注入器注册一些类,还会指出该如何创建它们。
|
||||
想象框架中有一个叫做_注入器Injector_的东西。
|
||||
我们使用这个注入器注册一些类,它会指出该如何创建它们。
|
||||
|
||||
When we need a `Car`, we simply ask the injector to get it for us and we're good to go.
|
||||
|
||||
|
@ -257,11 +257,11 @@ include ../_util-fns
|
|||
多方皆赢的方式。`Car`不需要知道如何创建`Engine`和`Tires`的任何事。
|
||||
消费者不知道如何创建`Car`的任何事。
|
||||
我们不需要一个巨大的工厂类来维护它们。
|
||||
`Car`和消费者只要简单的说出它们想要什么,注入器就会交付给它们。
|
||||
`Car`和消费者只要简单的说出它们想要什么,注入器就会交给它们。
|
||||
|
||||
This is what a **dependency injection framework** is all about.
|
||||
|
||||
这就是关于 **依赖注入框架** 的一切。
|
||||
这就是“什么是**依赖注入框架**”问题的答案。
|
||||
|
||||
Now that we know what dependency injection is and appreciate its benefits,
|
||||
let's see how it is implemented in Angular.
|
||||
|
@ -278,7 +278,7 @@ include ../_util-fns
|
|||
Angular ships with its own dependency injection framework. This framework can also be used
|
||||
as a standalone module by other applications and frameworks.
|
||||
|
||||
Angular自带了它自己的依赖注入框架。此框架也能被当做独立模块用于其它应用和框架中。
|
||||
Angular自带了它自己的依赖注入框架。但此框架也能被当做独立模块用于其它应用和框架中。
|
||||
|
||||
That sounds nice. What does it do for us when building components in Angular?
|
||||
Let's see, one step at a time.
|
||||
|
@ -289,7 +289,7 @@ include ../_util-fns
|
|||
We'll begin with a simplified version of the `HeroesComponent`
|
||||
that we built in the [The Tour of Heroes](../tutorial/).
|
||||
|
||||
我们从曾在[英雄指南](../tutorial/)中构建过的`HeroesComponent`的一个简化版本开始。
|
||||
我们从当初在[英雄指南](../tutorial/)中构建过的`HeroesComponent`的一个简化版本开始。
|
||||
// #enddocregion di-1
|
||||
+makeTabs(
|
||||
`dependency-injection/ts/app/heroes/heroes.component.1.ts,
|
||||
|
@ -322,11 +322,11 @@ include ../_util-fns
|
|||
|
||||
现在`HeroListComponent`从`HEROES`获得英雄数据,一个在另一个文件中定义的内存数据集。
|
||||
它在开发的早期阶段可能还够用,但离完美就差得远了。
|
||||
一旦我们开始测试此组件,或者想从远端服务器获得英雄数据,我们将不得不修改`heroes`的实现,并要修改每个用到了`HEROES`模拟数据的地方。
|
||||
一旦我们开始测试此组件,或者想从远端服务器获得英雄数据,我们就不得不修改`heroes`的实现,并要修改每个用到了`HEROES`模拟数据的地方。
|
||||
|
||||
Let's make a service that hides how we get hero data.
|
||||
|
||||
我们来制作一个服务,来把获取英雄数据的代码封装起来。
|
||||
我们来制作一个服务,把获取英雄数据的代码封装起来。
|
||||
// #enddocregion di-3
|
||||
|
||||
// Unnecessary for Dart
|
||||
|
@ -341,7 +341,7 @@ include ../_util-fns
|
|||
Our `HeroService` exposes a `getHeroes` method that returns
|
||||
the same mock data as before, but none of its consumers need to know that.
|
||||
|
||||
我们的`HeroService`暴露了`getHeroes`方法,用于返回跟以前一样的模拟数据,但是它的消费者不需要了解这一点。
|
||||
我们的`HeroService`暴露了`getHeroes`方法,用于返回跟以前一样的模拟数据,但它的消费者不需要知道这一点。
|
||||
// #enddocregion di-4
|
||||
// #docregion di-5
|
||||
.l-sub-section
|
||||
|
@ -370,13 +370,12 @@ include ../_util-fns
|
|||
### Configuring the injector
|
||||
### 配置注入器
|
||||
|
||||
<a id="bootstrap"></a>
|
||||
We don't have to create an Angular injector.
|
||||
Angular creates an application-wide injector for us during the bootstrap process.
|
||||
|
||||
<a id="bootstrap"></a>
|
||||
我们并不需要自己创建一个Angular注入器。
|
||||
Angular在启动期间会自动为我们创建一个应用级注入器。
|
||||
Angular在启动期间会自动为我们创建一个全应用级注入器。
|
||||
// #enddocregion di-configure-injector-1
|
||||
+makeExample('dependency-injection/ts/app/main.ts', 'bootstrap', 'app/main.ts (节选)')(format='.')
|
||||
// #docregion di-configure-injector-2
|
||||
|
@ -386,9 +385,9 @@ include ../_util-fns
|
|||
We'll explain what [providers](#providers) are later in this chapter.
|
||||
Before we do, let's see an example of provider registration during bootstrapping:
|
||||
|
||||
我们必须先注册**供应商**来配置好注入器,这在创建应用所需的服务时会用到。
|
||||
我们必须先注册**供应商Provider**来配置好注入器,这在创建应用所需的服务时会用到。
|
||||
我们将在本章的稍后部分解释什么是[供应商](#providers)。
|
||||
在此之前,我们先来看一个启动期间的注册供应商的例子。
|
||||
在此之前,我们先来看一个在启动期间注册供应商的例子。
|
||||
// #enddocregion di-configure-injector-2
|
||||
+makeExample('dependency-injection/ts/app/main.1.ts', 'bootstrap')(format='.')
|
||||
// #docregion di-configure-injector-3
|
||||
|
@ -412,7 +411,7 @@ include ../_util-fns
|
|||
Because the `HeroService` is used within the *Heroes* feature area —
|
||||
and nowhere else — the ideal place to register it is in the top-level `HeroesComponent`.
|
||||
|
||||
首选的方式是在应用组件中注册应用级的供应商。
|
||||
首选的方式是在应用的组件中注册供应商。
|
||||
因为`HeroService`是用于*英雄*功能区的 —— 并且没别处用它 —— 所以注册它的理想地点就是`HeroesComponent`的顶层。
|
||||
// #enddocregion di-configure-injector-3
|
||||
// #docregion di-register-providers-1
|
||||
|
@ -518,7 +517,7 @@ include ../_util-fns
|
|||
但无论在《英雄指南》还是其它范例中,我们都没有发现这样的代码。
|
||||
在必要时,我们*可以*写[使用显式注入器的代码](#explicit-injector),但却很少这样做。
|
||||
当Angular为我们创建组件时 —— 无论通过像`<hero-list></hero-list>`这样的HTML标签还是通过[路由](./router.html)导航到组件 —— 它都会自己管理好注入器的创建和调用。
|
||||
只要让Angular做好它自己的工作,我们就能安心享受自动依赖注入带来的好处。
|
||||
只要让Angular做好它自己的工作,我们就能安心享受“自动依赖注入”带来的好处。
|
||||
// #enddocregion di-create-injector-implicitly-2
|
||||
// #docregion di-singleton-services
|
||||
:marked
|
||||
|
@ -561,7 +560,7 @@ include ../_util-fns
|
|||
:marked
|
||||
Learn more in [Testing](../testing/index.html).
|
||||
|
||||
学习更多知识,参见[测试](../testing/index.html)。
|
||||
要学习更多知识,参见[测试](../testing/index.html)。
|
||||
// #enddocregion di-testing-component-2
|
||||
// #docregion di-service-service-1
|
||||
:marked
|
||||
|
@ -593,7 +592,7 @@ include ../_util-fns
|
|||
The constructor now asks for an injected instance of a `Logger` and stores it in a private property called `_logger`.
|
||||
We call that property within our `getHeroes` method when anyone asks for heroes.
|
||||
|
||||
这个构造函数现在会要求一个`Logger`类的实例注入进来,并且把它存在一个名叫`_logger`的私有属性中。
|
||||
现在,这个构造函数会要求一个`Logger`类的实例注入进来,并且把它存到一个名为`_logger`的私有属性中。
|
||||
当别人要求获得英雄数据时,我们会在`getHeroes`方法中使用这个属性。
|
||||
// #enddocregion di-service-service-2
|
||||
// #docregion di-injectable-1
|
||||
|
@ -605,7 +604,7 @@ include ../_util-fns
|
|||
:marked
|
||||
<a id="injectable"></a>
|
||||
### Why @Injectable?
|
||||
### 为什么要@Injectable?
|
||||
### 为什么要加@Injectable?
|
||||
Notice the `@Injectable()` #{decoration} above the service class.
|
||||
We haven't seen `@Injectable()` before.
|
||||
As it happens, we could have added it to our first version of `HeroService`.
|
||||
|
@ -629,7 +628,7 @@ include ../_util-fns
|
|||
- var decorated = lang == 'dart' ? 'annotated' : 'decorated'
|
||||
- var decoratedCn = lang == 'dart' ? '注解' : '装饰器'
|
||||
- var any_decorator = lang == 'dart' ? '' : 'TypeScript generates metadata for any class with a decorator, and any decorator will do.'
|
||||
- var any_decoratorCn = lang == 'dart' ? '' : 'TypeScript会为任何带有装饰器的类生成元数据,并且会为任何装饰器都生成。'
|
||||
- var any_decoratorCn = lang == 'dart' ? '' : 'TypeScript会为任何带有装饰器的类生成元数据,而且会为任何装饰器都生成。'
|
||||
.callout.is-helpful
|
||||
header Suggestion: add @Injectable() to every service class
|
||||
header 建议:为每一个服务类添加@Injectable()
|
||||
|
@ -637,10 +636,10 @@ include ../_util-fns
|
|||
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:
|
||||
|
||||
我们建议为每个服务类都添加`@Injectable()`装饰器,即使它们因为还没有任何依赖,而在技术上并不需要它。这是因为:
|
||||
我们建议为每个服务类都添加`@Injectable()`装饰器,即使它们因为目前没有任何依赖,而在技术上并不需要它。这是因为:
|
||||
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>面向未来:</b> 在我们将来添加依赖时,不必记着添加<code>@Injectable()</code>。
|
||||
li <b>面向未来:</b> 在我们将来添加依赖时,不用怕忘了添加<code>@Injectable()</code>。
|
||||
li <b>Consistency:</b> All services follow the same rules, and we don't have to wonder why #{a_decorator} is missing.
|
||||
li <b>统一性:</b> 所有服务都遵循着同样的规则,我们就不必困惑为什么#{a_decoratorCn}被省略了。
|
||||
|
||||
|
@ -649,7 +648,7 @@ include ../_util-fns
|
|||
Some developers prefer to add it only where needed and that's a reasonable policy too.
|
||||
|
||||
虽然我们建议给所有服务类都添加`@Injectable()`,但你也不必受此约束。
|
||||
有些开发人员就喜欢只在需要的时候才添加,那也同样是一个合理的准则。
|
||||
有些开发人员就是喜欢在需要的时候才添加,那也同样是一个合理的准则。
|
||||
|
||||
.l-sub-section
|
||||
:marked
|
||||
|
@ -664,13 +663,13 @@ include ../_util-fns
|
|||
// #enddocregion di-injectable-2
|
||||
.callout.is-critical
|
||||
header Always include the parentheses
|
||||
header 总是要带着括号
|
||||
header 总要带着括号
|
||||
:marked
|
||||
Always use `@Injectable()`, not just `@Injectable`.
|
||||
Our application will fail mysteriously if we forget the parentheses.
|
||||
|
||||
总是使用`@Injectable()`的形式,不能只用`@Injectable`。
|
||||
如果忘了括号,我们的应用就是神不知鬼不觉的失败!
|
||||
如果忘了括号,我们的应用就会神不知鬼不觉的失败!
|
||||
|
||||
// #docregion logger-service-1
|
||||
.l-main-section
|
||||
|
@ -678,7 +677,8 @@ include ../_util-fns
|
|||
## Creating and registering a logger service
|
||||
## 创建和注册日志服务
|
||||
We're injecting a logger into our `HeroService` in two steps:
|
||||
我们要把日志服务注入到`HeroService`中需要两步:
|
||||
|
||||
要把日志服务注入到`HeroService`中需要两步:
|
||||
1. Create the logger service.
|
||||
1. 创建日志服务。
|
||||
1. Register it with the application.
|
||||
|
@ -697,8 +697,8 @@ include ../_util-fns
|
|||
so we put it at the root level of the application in the `app/` folder, and
|
||||
we register it in the `providers` array of the metadata for our application root component, `AppComponent`.
|
||||
|
||||
我们很可能在应用的任何地方都使用同一个日志服务实例。
|
||||
所以,我们把它放到`app/`目录下,也就是应用的顶级,并且把它注册到我们的根组件`AppComponent`上,放到元数据中的`providers`数组里。
|
||||
我们很可能在应用的任何地方都使用同一个日志服务的实例。
|
||||
所以,我们把它放到`app/`目录下,也就是应用的顶级,并把它注册到我们的根组件`AppComponent`上,放到元数据中的`providers`数组里。
|
||||
+makeExample('dependency-injection/ts/app/providers.component.ts','providers-logger', 'app/app.component.ts (节选)')
|
||||
// #docregion logger-service-3
|
||||
:marked
|
||||
|
@ -728,9 +728,8 @@ code-example(format, language="html").
|
|||
这个“创建链”始于`Logger`的供应商。这个*供应商*就是我们下一节的主题。
|
||||
|
||||
But wait! What if the logger is optional?
|
||||
<a id="optional"></a>
|
||||
|
||||
但,等一下,如果这个日志服务是可选的呢?
|
||||
但是,等一下,如果这个日志服务是可选的呢?
|
||||
<a id="optional"></a>
|
||||
### Optional dependencies
|
||||
### 可选的依赖
|
||||
|
@ -770,8 +769,8 @@ code-example(format, language="html").
|
|||
the injector will inject `null`. We have a method that logs.
|
||||
What can we do to avoid a null reference exception?
|
||||
|
||||
准备一个空的日志服务。如果我们在代码中不注册一个,注入器就会注入`null`。
|
||||
假设我们有一个带日志的方法,该如何消除空指针错误呢?
|
||||
准备迎接一个空的日志服务。如果我们在代码中不注册一个,注入器就会注入`null`。
|
||||
我们有一个需要记日志的方法,这时该如何消除空指针错误呢?
|
||||
|
||||
We could substitute a *do-nothing* logger stub so that calling methods continue to work:
|
||||
|
||||
|
@ -805,7 +804,7 @@ code-example(format, language="html").
|
|||
The injector relies on **providers** to create instances of the 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.
|
||||
|
@ -822,9 +821,9 @@ code-example(format, language="html").
|
|||
- var implements = lang == 'dart' ? 'implements' : 'looks and behaves like a '
|
||||
- var implementsCn = lang == 'dart' ? '实现' : '表现和行为像'
|
||||
- var objectlike = lang == 'dart' ? '' : 'an object that behaves like '
|
||||
- var objectlikeCn = lang == 'dart' ? '' : '一个对象,它的行为像'
|
||||
- var objectlikeCn = lang == 'dart' ? '' : '一个对象,其行为像'
|
||||
- var loggerlike = lang == 'dart' ? '' : 'We could provide a logger-like object. '
|
||||
- var loggerlikeCn = lang == 'dart' ? '' : '我们可以提供一个像logger的对象。'
|
||||
- var loggerlikeCn = lang == 'dart' ? '' : '也就是可以提供一个像logger的对象。'
|
||||
:marked
|
||||
The `providers` array appears to hold a service class.
|
||||
In reality it holds an instance of the [Provider](../api/core/Provider-class.html) class that can create that service.
|
||||
|
@ -838,21 +837,21 @@ code-example(format, language="html").
|
|||
|
||||
有很多方式可以*提供*一些#{implementsCn} `Logger`类的东西。
|
||||
`Logger`类本身是一个显而易见而且自然而然的供应商 —— 它有正确的形态,并且它设计出来就是等着被创建的。
|
||||
但它不是唯一的方式。
|
||||
但它不是唯一的选项。
|
||||
|
||||
We can configure the injector with alternative providers that can deliver #{objectlike} a `Logger`.
|
||||
We could provide a substitute class. #{loggerlike}
|
||||
We could give it a provider that calls a logger factory function.
|
||||
Any of these approaches might be a good choice under the right circumstances.
|
||||
|
||||
我们可以使用另外的各种供应商来配置这个注入器,只要它们能交付#{objectlikeCn}`Logger`。
|
||||
我们可以使用另外的各种供应商来配置这个注入器,只要它们能交付#{objectlikeCn}`Logger`就可以了。
|
||||
我们可以提供一个替身类。#{loggerlikeCn}
|
||||
我们可以给它一个供应商,让它调用一个可以创建日志服务的工厂函数。
|
||||
所有这些方法,只要在正确的情境下,都可能是一个好的选择。
|
||||
所有这些方法,只要用在正确的场合,都可能是一个好的选择。
|
||||
|
||||
What matters is that the injector has a provider to go to when it needs a `Logger`.
|
||||
|
||||
重点是:当注入器需要一个`Logger`时,它得先有一个供应商。
|
||||
问题是:当注入器需要一个`Logger`时,它得先有一个供应商。
|
||||
// #enddocregion providers-2
|
||||
// #docregion providers-provide-1
|
||||
:marked
|
||||
|
@ -910,18 +909,18 @@ code-example(format, language="html").
|
|||
|
||||
第二个是一个供应商定义对象。
|
||||
我们可以把它看做一个指导如何创建依赖值的*配方*。
|
||||
有很多方式创建依赖值…… 也有很多方式可以写菜谱。
|
||||
有很多方式创建依赖值…… 也有很多方式可以写配方。
|
||||
// #docregion providers-alternative-1
|
||||
:marked
|
||||
<a id="class-provider"></a>
|
||||
### Alternative class providers
|
||||
### 另外的类供应商
|
||||
### 另外的“类”供应商
|
||||
|
||||
Occasionally we'll ask a different class to provide the service.
|
||||
The following code tells the injector
|
||||
to return a `BetterLogger` when something asks for the `Logger`.
|
||||
|
||||
某些时候,我们会请求另一个类来提供此服务。
|
||||
某些时候,我们会请求另一个类来供应此服务。
|
||||
下列代码告诉注入器:当有人请求一个`Logger`时,请返回一个`BetterLogger`。
|
||||
// #enddocregion providers-alternative-1
|
||||
+makeExample('dependency-injection/ts/app/providers.component.ts','providers-4')
|
||||
|
@ -965,7 +964,7 @@ code-example(format, language="html").
|
|||
when a component asks for either the new or the old logger.
|
||||
The `OldLogger` should be an alias for `NewLogger`.
|
||||
|
||||
当组件请求无论是新的还是老的日志服务时,依赖注入器注入的都应该是这个单例对象。
|
||||
当组件请求无论是新的还是老的日志服务时,依赖注入器注入的都应该是同一个单例对象。
|
||||
也就是说,`OldLogger`应该是`NewLogger`的一个别名。
|
||||
|
||||
We certainly do not want two different `NewLogger` instances in our app.
|
||||
|
@ -993,7 +992,7 @@ code-example(format, language="html").
|
|||
:marked
|
||||
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=".")
|
||||
:marked
|
||||
Then we register a provider with the `useValue` option,
|
||||
|
@ -1028,7 +1027,7 @@ code-example(format, language="html").
|
|||
Only authorized users should see secret heroes.
|
||||
|
||||
我们通过添加一个新的业务需求来说明这一点:
|
||||
HeroService必须对普通用户里隐藏 *秘密* 英雄。
|
||||
HeroService必须对普通用户隐藏掉*秘密*英雄。
|
||||
只有获得授权的用户才能看到秘密英雄。
|
||||
|
||||
Like the `EvenBetterLogger`, the `HeroService` needs a fact about the user.
|
||||
|
@ -1054,7 +1053,7 @@ code-example(format, language="html").
|
|||
:marked
|
||||
Instead the `HeroService` constructor takes a boolean flag to control display of secret heroes.
|
||||
|
||||
让`HeroService`的构造函数带上一个逻辑型的标志,来控制是否显示隐藏的英雄。
|
||||
让`HeroService`的构造函数带上一个布尔型的标志,来控制是否显示隐藏的英雄。
|
||||
// #enddocregion providers-factory-1
|
||||
+makeExample('dependency-injection/ts/app/heroes/hero.service.ts','internals', 'app/heroes/hero.service.ts (节选)')(format='.')
|
||||
// #docregion providers-factory-2
|
||||
|
@ -1093,9 +1092,9 @@ code-example(format, language="html").
|
|||
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.
|
||||
|
||||
`deps`属性是一个[供应商token](#token)的数组。
|
||||
`deps`属性是一个[供应商Token](#token)的数组。
|
||||
`Logger`和`UserService`类作为它们自身供应商的token。
|
||||
注入器解析这些token,并且把相应的服务注入到工厂函数参数中所对应的参数中去。
|
||||
注入器解析这些Token,并且把相应的服务注入到工厂函数参数中所对应的参数中去。
|
||||
// #enddocregion providers-factory-4
|
||||
// #docregion providers-factory-5
|
||||
- var lang = current.path[1]
|
||||
|
@ -1138,16 +1137,16 @@ code-example(format, language="html").
|
|||
The injector maintains an internal *token-provider* map that it references when
|
||||
asked for a dependency. The token is the key to the map.
|
||||
|
||||
当我们使用注入器注册一个供应商时,实际上是把这个供应商和一个DI token关联起来了。
|
||||
注入器维护一个内部的*token-供应商*映射表,这个映射表会在请求一个依赖时被引用到。
|
||||
token就是这个映射表中的键值(key)。
|
||||
当我们使用注入器注册一个供应商时,实际上是把这个供应商和一个DI Token关联起来了。
|
||||
注入器维护一个内部的*Token-供应商*映射表,这个映射表会在请求一个依赖时被引用到。
|
||||
Token就是这个映射表中的键值(key)。
|
||||
|
||||
In all previous examples, the dependency value has been a class *instance*, and
|
||||
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:
|
||||
|
||||
在以前的所有范例中,依赖值都是一个类*实例*,并且类的*类型*作为它自己的查找键值。
|
||||
这种情况下,我们实际上是直接从注入器中以`HeroService`类型作为token,来获取一个`HeroService` 实例。
|
||||
这种情况下,我们实际上是直接从注入器中以`HeroService`类型作为Token,来获取一个`HeroService` 实例。
|
||||
// #enddocregion tokens-1
|
||||
+makeExample('dependency-injection/ts/app/injector.component.ts','get-hero-service')(format='.')
|
||||
// #docregion tokens-2
|
||||
|
@ -1158,7 +1157,7 @@ code-example(format, language="html").
|
|||
service associated with that `HeroService` class token:
|
||||
|
||||
当我们写一个请求注入基于类的依赖的构造函数时,我们是幸运的。
|
||||
我们只要以`HeroService`类为类型,定义一个构造函数参数,Angular就会知道把跟`HeroService`类这个token关联的服务注入进来:
|
||||
我们只要以`HeroService`类为类型,定义一个构造函数参数,Angular就会知道把跟`HeroService`类这个Token关联的服务注入进来:
|
||||
// #enddocregion tokens-2
|
||||
+makeExample('dependency-injection/ts/app/providers.component.ts','provider-8-ctor')(format=".")
|
||||
// #docregion tokens-3
|
||||
|
@ -1202,8 +1201,8 @@ code-example(format, language="html").
|
|||
|
||||
我们想让这个`config`对象在注入时可用。
|
||||
我们已经知道可以使用一个[值供应商](#value-provider)来注册一个对象。
|
||||
但是这种情况下我们要把什么用作token呢?
|
||||
我们没办法找一个类来当做token,因为没有`Config`类。
|
||||
但是这种情况下我们要把什么用作Token呢?
|
||||
我们没办法找一个类来当做Token,因为没有`Config`类。
|
||||
|
||||
// Typescript only
|
||||
<a id="interface"></a>
|
||||
|
@ -1241,8 +1240,7 @@ h3 OpaqueToken
|
|||
p.
|
||||
The solution is to define and use an !{opaquetoken}.
|
||||
The definition looks like this:
|
||||
|
||||
解决方案是定义和使用用一个!{opaquetoken}(不透明的Token)。定义方式类似于这样:
|
||||
p 解决方案是定义和使用用一个!{opaquetoken}(不透明的Token)。定义方式类似于这样:
|
||||
// #enddocregion tokens-opaque-1
|
||||
+makeExample('dependency-injection/ts/app/app.config.ts','token')(format='.')
|
||||
:marked
|
||||
|
@ -1260,7 +1258,8 @@ p.
|
|||
Now we can inject the #{configuration} object into any constructor that needs it, with
|
||||
the help of an `@Inject` #{decorator} that tells Angular how to find the #{configuration} dependency value.
|
||||
|
||||
现在,我们可以把这个#{configurationCn}对象注入到任何需要它的构造函数中,在`@Inject`#{decoratorCn}的帮助下,告诉Angular如何找到这个#{configuration}依赖的值。
|
||||
现在,我们可以把这个#{configurationCn}对象注入到任何需要它的构造函数中,在`@Inject`#{decoratorCn}的帮助下,
|
||||
告诉Angular如何找到这个#{configurationCn}依赖的值。
|
||||
// #enddocregion tokens-opaque-2
|
||||
+makeExample('dependency-injection/ts/app/providers.component.ts','provider-9b-ctor')(format=".")
|
||||
|
||||
|
@ -1293,7 +1292,7 @@ p.
|
|||
adding a parameter to a constructor.
|
||||
|
||||
在本章中,我们学会了Angular依赖注入的基础。
|
||||
我们可以注册很多种类的供应商,而且我们知道了该如何通过添加构造函数的参数来请求一个被注入对象(比如服务)。
|
||||
我们可以注册很多种类的供应商,还知道了该如何通过添加构造函数的参数来请求一个被注入对象(比如服务)。
|
||||
|
||||
Angular dependency injection is more capable than we've described.
|
||||
We can learn more about its advanced features, beginning with its support for
|
||||
|
@ -1301,7 +1300,7 @@ p.
|
|||
[Hierarchical Dependency Injection](hierarchical-dependency-injection.html) chapter.
|
||||
|
||||
Angular依赖注入比我们描述的更能干。
|
||||
我们还可以学到它的更多高级特性,从它对嵌套注入器的支持开始,就在[分层依赖注入](hierarchical-dependency-injection.html)一章。
|
||||
我们还可以学到它的更多高级特性,从它对嵌套注入器的支持开始,参见[分层依赖注入](hierarchical-dependency-injection.html)一章。
|
||||
// #enddocregion summary
|
||||
|
||||
// #docregion appendix-explicit-injector-1
|
||||
|
@ -1327,7 +1326,7 @@ p.
|
|||
The component then asks the injected injector for the services it wants.
|
||||
|
||||
在这个例子中,Angular把组件自身的`Injector`注入到了组件的构造函数中。
|
||||
然后组件像注入进来的这个注入器请求它所需的服务。
|
||||
然后组件向注入进来的这个注入器请求它所需的服务。
|
||||
|
||||
Note that the services themselves are not injected into the component.
|
||||
They are retrieved by calling `injector.get`.
|
||||
|
@ -1340,14 +1339,14 @@ p.
|
|||
to retrieve a service (`ROUS`) that isn't registered with this or any ancestor injector.
|
||||
|
||||
`get`方法如果解析不出所请求的服务,它就会抛出一个异常。
|
||||
我们还可以带上第二个参数(如果服务没找到,就返回它)调用`get`,
|
||||
在该例子中,我们获取一个服务(`ROUS`),它没有在这个注入器或它的任何祖先中注册过。
|
||||
我们还可以带上第二个参数(如果服务没找到,就把它作为默认值返回)调用`get`,
|
||||
在该例子中,我们获取了一个服务(`ROUS`),它没有在这个注入器或它的任何祖先中注册过。
|
||||
.l-sub-section
|
||||
:marked
|
||||
The technique we just described is an example of the
|
||||
[service locator pattern](https://en.wikipedia.org/wiki/Service_locator_pattern).
|
||||
|
||||
我们只通过一个范例描述了这项技术:[服务定位器模式](https://en.wikipedia.org/wiki/Service_locator_pattern)。
|
||||
我们只通过一个范例描述过这项技术:[服务定位器模式](https://en.wikipedia.org/wiki/Service_locator_pattern)。
|
||||
|
||||
We **avoid** this technique unless we genuinely need it.
|
||||
It encourages a careless grab-bag approach such as we see here.
|
||||
|
@ -1366,7 +1365,7 @@ p.
|
|||
Framework developers may take this approach when they
|
||||
must acquire services generically and dynamically.
|
||||
|
||||
框架开发人员可能需要此方法 —— 当他们不得不以通用和动态的方式获取服务时。
|
||||
框架的开发人员可能会需要此方法 —— 当他们不得不以通用和动态的方式获取服务时。
|
||||
// #enddocregion appendix-explicit-injector-2
|
||||
|
||||
// TypeScript only? Unnecessary for Dart
|
||||
|
@ -1388,8 +1387,8 @@ p.
|
|||
If we define the component before the service,
|
||||
we'll get a runtime null reference error.
|
||||
|
||||
如果我们蔑视这个建议,并且 —— 比如说 —— 把`HeroService`和`HeroesComponent`组合在同一个文件里,**就得把组件定义在后面!**
|
||||
如果我们把组件定义在服务的前面,我们就会在运行时获得一个空指针错。
|
||||
如果我们蔑视这个建议,并且 —— 比如说 —— 把`HeroService`和`HeroesComponent`组合在同一个文件里,**就得把组件定义放在后面!**
|
||||
如果我们把组件定义在了服务的前面,就会在运行时获得一个空指针错误。
|
||||
|
||||
.l-sub-section
|
||||
:marked
|
||||
|
|
Loading…
Reference in New Issue