` 标签上的效果:
{@a providers}
## Define dependencies with providers
## 使用提供商来定义依赖
This section demonstrates how to write providers that deliver dependent services.
本节会示范如何编写提供商来交付被依赖的服务。
In order to get a service from a dependency injector, you have to give it a [token](guide/glossary#token).
Angular usually handles this transaction by specifying a constructor parameter and its type.
The parameter type serves as the injector lookup token.
Angular passes this token to the injector and assigns the result to the parameter.
为了从依赖注入器中获取服务,你必须传给它一个[令牌](guide/glossary#token)。
Angular 通常会通过指定构造函数参数以及参数的类型来处理它。
参数的类型可以用作注入器的查阅令牌。
Angular 会把该令牌传给注入器,并把它的结果赋给相应的参数。
The following is a typical example.
下面是一个典型的例子。
Angular asks the injector for the service associated with `LoggerService`
and assigns the returned value to the `logger` parameter.
Angular 会要求注入器提供与 `LoggerService` 相关的服务,并把返回的值赋给 `logger` 参数。
If the injector has already cached an instance of the service associated with the token,
it provides that instance.
If it doesn't, it needs to make one using the provider associated with the token.
如果注入器已经缓存了与该令牌相关的服务实例,那么它就会直接提供此实例。
如果它没有,它就要使用与该令牌相关的提供商来创建一个。
If the injector doesn't have a provider for a requested token, it delegates the request
to its parent injector, where the process repeats until there are no more injectors.
If the search fails, the injector throws an error—unless the request was [optional](guide/dependency-injection-in-action#optional).
如果注入器无法根据令牌在自己内部找到对应的提供商,它便将请求移交给它的父级注入器,这个过程不断重复,直到没有更多注入器为止。
如果没找到,注入器就抛出一个错误...除非这个请求是[可选的](guide/dependency-injection-in-action#optional)。
A new injector has no providers.
Angular initializes the injectors it creates with a set of preferred providers.
You have to configure providers for your own app-specific dependencies.
新的注入器没有提供商。
Angular 会使用一组首选提供商来初始化它本身的注入器。
你必须为自己应用程序特有的依赖项来配置提供商。
{@a defining-providers}
### Defining providers
### 定义提供商
A dependency can't always be created by the default method of instantiating a class.
You learned about some other methods in [Dependency Providers](guide/dependency-injection-providers).
The following `HeroOfTheMonthComponent` example demonstrates many of the alternatives and why you need them.
It's visually simple: a few properties and the logs produced by a logger.
用于实例化类的默认方法不一定总适合用来创建依赖。你可以到[依赖提供商](guide/dependency-injection-providers)部分查看其它方法。
`HeroOfTheMonthComponent` 例子示范了一些替代方案,展示了为什么需要它们。
它看起来很简单:一些属性和一些由 logger 生成的日志。
The code behind it customizes how and where the DI framework provides dependencies.
The use cases illustrate different ways to use the [*provide* object literal](guide/dependency-injection-providers#provide) to associate a definition object with a DI token.
它背后的代码定制了 DI 框架提供依赖项的方法和位置。
这个例子阐明了通过[*提供*对象字面量](guide/dependency-injection-providers#provide)来把对象的定义和 DI 令牌关联起来的另一种方式。
The `providers` array shows how you might use the different provider-definition keys;
`useValue`, `useClass`, `useExisting`, or `useFactory`.
`providers` 数组展示了你可以如何使用其它的键来定义提供商:`useValue`、`useClass`、`useExisting` 或 `useFactory`。
{@a usevalue}
#### Value providers: `useValue`
#### 值提供商:`useValue`
The `useValue` key lets you associate a fixed value with a DI token.
Use this technique to provide *runtime configuration constants* such as website base addresses and feature flags.
You can also use a value provider in a unit test to provide mock data in place of a production data service.
`useValue` 键让你可以为 DI 令牌关联一个固定的值。
使用该技巧来进行*运行期常量设置*,比如网站的基础地址和功能标志等。
你也可以在单元测试中使用*值提供商*,来用一个 Mock 数据来代替一个生产环境下的数据服务。
The `HeroOfTheMonthComponent` example has two value providers.
`HeroOfTheMonthComponent` 例子中有两个*值-提供商*。
* The first provides an existing instance of the `Hero` class to use for the `Hero` token, rather than
requiring the injector to create a new instance with `new` or use its own cached instance.
Here, the token is the class itself.
第一处提供了用于 `Hero` 令牌的 `Hero` 类的现有实例,而不是要求注入器使用 `new` 来创建一个新实例或使用它自己的缓存实例。这里令牌就是这个类本身。
* The second specifies a literal string resource to use for the `TITLE` token.
The `TITLE` provider token is *not* a class, but is instead a
special kind of provider lookup key called an [injection token](guide/dependency-injection-in-action#injection-token), represented by
an `InjectionToken` instance.
第二处为 `TITLE` 令牌指定了一个字符串字面量资源。
`TITLE` 提供商的令牌*不是一个类*,而是一个特别的提供商查询键,名叫[InjectionToken](guide/dependency-injection-in-action#injection-token),表示一个 `InjectionToken` 实例。
You can use an injection token for any kind of provider but it's particularly
helpful when the dependency is a simple value like a string, a number, or a function.
你可以把 `InjectionToken` 用作任何类型的提供商的令牌,但是当依赖是简单类型(比如字符串、数字、函数)时,它会特别有用。
The value of a *value provider* must be defined before you specify it here.
The title string literal is immediately available.
The `someHero` variable in this example was set earlier in the file as shown below.
You can't use a variable whose value will be defined later.
一个*值-提供商*的值必须在指定之前定义。
比如标题字符串就是立即可用的。
该例中的 `someHero` 变量是以前在如下的文件中定义的。
你不能使用那些要等以后才能定义其值的变量。
Other types of providers can create their values *lazily*; that is, when they're needed for injection.
其它类型的提供商都会*惰性创建*它们的值,也就是说只在需要注入它们的时候才创建。
{@a useclass}
#### Class providers: `useClass`
#### 类提供商:`useClass`
The `useClass` provider key lets you create and return a new instance of the specified class.
`useClass` 提供的键让你可以创建并返回指定类的新实例。
You can use this type of provider to substitute an *alternative implementation*
for a common or default class.
The alternative implementation could, for example, implement a different strategy,
extend the default class, or emulate the behavior of the real class in a test case.
你可以使用这类提供商来为公共类或默认类换上一个*替代实现*。比如,这个替代实现可以实现一种不同的策略来扩展默认类,或在测试环境中模拟真实类的行为。
The following code shows two examples in `HeroOfTheMonthComponent`.
请看下面 `HeroOfTheMonthComponent` 里的两个例子:
The first provider is the *de-sugared*, expanded form of the most typical case in which the
class to be created (`HeroService`) is also the provider's dependency injection token.
The short form is generally preferred; this long form makes the details explicit.
第一个提供商是*展开了语法糖的*,是一个典型情况的展开。一般来说,被新建的类(`HeroService`)同时也是该提供商的注入令牌。
通常都选用缩写形式,完整形式可以让细节更明确。
The second provider substitutes `DateLoggerService` for `LoggerService`.
`LoggerService` is already registered at the `AppComponent` level.
When this child component requests `LoggerService`, it receives a `DateLoggerService` instance instead.
第二个提供商使用 `DateLoggerService` 来满足 `LoggerService`。该 `LoggerService` 在 `AppComponent` 级别已经被注册。当*这个组件*要求 `LoggerService` 的时候,它得到的却是 `DateLoggerService` 服务的实例。
This component and its tree of child components receive `DateLoggerService` instance.
Components outside the tree continue to receive the original `LoggerService` instance.
这个组件及其子组件会得到 `DateLoggerService` 实例。这个组件树之外的组件得到的仍是 `LoggerService` 实例。
`DateLoggerService` inherits from `LoggerService`; it appends the current date/time to each message:
`DateLoggerService` 从 `LoggerService` 继承;它把当前的日期/时间附加到每条信息上。
{@a useexisting}
#### Alias providers: `useExisting`
#### 别名提供商:`useExisting`
The `useExisting` provider key lets you map one token to another.
In effect, the first token is an *alias* for the service associated with the second token,
creating two ways to access the same service object.
`useExisting` 提供了一个键,让你可以把一个令牌映射成另一个令牌。实际上,第一个令牌就是第二个令牌所关联的服务的*别名*,这样就创建了访问同一个服务对象的两种途径。
You can use this technique to narrow an API through an aliasing interface.
The following example shows an alias introduced for that purpose.
你可以使用别名接口来窄化 API。下面的例子中使用别名就是为了这个目的。
Imagine that `LoggerService` had a large API, much larger than the actual three methods and a property.
You might want to shrink that API surface to just the members you actually need.
In this example, the `MinimalLogger` [class-interface](#class-interface) reduces the API to two members:
想象 `LoggerService` 有个很大的 API 接口,远超过现有的三个方法和一个属性。你可能希望把 API 接口收窄到只有两个你确实需要的成员。在这个例子中,`MinimalLogger`[*类-接口*](guide/dependency-injection-in-action#class-interface),就这个 API 成功缩小到了只有两个成员:
The following example puts `MinimalLogger` to use in a simplified version of `HeroOfTheMonthComponent`.
下面的例子在一个简化版的 `HeroOfTheMonthComponent` 中使用 `MinimalLogger`。
The `HeroOfTheMonthComponent` constructor's `logger` parameter is typed as `MinimalLogger`, so only the `logs` and `logInfo` members are visible in a TypeScript-aware editor.
`HeroOfTheMonthComponent` 构造函数的 `logger` 参数是一个 `MinimalLogger` 类型,在支持 TypeScript 感知的编辑器里,只能看到它的两个成员 `logs` 和 `logInfo`:
Behind the scenes, Angular sets the `logger` parameter to the full service registered under the `LoggingService` token, which happens to be the `DateLoggerService` instance that was [provided above](guide/dependency-injection-in-action#useclass).
实际上,Angular 把 `logger` 参数设置为注入器里 `LoggerService` 令牌下注册的完整服务,该令牌恰好是[以前提供的那个](guide/dependency-injection-in-action#useclass) `DateLoggerService` 实例。
This is illustrated in the following image, which displays the logging date.
在下面的图片中,显示了日志日期,可以确认这一点:
{@a usefactory}
#### Factory providers: `useFactory`
#### 工厂提供商:`useFactory`
The `useFactory` provider key lets you create a dependency object by calling a factory function,
as in the following example.
`useFactory` 提供了一个键,让你可以通过调用一个工厂函数来创建依赖实例,如下面的例子所示。
The injector provides the dependency value by invoking a factory function,
that you provide as the value of the `useFactory` key.
Notice that this form of provider has a third key, `deps`, which specifies
dependencies for the `useFactory` function.
注入器通过调用你用 `useFactory` 键指定的工厂函数来提供该依赖的值。
注意,提供商的这种形态还有第三个键 `deps`,它指定了供 `useFactory` 函数使用的那些依赖。
Use this technique to create a dependency object with a factory function
whose inputs are a combination of *injected services* and *local state*.
使用这项技术,可以用包含了一些***依赖服务和本地状态***输入的工厂函数来***建立一个依赖对象***。
The dependency object (returned by the factory function) is typically a class instance,
but can be other things as well.
In this example, the dependency object is a string of the names of the runners up
to the "Hero of the Month" contest.
这个依赖对象(由工厂函数返回的)通常是一个类实例,不过也可以是任何其它东西。
在这个例子中,依赖对象是一个表示 "月度英雄" 参赛者名称的字符串。
In the example, the local state is the number `2`, the number of runners up that the component should show.
The state value is passed as an argument to `runnersUpFactory()`.
The `runnersUpFactory()` returns the *provider factory function*, which can use both
the passed-in state value and the injected services `Hero` and `HeroService`.
在这个例子中,局部状态是数字 `2`,也就是组件应该显示的参赛者数量。
该状态的值传给了 `runnersUpFactory()` 作为参数。
`runnersUpFactory()` 返回了*提供商的工厂函数*,它可以使用传入的状态值和注入的服务 `Hero` 和 `HeroService`。
The provider factory function (returned by `runnersUpFactory()`) returns the actual dependency object,
the string of names.
由 `runnersUpFactory()` 返回的提供商的工厂函数返回了实际的依赖对象,也就是表示名字的字符串。
* The function takes a winning `Hero` and a `HeroService` as arguments.
这个返回的函数需要一个 `Hero` 和一个 `HeroService` 参数。
Angular supplies these arguments from injected values identified by
the two *tokens* in the `deps` array.
Angular 根据 `deps` 数组中指定的两个*令牌*来提供这些注入参数。
* The function returns the string of names, which Angular than injects into
the `runnersUp` parameter of `HeroOfTheMonthComponent`.
该函数返回名字的字符串,Angular 可以把它们注入到 `HeroOfTheMonthComponent` 的 `runnersUp` 参数中。
The function retrieves candidate heroes from the `HeroService`,
takes `2` of them to be the runners-up, and returns their concatenated names.
Look at the
for the full source code.
该函数从 `HeroService` 中接受候选的英雄,从中取 `2` 个参加竞赛,并把他们的名字串接起来返回。
参见 查看完整源码。
{@a tokens}
## Provider token alternatives: class interface and 'InjectionToken'
## 提供替代令牌:类接口与 'InjectionToken'
Angular dependency injection is easiest when the provider token is a class
that is also the type of the returned dependency object, or service.
当使用类作为令牌,同时也把它作为返回依赖对象或服务的类型时,Angular 依赖注入使用起来最容易。
However, a token doesn't have to be a class and even when it is a class,
it doesn't have to be the same type as the returned object.
That's the subject of the next section.
但令牌不一定都是类,就算它是一个类,它也不一定都返回类型相同的对象。这是下一节的主题。
{@a class-interface}
### Classinterface
### 类-接口
The previous *Hero of the Month* example used the `MinimalLogger` class
as the token for a provider of `LoggerService`.
前面的*月度英雄*的例子使用了 `MinimalLogger` 类作为 `LoggerService` 提供商的令牌。
`MinimalLogger` is an abstract class.
该 `MinimalLogger` 是一个抽象类。
An abstract class is usually a base class that you can extend.
In this app, however there is no class that inherits from `MinimalLogger`.
你通常从一个可扩展的抽象类继承。但这个应用中*并没有*类会继承 `MinimalLogger`。
The `LoggerService` and the `DateLoggerService` could have inherited from `MinimalLogger`,
or they could have implemented it instead, in the manner of an interface.
But they did neither.
`MinimalLogger` is used only as a dependency injection token.
`LoggerService` 和 `DateLoggerService`*本可以*从 `MinimalLogger` 中继承。
它们也可以实现 `MinimalLogger`,而不用单独定义接口。
但它们没有。
`MinimalLogger` 在这里仅仅被用作一个 "依赖注入令牌"。
When you use a class this way, it's called a *class interface*.
当你通过这种方式使用类时,它称作*类接口*。
As mentioned in [DI Providers](guide/dependency-injection-providers#interface-not-valid-token), an interface is not a valid DI token because it is a TypeScript artifact that doesn't exist at run time. Use this abstract class interface to get the strong typing of an interface, and also use it as a provider token in the way you would a normal class.
就像 [DI 提供商](guide/dependency-injection-providers#interface-not-valid-token)中提到的那样,接口不是有效的 DI 令牌,因为它是 TypeScript 自己用的,在运行期间不存在。使用这种抽象类接口不但可以获得像接口一样的强类型,而且可以像普通类一样把它用作提供商令牌。
A class interface should define *only* the members that its consumers are allowed to call.
Such a narrowing interface helps decouple the concrete class from its consumers.
类接口应该*只*定义允许它的消费者调用的成员。窄的接口有助于解耦该类的具体实现和它的消费者。
Using a class as an interface gives you the characteristics of an interface in a real JavaScript object.
To minimize memory cost, however, the class should have *no implementation*.
The `MinimalLogger` transpiles to this unoptimized, pre-minified JavaScript for a constructor function.
用类作为接口可以让你获得真实 JavaScript 对象中的接口的特性。
但是,为了最小化内存开销,该类应该是*没有实现*的。
对于构造函数,`MinimalLogger` 会转译成未优化过的、预先最小化过的 JavaScript。
Notice that it doesn't have any members. It never grows no matter how many members you add to the class,
as long as those members are typed but not implemented. Look again at the TypeScript `MinimalLogger` class to confirm that it has no implementation.
注意,***只要不实现它***,不管添加多少成员,它都不会增长大小,因为这些成员虽然是有类型的,但却没有实现。
你可以再看看 TypeScript 的 `MinimalLogger` 类,确定一下它是没有实现的。
{@a injection-token}
### 'InjectionToken' objects
### 'InjectionToken' 对象
Dependency objects can be simple values like dates, numbers and strings, or
shapeless objects like arrays and functions.
依赖对象可以是一个简单的值,比如日期,数字和字符串,或者一个无形的对象,比如数组和函数。
Such objects don't have application interfaces and therefore aren't well represented by a class.
They're better represented by a token that is both unique and symbolic,
a JavaScript object that has a friendly name but won't conflict with
another token that happens to have the same name.
这样的对象没有应用程序接口,所以不能用一个类来表示。更适合表示它们的是:唯一的和符号性的令牌,一个 JavaScript 对象,拥有一个友好的名字,但不会与其它的同名令牌发生冲突。
`InjectionToken` has these characteristics.
You encountered them twice in the *Hero of the Month* example,
in the *title* value provider and in the *runnersUp* factory provider.
`InjectionToken` 具有这些特征。在*Hero of the Month*例子中遇见它们两次,一个是 *title* 的值,一个是 *runnersUp* 工厂提供商。
You created the `TITLE` token like this:
这样创建 `TITLE` 令牌:
The type parameter, while optional, conveys the dependency's type to developers and tooling.
The token description is another developer aid.
类型参数,虽然是可选的,但可以向开发者和开发工具传达类型信息。
而且这个令牌的描述信息也可以为开发者提供帮助。
{@a di-inheritance}
## Inject into a derived class
## 注入到派生类
Take care when writing a component that inherits from another component.
If the base component has injected dependencies,
you must re-provide and re-inject them in the derived class
and then pass them down to the base class through the constructor.
当编写一个继承自另一个组件的组件时,要格外小心。如果基础组件有依赖注入,必须要在派生类中重新提供和重新注入它们,并将它们通过构造函数传给基类。
In this contrived example, `SortedHeroesComponent` inherits from `HeroesBaseComponent`
to display a *sorted* list of heroes.
在这个刻意生成的例子里,`SortedHeroesComponent` 继承自 `HeroesBaseComponent`,显示一个*被排序*的英雄列表。
The `HeroesBaseComponent` can stand on its own.
It demands its own instance of `HeroService` to get heroes
and displays them in the order they arrive from the database.
`HeroesBaseComponent` 能自己独立运行。它在自己的实例里要求 `HeroService`,用来得到英雄,并将他们按照数据库返回的顺序显示出来。
### Keep constructors simple
### 让构造函数保持简单
Constructors should do little more than initialize variables.
This rule makes the component safe to construct under test without fear that it will do something dramatic like talk to the server.
That's why you call the `HeroService` from within the `ngOnInit` rather than the constructor.
构造函数应该只用来初始化变量。
这条规则让组件在测试环境中可以放心地构造组件,以免在构造它们时,无意中做出一些非常戏剧化的动作(比如与服务器进行会话)。
这就是为什么你要在 `ngOnInit` 里面调用 `HeroService`,而不是在构造函数中。
Users want to see the heroes in alphabetical order.
Rather than modify the original component, sub-class it and create a
`SortedHeroesComponent` that sorts the heroes before presenting them.
The `SortedHeroesComponent` lets the base class fetch the heroes.
用户希望看到英雄按字母顺序排序。与其修改原始的组件,不如派生它,新建 `SortedHeroesComponent`,以便展示英雄之前进行排序。
`SortedHeroesComponent` 让基类来获取英雄。
Unfortunately, Angular cannot inject the `HeroService` directly into the base class.
You must provide the `HeroService` again for *this* component,
then pass it down to the base class inside the constructor.
可惜,Angular 不能直接在基类里直接注入 `HeroService`。必须在*这个*组件里再次提供 `HeroService`,然后通过构造函数传给基类。
Now take note of the `afterGetHeroes()` method.
Your first instinct might have been to create an `ngOnInit` method in `SortedHeroesComponent` and do the sorting there.
But Angular calls the *derived* class's `ngOnInit` *before* calling the base class's `ngOnInit`
so you'd be sorting the heroes array *before they arrived*. That produces a nasty error.
现在,请注意 `afterGetHeroes()` 方法。
你的第一反应是在 `SortedHeroesComponent` 组件里面建一个 `ngOnInit` 方法来做排序。但是 Angular 会先调用*派生*类的 `ngOnInit`,后调用基类的 `ngOnInit`,
所以可能在*英雄到达之前*就开始排序。这就产生了一个讨厌的错误。
Overriding the base class's `afterGetHeroes()` method solves the problem.
覆盖基类的 `afterGetHeroes()` 方法可以解决这个问题。
These complications argue for *avoiding component inheritance*.
分析上面的这些复杂性是为了强调*避免使用组件继承*这一点。
{@a forwardref}
## Break circularities with a forward class reference (*forwardRef*)
## 使用一个前向引用(*forwardRef*)来打破循环
The order of class declaration matters in TypeScript.
You can't refer directly to a class until it's been defined.
在 TypeScript 里面,类声明的顺序是很重要的。如果一个类尚未定义,就不能引用它。
This isn't usually a problem, especially if you adhere to the recommended *one class per file* rule.
But sometimes circular references are unavoidable.
You're in a bind when class 'A' refers to class 'B' and 'B' refers to 'A'.
One of them has to be defined first.
这通常不是一个问题,特别是当你遵循*一个文件一个类*规则的时候。
但是有时候循环引用可能不能避免。当一个类*A 引用类 B*,同时'B'引用'A'的时候,你就陷入困境了:它们中间的某一个必须要先定义。
The Angular `forwardRef()` function creates an *indirect* reference that Angular can resolve later.
Angular 的 `forwardRef()` 函数建立一个*间接地*引用,Angular 可以随后解析。
The *Parent Finder* sample is full of circular class references that are impossible to break.
这个关于*父查找器*的例子中全都是没办法打破的循环类引用。
You face this dilemma when a class makes *a reference to itself*
as does `AlexComponent` in its `providers` array.
The `providers` array is a property of the `@Component()` decorator function which must
appear *above* the class definition.
当一个类*需要引用自身*的时候,你面临同样的困境,就像在 `AlexComponent` 的 `provdiers` 数组中遇到的困境一样。
该 `providers` 数组是一个 `@Component()` 装饰器函数的一个属性,它必须在类定义*之前*出现。
Break the circularity with `forwardRef`.
使用 `forwardRef` 来打破这种循环: