2016-04-28 13:54:28 -04:00
|
|
|
|
block includes
|
|
|
|
|
include ../_util-fns
|
2016-04-23 15:44:09 -04:00
|
|
|
|
|
2015-11-10 13:31:46 -05:00
|
|
|
|
:marked
|
2017-01-13 16:21:22 -05:00
|
|
|
|
You learned the basics of Angular Dependency injection in the
|
|
|
|
|
[Dependency Injection](./dependency-injection.html) guide.
|
2016-05-21 08:48:55 -04:00
|
|
|
|
|
2016-12-07 04:33:27 -05:00
|
|
|
|
在[依赖注入](./dependency-injection.html)一章中,我们已经学过了 Angular 依赖注入的基础知识。
|
2015-11-10 13:31:46 -05:00
|
|
|
|
|
2017-01-13 16:21:22 -05:00
|
|
|
|
Angular has a _Hierarchical Dependency Injection_ system.
|
|
|
|
|
There is actually a tree of injectors that parallel an application's component tree.
|
|
|
|
|
You can reconfigure the injectors at any level of that component tree.
|
2016-05-21 08:48:55 -04:00
|
|
|
|
|
2017-02-26 02:04:21 -05:00
|
|
|
|
Angular 有一个*多级依赖注入系统*。
|
2016-12-07 04:33:27 -05:00
|
|
|
|
实际上,应用程序中有一个与组件树平行的注入器树(译注:平行是指结构完全相同且一一对应)。
|
2016-06-02 18:04:09 -04:00
|
|
|
|
我们可以在组件树中的任何级别上重新配置注入器,达到一些有趣和有用的效果。
|
2016-01-14 05:47:38 -05:00
|
|
|
|
|
2017-01-13 16:21:22 -05:00
|
|
|
|
This guide explores this system and how to use it to your advantage.
|
2016-05-21 08:48:55 -04:00
|
|
|
|
|
2017-02-26 02:04:21 -05:00
|
|
|
|
在本章中,我们将浏览这个体系,并告诉你如何善用它。
|
2016-05-20 19:18:58 -04:00
|
|
|
|
|
2016-07-03 20:11:17 -04:00
|
|
|
|
Try the <live-example></live-example>.
|
2016-07-06 20:15:51 -04:00
|
|
|
|
|
2016-09-19 05:03:53 -04:00
|
|
|
|
试试<live-example>在线例子</live-example>.
|
2015-10-17 19:40:10 -04:00
|
|
|
|
|
2015-11-10 13:31:46 -05:00
|
|
|
|
.l-main-section
|
|
|
|
|
:marked
|
2017-01-13 16:21:22 -05:00
|
|
|
|
## The injector tree
|
2016-12-07 04:33:27 -05:00
|
|
|
|
|
2016-05-21 08:48:55 -04:00
|
|
|
|
## 注入器树
|
2015-11-10 13:31:46 -05:00
|
|
|
|
|
2017-01-13 16:21:22 -05:00
|
|
|
|
In the [Dependency Injection](./dependency-injection.html) guide,
|
|
|
|
|
you learned how to configure a dependency injector and how to retrieve dependencies where you need them.
|
2016-05-21 08:48:55 -04:00
|
|
|
|
|
|
|
|
|
在[依赖注入](./dependency-injection.html)一章中,我们学过如何配置依赖注入器,以及如何在我们需要时用它获取依赖。
|
2015-11-10 13:31:46 -05:00
|
|
|
|
|
2017-01-13 16:21:22 -05:00
|
|
|
|
In fact, there is no such thing as *the* injector.
|
|
|
|
|
An application may have multiple injectors.
|
2015-12-11 16:18:37 -05:00
|
|
|
|
An Angular application is a tree of components. Each component instance has its own injector!
|
|
|
|
|
The tree of components parallels the tree of injectors.
|
2016-05-21 08:48:55 -04:00
|
|
|
|
|
2017-02-26 02:04:21 -05:00
|
|
|
|
实际上,没有***那个(唯一的)***注入器这回事,一个应用中可能有多个注入器。
|
2016-12-07 04:33:27 -05:00
|
|
|
|
一个 Angular 应用是一个组件树。每个组件实例都有自己的注入器!
|
2016-05-21 08:48:55 -04:00
|
|
|
|
组件的树与注入器的树平行。
|
2016-01-14 05:47:38 -05:00
|
|
|
|
|
2015-11-10 13:31:46 -05:00
|
|
|
|
:marked
|
2017-02-26 02:04:21 -05:00
|
|
|
|
Consider this guide's variation on the Tour of Heroes application .
|
|
|
|
|
At the top is the `AppComponent` which has some sub- components.
|
|
|
|
|
One of them is the`HeroesListComponent`.
|
|
|
|
|
The `HeroesListComponent` holds and manages multiple instances of the `HeroTaxReturnComponent`.
|
2017-01-13 16:21:22 -05:00
|
|
|
|
The following diagram represents the state of the this guide's three-level component tree when there are three instances of `HeroTaxReturnComponent`
|
2015-10-17 19:40:10 -04:00
|
|
|
|
open simultaneously.
|
2016-05-21 08:48:55 -04:00
|
|
|
|
|
2017-02-26 02:04:21 -05:00
|
|
|
|
考虑《英雄指南》应用的一个简单变种。它的顶层是`AppComponent`组件,它有一些子组件。
|
|
|
|
|
`HeroesListComponent`组件保存和管理着`HeroTaxReturnComponent`的多个实例。
|
|
|
|
|
下图展示了当`HeroesCardComponent`的三个 `HeroTaxReturnComponent` 实例同时展开时的三级组件树状态。
|
2015-11-10 13:31:46 -05:00
|
|
|
|
|
2015-10-17 19:40:10 -04:00
|
|
|
|
figure.image-display
|
2017-01-13 16:21:22 -05:00
|
|
|
|
img(src="/resources/images/devguide/dependency-injection/component-hierarchy.png" alt="injector tree" width="600")
|
2015-11-10 13:31:46 -05:00
|
|
|
|
|
|
|
|
|
:marked
|
2017-01-13 16:21:22 -05:00
|
|
|
|
Angular doesn't actually _create_ a separate injector for each component.
|
|
|
|
|
Every component doesn't need its own injector and it would be horribly inefficient to create
|
|
|
|
|
masses of injectors for no good purpose.
|
2016-05-21 08:48:55 -04:00
|
|
|
|
|
2017-03-03 04:23:54 -05:00
|
|
|
|
Angular实际上并没有为每个组件*创建*一个独立的注入器。
|
2017-02-26 02:04:21 -05:00
|
|
|
|
每个组件并不需要自己的注入器,那样做会非常低效,而且没有好处。
|
2017-01-13 16:21:22 -05:00
|
|
|
|
|
|
|
|
|
But every component _has an injector_, even if it shares that injector with another component or with the injector of the root `AppModule`.
|
|
|
|
|
And there _may_ be multiple injector instances operating at different levels of the component tree
|
|
|
|
|
depending upon how the developer registers providers, which is the subject of this guide.
|
2017-03-03 04:23:54 -05:00
|
|
|
|
|
|
|
|
|
但是每个组件都*有*一个注入器,它可能共享其它组件的注入器或使用根模块`AppModule`的注入器。
|
|
|
|
|
而且在组件树的不同层次上还*可能有*多个注入器实例,这取决于开发人员如何注册这些提供商。这正是本章的主题。
|
2015-11-10 13:31:46 -05:00
|
|
|
|
|
2017-01-13 16:21:22 -05:00
|
|
|
|
### Injector bubbling
|
2017-03-03 04:23:54 -05:00
|
|
|
|
|
|
|
|
|
### 注入器"冒泡"
|
2015-11-10 13:31:46 -05:00
|
|
|
|
|
2017-01-13 16:21:22 -05:00
|
|
|
|
When a component requests a dependency, Angular tries to satisfy that dependency with a provider registered in that component's own injector.
|
2015-11-10 13:31:46 -05:00
|
|
|
|
If the component's injector lacks the provider, it passes the request up to its parent component's injector.
|
2017-01-13 16:21:22 -05:00
|
|
|
|
If that injector can't satisfy the request, it passes it along to *its* parent injector.
|
|
|
|
|
The requests keep bubbling up until Angular finds an injector that can handle the request or runs out of ancestor injectors.
|
|
|
|
|
If it runs out of ancestors, Angular throws an error.
|
2016-05-21 08:48:55 -04:00
|
|
|
|
|
2017-02-26 02:04:21 -05:00
|
|
|
|
当一个组件申请获得一个依赖时,Angular 先尝试用该组件自己的注入器来满足它。
|
2016-07-06 20:22:36 -04:00
|
|
|
|
如果该组件的注入器没有找到对应的提供商,它就把这个申请转给它父组件的注入器来处理。
|
2016-05-21 08:48:55 -04:00
|
|
|
|
如果那个注入器也无法满足这个申请,它就继续转给*它的*父组件的注入器。
|
2016-06-02 18:04:09 -04:00
|
|
|
|
这个申请继续往上冒泡 —— 直到我们找到了一个能处理此申请的注入器或者超出了组件树中的祖先位置为止。
|
2016-12-07 04:33:27 -05:00
|
|
|
|
如果超出了组件树中的祖先还未找到,Angular 就会抛出一个错误。
|
2015-11-10 13:31:46 -05:00
|
|
|
|
|
2015-10-17 19:40:10 -04:00
|
|
|
|
.l-sub-section
|
2015-11-10 13:31:46 -05:00
|
|
|
|
:marked
|
2017-01-13 16:21:22 -05:00
|
|
|
|
You can cap the bubbling. An intermediate component can declare that it is the "host" component.
|
|
|
|
|
The hunt for providers will climb no higher than the injector for that host component.
|
2017-02-26 02:04:21 -05:00
|
|
|
|
This a topic for another day.
|
|
|
|
|
|
|
|
|
|
我们还可以“盖住”这次冒泡。一个中层的组件可以声称自己是“宿主”组件。
|
2016-07-06 20:22:36 -04:00
|
|
|
|
向上查找提供商的过程会截止于这个“宿主”组件。
|
2016-05-21 08:48:55 -04:00
|
|
|
|
我们先保留这个问题,等改天再讨论这个选项。
|
2017-02-26 02:04:21 -05:00
|
|
|
|
|
2015-11-10 13:31:46 -05:00
|
|
|
|
:marked
|
2017-01-13 16:21:22 -05:00
|
|
|
|
### Re-providing a service at different levels
|
|
|
|
|
You can re-register a provider for a particular dependency token at multiple levels of the injector tree.
|
|
|
|
|
You don't *have* to re-register providers. You shouldn't do so unless you have a good reason.
|
|
|
|
|
But you *can*.
|
2015-11-10 13:31:46 -05:00
|
|
|
|
|
2017-01-13 16:21:22 -05:00
|
|
|
|
As the resolution logic works upwards, the first provider encountered wins.
|
|
|
|
|
Thus, a provider in an intermediate injector intercepts a request for a service from something lower in the tree.
|
|
|
|
|
It effectively "reconfigures" and "shadows" a provider at a higher level in the tree.
|
2015-11-10 13:31:46 -05:00
|
|
|
|
|
2017-01-13 16:21:22 -05:00
|
|
|
|
If you only specify providers at the top level (typically the root `AppModule`), the tree of injectors appears to be flat.
|
|
|
|
|
All requests bubble up to the root <span if-docs="ts"><code>NgModule</code></span> injector that you configured with the `!{_bootstrapModule}` method.
|
2016-05-22 05:22:52 -04:00
|
|
|
|
|
2017-02-26 02:04:21 -05:00
|
|
|
|
如果我们只在顶级(通常是根模块`AppModule`),这三个注入器看起来将是“平面”的。
|
|
|
|
|
所有的申请都会冒泡到根<span if-docs="ts"><code>NgModule</code></span>进行处理,也就是我们在`!{_bootstrapModule}`方法中配置的那个。
|
2015-11-10 13:31:46 -05:00
|
|
|
|
|
2017-01-13 16:21:22 -05:00
|
|
|
|
.l-main-section
|
|
|
|
|
:marked
|
|
|
|
|
## Component injectors
|
2016-05-22 05:22:52 -04:00
|
|
|
|
|
2017-02-26 02:04:21 -05:00
|
|
|
|
## 组件注入器
|
2015-11-10 13:31:46 -05:00
|
|
|
|
|
2017-01-13 16:21:22 -05:00
|
|
|
|
The ability to configure one or more providers at different levels opens up interesting and useful possibilities.
|
2016-05-22 05:22:52 -04:00
|
|
|
|
|
2017-02-26 02:04:21 -05:00
|
|
|
|
在不同层次上重新配置一个或多个提供商的能力,开启了一些既有趣又有用的可能性。
|
2015-10-17 19:40:10 -04:00
|
|
|
|
|
2015-11-10 13:31:46 -05:00
|
|
|
|
:marked
|
2017-01-13 16:21:22 -05:00
|
|
|
|
### Scenario: service isolation
|
2016-12-07 04:33:27 -05:00
|
|
|
|
|
2017-01-13 16:21:22 -05:00
|
|
|
|
Architectural reasons may lead you to restrict access to a service to the application domain where it belongs.
|
2015-11-10 13:31:46 -05:00
|
|
|
|
|
2017-01-13 16:21:22 -05:00
|
|
|
|
The guide sample includes a `VillainsListComponent` that displays a list of villains.
|
|
|
|
|
It gets those villains from a `VillainsService`.
|
2015-11-10 13:31:46 -05:00
|
|
|
|
|
2017-01-13 16:21:22 -05:00
|
|
|
|
While you could provide `VillainsService` in the root `AppModule` (that's where you'll find the `HeroesService`),
|
|
|
|
|
that would make the `VillainsService` available everywhere in the application, including the _Hero_ workflows.
|
2015-11-10 13:31:46 -05:00
|
|
|
|
|
2017-01-13 16:21:22 -05:00
|
|
|
|
If you later modify the `VillainsService`, you could break something in a hero component somewhere.
|
|
|
|
|
That's not supposed to happen but the way you've provided the service creates that risk.
|
2015-11-10 13:31:46 -05:00
|
|
|
|
|
2017-01-13 16:21:22 -05:00
|
|
|
|
Instead, provide the `VillainsService` in the `providers` metadata of the `VillainsListComponent` like this:
|
2015-11-10 13:31:46 -05:00
|
|
|
|
|
2017-02-02 13:38:17 -05:00
|
|
|
|
+makeExample('hierarchical-dependency-injection/ts/src/app/villains-list.component.ts', 'metadata','src/app/villains-list.component.ts (metadata)')(format='.')
|
2015-11-25 12:01:44 -05:00
|
|
|
|
:marked
|
2017-01-13 16:21:22 -05:00
|
|
|
|
By providing `VillainsService` in the `VillainsListComponent` metadata — and nowhere else —,
|
|
|
|
|
the service becomes available only in the `VillainsListComponent` and its sub-component tree.
|
|
|
|
|
It's still a singleton, but it's a singleton that exist solely in the _villain_ domain.
|
2015-11-10 13:31:46 -05:00
|
|
|
|
|
2017-01-13 16:21:22 -05:00
|
|
|
|
You are confident that a hero component can't access it. You've reduced your exposure to error.
|
2015-11-10 13:31:46 -05:00
|
|
|
|
|
2017-01-13 16:21:22 -05:00
|
|
|
|
### Scenario: multiple edit sessions
|
2015-11-10 13:31:46 -05:00
|
|
|
|
|
2017-01-13 16:21:22 -05:00
|
|
|
|
Many applications allow users to work on several open tasks at the same time.
|
|
|
|
|
For example, in a tax preparation application, the preparer could be working several tax returns,
|
|
|
|
|
switching from one to the other throughout the day.
|
2015-11-25 12:01:44 -05:00
|
|
|
|
|
2017-01-13 16:21:22 -05:00
|
|
|
|
This guide demonstrates that scenario with an example in the Tour of Heroes theme.
|
|
|
|
|
Imagine an outer `HeroListComponent` that displays a list of super heroes.
|
2015-11-10 13:31:46 -05:00
|
|
|
|
|
2017-01-13 16:21:22 -05:00
|
|
|
|
To open a hero's tax return, the preparer clicks on a hero name, which opens a component for editing that return.
|
|
|
|
|
Each selected hero tax return opens in its own component and multiple returns can be open at the same time.
|
2015-11-10 13:31:46 -05:00
|
|
|
|
|
2017-01-13 16:21:22 -05:00
|
|
|
|
Each tax return component
|
|
|
|
|
* is its own tax return editing session.
|
|
|
|
|
* can change a tax return without affecting a return in another component.
|
|
|
|
|
* has the ability to save the changes to its tax return or cancel them.
|
|
|
|
|
|
|
|
|
|
figure.image-display
|
|
|
|
|
img(src="/resources/images/devguide/dependency-injection/hid-heroes-anim.gif" width="400" alt="Heroes in action")
|
2015-11-25 12:01:44 -05:00
|
|
|
|
:marked
|
2017-01-13 16:21:22 -05:00
|
|
|
|
One might suppose that the `TaxReturnComponent` has logic to manage and restore changes.
|
|
|
|
|
That would be a pretty easy task for a simple hero tax return.
|
|
|
|
|
In the real world, with a rich tax return data model, the change management would be tricky.
|
|
|
|
|
You might delegate that management to a helper service, as this example does.
|
2015-11-10 13:31:46 -05:00
|
|
|
|
|
2017-01-13 16:21:22 -05:00
|
|
|
|
Here is the `HeroTaxReturnService`.
|
|
|
|
|
It caches a single `HeroTaxReturn`, tracks changes to that return, and can save or restore it.
|
|
|
|
|
It also delegates to the application-wide, singleton `HeroService`, which it gets by injection.
|
2017-02-02 13:38:17 -05:00
|
|
|
|
+makeExample('hierarchical-dependency-injection/ts/src/app/hero-tax-return.service.ts', '', 'src/app/hero-tax-return.service.ts')
|
2015-11-10 13:31:46 -05:00
|
|
|
|
|
2017-01-13 16:21:22 -05:00
|
|
|
|
:marked
|
|
|
|
|
Here is the `HeroTaxReturnComponent` that makes use of it.
|
2017-02-02 13:38:17 -05:00
|
|
|
|
+makeExample('hierarchical-dependency-injection/ts/src/app/hero-tax-return.component.ts', null, 'src/app/hero-tax-return.component.ts')
|
2015-11-10 13:31:46 -05:00
|
|
|
|
|
2015-11-25 12:01:44 -05:00
|
|
|
|
:marked
|
2017-01-13 16:21:22 -05:00
|
|
|
|
The _tax-return-to-edit_ arrives via the input property which is implemented with getters and setters.
|
|
|
|
|
The setter initializes the component's own instance of the `HeroTaxReturnService` with the incoming return.
|
|
|
|
|
The getter always returns what that service says is the current state of the hero.
|
|
|
|
|
The component also asks the service to save and restore this tax return.
|
2015-11-10 13:31:46 -05:00
|
|
|
|
|
2017-01-13 16:21:22 -05:00
|
|
|
|
There'd be big trouble if _this_ service were an application-wide singleton.
|
|
|
|
|
Every component would share the same service instance.
|
|
|
|
|
Each component would overwrite the tax return that belonged to another hero.
|
|
|
|
|
What a mess!
|
2015-11-10 13:31:46 -05:00
|
|
|
|
|
2017-01-13 16:21:22 -05:00
|
|
|
|
Look closely at the metadata for the `HeroTaxReturnComponent`. Notice the `providers` property.
|
2015-11-10 13:31:46 -05:00
|
|
|
|
|
2017-02-02 13:38:17 -05:00
|
|
|
|
+makeExample('hierarchical-dependency-injection/ts/src/app/hero-tax-return.component.ts', 'providers')
|
2015-11-25 12:01:44 -05:00
|
|
|
|
:marked
|
2017-01-13 16:21:22 -05:00
|
|
|
|
The `HeroTaxReturnComponent` has its own provider of the `HeroTaxReturnService`.
|
|
|
|
|
Recall that every component _instance_ has its own injector.
|
|
|
|
|
Providing the service at the component level ensures that _every_ instance of the component gets its own, private instance of the service.
|
|
|
|
|
No tax return overwriting. No mess.
|
2016-05-22 05:22:52 -04:00
|
|
|
|
|
2017-01-13 16:21:22 -05:00
|
|
|
|
.l-sub-section
|
|
|
|
|
:marked
|
|
|
|
|
The rest of the scenario code relies on other Angular features and techniques that you can learn about elsewhere in the documentation.
|
|
|
|
|
You can review it and download it from the <live-example></live-example>
|
2015-11-10 13:31:46 -05:00
|
|
|
|
|
2017-01-13 16:21:22 -05:00
|
|
|
|
:marked
|
|
|
|
|
### Scenario: specialized providers
|
2015-11-10 13:31:46 -05:00
|
|
|
|
|
2017-01-13 16:21:22 -05:00
|
|
|
|
Another reason to re-provide a service is to substitute a _more specialized_ implementation of that service,
|
|
|
|
|
deeper in the component tree.
|
2015-11-10 13:31:46 -05:00
|
|
|
|
|
2017-01-13 16:21:22 -05:00
|
|
|
|
Consider again the Car example from the [Dependency Injection](./dependency-injection.html) guide.
|
|
|
|
|
Suppose you configured the root injector (marked as A) with _generic_ providers for
|
|
|
|
|
`CarService`, `EngineService` and `TiresService`.
|
2016-08-11 17:43:59 -04:00
|
|
|
|
|
2017-01-13 16:21:22 -05:00
|
|
|
|
You create a car component (A) that displays a car constructed from these three generic services.
|
2016-08-11 17:43:59 -04:00
|
|
|
|
|
2017-01-13 16:21:22 -05:00
|
|
|
|
Then you create a child component (B) that defines its own, _specialized_ providers for `CarService` and `EngineService`
|
|
|
|
|
that have special capabilites suitable for whatever is going on in component (B).
|
2015-11-10 13:31:46 -05:00
|
|
|
|
|
2017-01-13 16:21:22 -05:00
|
|
|
|
Component (B) is the parent of another component (C) that defines its own, even _more specialized_ provider for `CarService`.
|
2015-11-10 13:31:46 -05:00
|
|
|
|
|
2017-01-13 16:21:22 -05:00
|
|
|
|
figure.image-display
|
|
|
|
|
img(src="/resources/images/devguide/dependency-injection/car-components.png" alt="car components" width="220")
|
2015-11-10 13:31:46 -05:00
|
|
|
|
|
2017-01-13 16:21:22 -05:00
|
|
|
|
:marked
|
|
|
|
|
Behind the scenes, each component sets up its own injector with zero, one, or more providers defined for that component itself.
|
2015-12-11 16:18:37 -05:00
|
|
|
|
|
2017-01-13 16:21:22 -05:00
|
|
|
|
When you resolve an instance of `Car` at the deepest component (C),
|
|
|
|
|
its injector produces an instance of `Car` resolved by injector (C) with an `Engine` resolved by injector (B) and
|
|
|
|
|
`Tires` resolved by the root injector (A).
|
2016-01-14 05:47:38 -05:00
|
|
|
|
|
2017-01-13 16:21:22 -05:00
|
|
|
|
figure.image-display
|
|
|
|
|
img(src="/resources/images/devguide/dependency-injection/injector-tree.png" alt="car injector tree" width="600")
|
2015-11-10 13:31:46 -05:00
|
|
|
|
|
2017-01-13 16:21:22 -05:00
|
|
|
|
.l-sub-section
|
|
|
|
|
:marked
|
|
|
|
|
The code for this _cars_ scenario is in the `car.components.ts` and `car.services.ts` files of the sample
|
|
|
|
|
which you can review and download from the <live-example></live-example>
|
|
|
|
|
:marked
|
2016-09-19 23:24:40 -04:00
|
|
|
|
//- ## Advanced Dependency Injection in Angular
|
2016-08-11 17:43:59 -04:00
|
|
|
|
//- Restrict Dependency Lookups
|
|
|
|
|
//- [TODO] (@Host) This has been postponed for now until we come up with a decent use case
|
|
|
|
|
//- .l-main-section
|
|
|
|
|
//- :marked
|
|
|
|
|
//- ## Dependency Visibility
|
|
|
|
|
//- [TODO] (providers vs viewProviders) This has been postponed for now until come up with a decent use case
|