# Conflicts: # aio/content/cli/index.md # aio/content/guide/accessibility.md # aio/content/guide/ajs-quick-reference.md # aio/content/guide/angular-compiler-options.md # aio/content/guide/animations.md # aio/content/guide/aot-compiler.md # aio/content/guide/architecture-components.md # aio/content/guide/architecture-modules.md # aio/content/guide/architecture-next-steps.md # aio/content/guide/architecture-services.md # aio/content/guide/attribute-directives.md # aio/content/guide/bootstrapping.md # aio/content/guide/browser-support.md # aio/content/guide/cli-builder.md # aio/content/guide/comparing-observables.md # aio/content/guide/component-styles.md # aio/content/guide/creating-libraries.md # aio/content/guide/dependency-injection-in-action.md # aio/content/guide/dependency-injection-navtree.md # aio/content/guide/dependency-injection-providers.md # aio/content/guide/dependency-injection.md # aio/content/guide/deployment.md # aio/content/guide/deprecations.md # aio/content/guide/displaying-data.md # aio/content/guide/dynamic-component-loader.md # aio/content/guide/elements.md # aio/content/guide/file-structure.md # aio/content/guide/glossary.md # aio/content/guide/http.md # aio/content/guide/i18n.md # aio/content/guide/ivy-compatibility.md # aio/content/guide/language-service.md # aio/content/guide/lifecycle-hooks.md # aio/content/guide/module-types.md # aio/content/guide/ngmodule-faq.md # aio/content/guide/ngmodule-vs-jsmodule.md # aio/content/guide/ngmodules.md # aio/content/guide/observables-in-angular.md # aio/content/guide/pipes.md # aio/content/guide/providers.md # aio/content/guide/releases.md # aio/content/guide/route-animations.md # aio/content/guide/router-tutorial.md # aio/content/guide/router.md # aio/content/guide/rx-library.md # aio/content/guide/schematics-authoring.md # aio/content/guide/schematics-for-libraries.md # aio/content/guide/service-worker-config.md # aio/content/guide/service-worker-getting-started.md # aio/content/guide/structural-directives.md # aio/content/guide/styleguide.md # aio/content/guide/template-syntax.md # aio/content/guide/template-typecheck.md # aio/content/guide/testing.md # aio/content/guide/typescript-configuration.md # aio/content/guide/upgrade-setup.md # aio/content/guide/user-input.md # aio/content/guide/using-libraries.md # aio/content/guide/web-worker.md # aio/content/guide/workspace-config.md # aio/content/marketing/docs.md # aio/content/marketing/events.html # aio/content/navigation.json # aio/content/start/index.md # aio/content/start/start-data.md # aio/content/start/start-deployment.md # aio/content/tutorial/toh-pt2.md # aio/content/tutorial/toh-pt4.md # aio/package.json # aio/src/app/app.component.ts # aio/src/app/layout/footer/footer.component.html # aio/src/app/shared/custom-icon-registry.ts # aio/src/environments/environment.stable.ts # aio/tools/stackblitz-builder/builder.js # aio/tools/transforms/angular-content-package/index.js # aio/tools/transforms/templates/api/lib/memberHelpers.html # aio/yarn.lock # integration/ng_elements/e2e/app.e2e-spec.ts # integration/ng_elements/src/app.ts # packages/common/src/pipes/date_pipe.ts # packages/core/src/metadata/directives.ts # packages/core/src/metadata/ng_module.ts # packages/core/src/render/api.ts # packages/forms/src/directives/ng_model.ts # packages/forms/src/form_builder.ts # packages/forms/src/model.ts # packages/platform-browser/src/browser.ts # packages/router/src/config.ts # packages/router/src/directives/router_link.ts # packages/router/src/directives/router_link_active.ts # packages/router/src/events.ts # packages/router/src/router.ts # packages/router/src/router_module.ts # packages/router/src/router_state.ts
70 KiB
Hierarchical injectors
多级注入器
Injectors in Angular have rules that you can leverage to achieve the desired visibility of injectables in your apps. By understanding these rules, you can determine in which NgModule, Component or Directive you should declare a provider.
Angular 中的注入器有一些规则,你可以利用这些规则来在应用程序中获得所需的可注入对象可见性。通过了解这些规则,可以确定应在哪个 NgModule、组件或指令中声明服务提供者。
Two injector hierarchies
两个注入器层次结构
There are two injector hierarchies in Angular:
Angular 中有两个注入器层次结构:
-
ModuleInjector
hierarchy—configure aModuleInjector
in this hierarchy using an@NgModule()
or@Injectable()
annotation.ModuleInjector
层次结构 —— 使用@NgModule()
或@Injectable()
注解在此层次结构中配置ModuleInjector
。 -
ElementInjector
hierarchy—created implicitly at each DOM element. AnElementInjector
is empty by default unless you configure it in theproviders
property on@Directive()
or@Component()
.ElementInjector
层次结构 —— 在每个 DOM 元素上隐式创建。除非你在@Directive()
或@Component()
的providers
属性中进行配置,否则默认情况下,ElementInjector
为空。
{@a register-providers-injectable}
ModuleInjector
The ModuleInjector
can be configured in one of two ways:
可以通过以下两种方式之一配置 ModuleInjector
:
-
Using the
@Injectable()
providedIn
property to refer to@NgModule()
, orroot
.使用
@Injectable()
的providedIn
属性引用@NgModule()
或root
。 -
Using the
@NgModule()
providers
array.使用
@NgModule()
的providers
数组。
Tree-shaking and @Injectable()
摇树优化与 @Injectable()
Using the @Injectable()
providedIn
property is preferable
to the @NgModule()
providers
array because with @Injectable()
providedIn
, optimization
tools can perform
tree-shaking, which removes services that your app isn't
using and results in smaller bundle sizes.
使用 @Injectable()
的 providedIn
属性优于 @NgModule()
的 providers
数组,因为使用 @Injectable()
的 providedIn
时,优化工具可以进行摇树优化,从而删除你的应用程序中未使用的服务,以减小捆绑包尺寸。
Tree-shaking is especially useful for a library because the application which uses the library may not have a need to inject it. Read more about tree-shakable providers in Introduction to services and dependency injection.
摇树优化对于库特别有用,因为使用该库的应用程序不需要注入它。在 服务与依赖注入简介了解关于可摇树优化的提供者的更多信息。
ModuleInjector
is configured by the @NgModule.providers
and
NgModule.imports
property. ModuleInjector
is a flattening of
all of the providers arrays which can be reached by following the
NgModule.imports
recursively.
ModuleInjector
由 @NgModule.providers
和 NgModule.imports
属性配置。ModuleInjector
是可以通过 NgModule.imports
递归找到的所有 providers 数组的扁平化。
Child ModuleInjector
s are created when lazy loading other @NgModules
.
子 ModuleInjector
是在惰性加载其它 @NgModules
时创建的。
Provide services with the providedIn
property of @Injectable()
as follows:
使用 @Injectable()
的 providedIn
属性提供服务的方式如下:
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root' // <--provides this service in the root ModuleInjector
})
export class ItemService {
name = 'telephone';
}
The @Injectable()
decorator identifies a service class.
The providedIn
property configures a specific ModuleInjector
,
here root
, which makes the service available in the root
ModuleInjector
.
@Injectable()
装饰器标识服务类。该 providedIn
属性配置指定的 ModuleInjector
,这里的 root
会把让该服务在 root
ModuleInjector
上可用。
Platform injector
平台注入器
There are two more injectors above root
, an
additional ModuleInjector
and NullInjector()
.
在 root
之上还有两个注入器,一个是额外的 ModuleInjector
,一个是 NullInjector()
。
Consider how Angular bootstraps the app with the
following in main.ts
:
思考下 Angular 要如何通过 main.ts
中的如下代码引导应用程序:
platformBrowserDynamic().bootstrapModule(AppModule).then(ref => {...})
The bootstrapModule()
method creates a child injector of
the platform injector which is configured by the AppModule
.
This is the root
ModuleInjector
.
bootstrapModule()
方法会创建一个由 AppModule
配置的注入器作为平台注入器的子注入器。也就是 root
ModuleInjector
。
The platformBrowserDynamic()
method creates an injector
configured by a PlatformModule
, which contains platform-specific
dependencies. This allows multiple apps to share a platform
configuration.
For example, a browser has only one URL bar, no matter how
many apps you have running.
You can configure additional platform-specific providers at the
platform level by supplying extraProviders
using the platformBrowser()
function.
platformBrowserDynamic()
方法创建一个由 PlatformModule
配置的注入器,该注入器包含特定平台的依赖项。这允许多个应用共享同一套平台配置。例如,无论你运行多少个应用程序,浏览器都只有一个 URL 栏。你可以使用 platformBrowser()
函数提供 extraProviders
,从而在平台级别配置特定平台的额外提供者。
The next parent injector in the hierarchy is the NullInjector()
,
which is the top of the tree. If you've gone so far up the tree
that you are looking for a service in the NullInjector()
, you'll
get an error unless you've used @Optional()
because ultimately,
everything ends at the NullInjector()
and it returns an error or,
in the case of @Optional()
, null
. For more information on
@Optional()
, see the @Optional()
section of this guide.
层次结构中的下一个父注入器是 NullInjector()
,它是树的顶部。如果你在树中向上走了很远,以至于要在 NullInjector()
中寻找服务,那么除非使用 @Optional()
,否则将收到错误消息,因为最终所有东西都将以 NullInjector()
结束并返回错误,或者对于 @Optional()
,返回 null
。关于 @Optional()
的更多信息,请参阅本指南的 @Optional()
部分。
The following diagram represents the relationship between the
root
ModuleInjector
and its parent injectors as the
previous paragraphs describe.
下图展示了前面各段落描述的 root
ModuleInjector
及其父注入器之间的关系。
While the name root
is a special alias, other ModuleInjector
s
don't have aliases. You have the option to create ModuleInjector
s
whenever a dynamically loaded component is created, such as with
the Router, which will create child ModuleInjector
s.
虽然 root
是一个特殊的别名,但其它 ModuleInjector
都没有别名。每当创建动态加载组件时,你还会创建 ModuleInjector
,比如路由器,它还会创建子 ModuleInjector
。
All requests forward up to the root injector, whether you configured it
with the bootstrapModule()
method, or registered all providers
with root
in their own services.
无论是使用 bootstrapModule()
的方法配置它,还是将所有提供者都用 root
注册到其自己的服务中,所有请求最终都会转发到 root
注入器。
@Injectable()
vs. @NgModule()
If you configure an app-wide provider in the @NgModule()
of
AppModule
, it overrides one configured for root
in the
@Injectable()
metadata. You can do this to configure a
non-default provider of a service that is shared with multiple apps.
如果你在 AppModule
的 @NgModule()
中配置应用级提供者,它就会覆盖一个在 @Injectable()
的 root
元数据中配置的提供者。你可以用这种方式,来配置供多个应用共享的服务的非默认提供者。
Here is an example of the case where the component router
configuration includes
a non-default location strategy
by listing its provider
in the providers
list of the AppModule
.
下面的例子中,通过把 location 策略 的提供者添加到 AppModule
的 providers
列表中,为路由器配置了非默认的 location 策略。
ElementInjector
Angular creates ElementInjector
s implicitly for each DOM element.
Angular 会为每个 DOM 元素隐式创建 ElementInjector
。
Providing a service in the @Component()
decorator using
its providers
or viewProviders
property configures an ElementInjector
.
For example, the following TestComponent
configures the ElementInjector
by providing the service as follows:
可以用 @Component()
装饰器中的 providers
或 viewProviders
属性来配置 ElementInjector
以提供服务。例如,下面的 TestComponent
通过提供此服务来配置 ElementInjector
:
@Component({
...
providers: [{ provide: ItemService, useValue: { name: 'lamp' } }]
})
export class TestComponent
Note: Please see the
resolution rules
section to understand the relationship between the ModuleInjector
tree and
the ElementInjector
tree.
**注意:**请参阅解析规则部分,以了解 ModuleInjector
树和 ElementInjector
树之间的关系。
When you provide services in a component, that service is available via
the ElementInjector
at that component instance.
It may also be visible at
child component/directives based on visibility rules described in the resolution rules section.
在组件中提供服务时,可以通过 ElementInjector
在该组件实例处使用该服务。根据解析规则部分描述的可见性规则,它也同样在子组件/指令处可见。
When the component instance is destroyed, so is that service instance.
当组件实例被销毁时,该服务实例也将被销毁。
@Directive()
and @Component()
@Directive()
和 @Component()
A component is a special type of directive, which means that
just as @Directive()
has a providers
property, @Component()
does too.
This means that directives as well as components can configure
providers, using the providers
property.
When you configure a provider for a component or directive
using the providers
property,
that provider belongs to the ElementInjector
of that component or
directive.
Components and directives on the same element share an injector.
组件是一种特殊类型的指令,这意味着 @Directive()
具有 providers
属性,@Component()
也同样如此。
这意味着指令和组件都可以使用 providers
属性来配置提供者。当使用 providers
属性为组件或指令配置提供者时,该提供程商就属于该组件或指令的 ElementInjector
。同一元素上的组件和指令共享同一个注入器。
{@a resolution-rules}
Resolution rules
解析规则
When resolving a token for a component/directive, Angular resolves it in two phases:
当为组件/指令解析令牌时,Angular 分为两个阶段来解析它:
-
Against the
ElementInjector
hierarchy (its parents)针对
ElementInjector
层次结构(其父级) -
Against the
ModuleInjector
hierarchy (its parents)针对
ModuleInjector
层次结构(其父级)
When a component declares a dependency, Angular tries to satisfy that
dependency with its own ElementInjector
.
If the component's injector lacks the provider, it passes the request
up to its parent component's ElementInjector
.
当组件声明依赖项时,Angular 会尝试使用它自己的 ElementInjector
来满足该依赖。
如果组件的注入器缺少提供者,它将把请求传给其父组件的 ElementInjector
。
The requests keep forwarding up until Angular finds an injector that can
handle the request or runs out of ancestor ElementInjector
s.
这些请求将继续转发,直到 Angular 找到可以处理该请求的注入器或用完祖先 ElementInjector
。
If Angular doesn't find the provider in any ElementInjector
s,
it goes back to the element where the request originated and looks
in the ModuleInjector
hierarchy.
If Angular still doesn't find the provider, it throws an error.
如果 Angular 在任何 ElementInjector
中都找不到提供者,它将返回到发起请求的元素,并在 ModuleInjector
层次结构中进行查找。如果 Angular 仍然找不到提供者,它将引发错误。
If you have registered a provider for the same DI token at different levels, the first one Angular encounters is the one it uses to resolve the dependency. If, for example, a provider is registered locally in the component that needs a service, Angular doesn't look for another provider of the same service.
如果你已在不同级别注册了相同 DI 令牌的提供者,则 Angular 会用遇到的第一个来解析该依赖。例如,如果提供者已经在需要此服务的组件中本地注册了,则 Angular 不会再寻找同一服务的其它提供者。
Resolution modifiers
解析修饰符
Angular's resolution behavior can be modified with @Optional()
, @Self()
,
@SkipSelf()
and @Host()
. Import each of them from @angular/core
and use each in the component class constructor when you inject your service.
可以使用 @Optional()
,@Self()
,@SkipSelf()
和 @Host()
来修饰 Angular 的解析行为。从 @angular/core
导入它们,并在注入服务时在组件类构造函数中使用它们。
For a working app showcasing the resolution modifiers that this section covers, see the resolution modifiers example.
关于展示本节介绍的解析修饰符的可运行应用,请参阅解析修饰符范例。
Types of modifiers
修饰符的类型
Resolution modifiers fall into three categories:
解析修饰符分为三类:
-
What to do if Angular doesn't find what you're looking for, that is
@Optional()
如果 Angular 找不到你要的东西该怎么办,用
@Optional()
-
Where to start looking, that is
@SkipSelf()
从哪里开始寻找,用
@SkipSelf()
-
Where to stop looking,
@Host()
and@Self()
到哪里停止寻找,用
@Host()
和@Self()
By default, Angular always starts at the current Injector
and keeps
searching all the way up. Modifiers allow you to change the starting
(self) or ending location.
默认情况下,Angular 始终从当前的 Injector
开始,并一直向上搜索。修饰符使你可以更改开始(默认是自己)或结束位置。
Additionally, you can combine all of the modifiers except @Host()
and @Self()
and of course @SkipSelf()
and @Self()
.
另外,你可以组合除 @Host()
和 @Self()
之外的所有修饰符,当然还有 @SkipSelf()
和 @Self()
。
{@a optional}
@Optional()
@Optional()
allows Angular to consider a service you inject to be optional.
This way, if it can't be resolved at runtime, Angular simply
resolves the service as null
, rather than throwing an error. In
the following example, the service, OptionalService
, isn't provided in
the service, @NgModule()
, or component class, so it isn't available
anywhere in the app.
@Optional()
允许 Angular 将你注入的服务视为可选服务。这样,如果无法在运行时解析它,Angular 只会将服务解析为 null
,而不会抛出错误。在下面的范例中,服务 OptionalService
没有在 @NgModule()
或组件类中提供,所以它没有在应用中的任何地方。
@Self()
Use @Self()
so that Angular will only look at the ElementInjector
for the current component or directive.
使用 @Self()
让 Angular 仅查看当前组件或指令的 ElementInjector
。
A good use case for @Self()
is to inject a service but only if it is
available on the current host element. To avoid errors in this situation,
combine @Self()
with @Optional()
.
@Self()
的一个好例子是要注入某个服务,但只有当该服务在当前宿主元素上可用时才行。为了避免这种情况下出错,请将 @Self()
与 @Optional()
结合使用。
For example, in the following SelfComponent
, notice
the injected LeafService
in
the constructor.
例如,在下面的 SelfComponent
中。请注意在构造函数中注入的 LeafService
。
In this example, there is a parent provider and injecting the
service will return the value, however, injecting the service
with @Self()
and @Optional()
will return null
because
@Self()
tells the injector to stop searching in the current
host element.
在这个例子中,有一个父提供者,注入服务将返回该值,但是,使用 @Self()
和 @Optional()
注入的服务将返回 null
因为 @Self()
告诉注入器在当前宿主元素上就要停止搜索。
Another example shows the component class with a provider
for FlowerService
. In this case, the injector looks no further
than the current ElementInjector
because it finds the FlowerService
and returns the yellow flower 🌼.
另一个范例显示了具有 FlowerService
提供者的组件类。在这个例子中,注入器没有超出当前 ElementInjector
就停止了,因为它已经找到了 FlowerService
并返回了黄色花朵🌼。
@SkipSelf()
@SkipSelf()
is the opposite of @Self()
. With @SkipSelf()
, Angular
starts its search for a service in the parent ElementInjector
, rather than
in the current one. So if the parent ElementInjector
were using the value 🌿
(fern)
for emoji
, but you had 🍁
(maple leaf) in the component's providers
array,
Angular would ignore 🍁
(maple leaf) and use 🌿
(fern).
@SkipSelf()
与 @Self()
相反。使用 @SkipSelf()
,Angular 在父 ElementInjector
中而不是当前 ElementInjector
中开始搜索服务。因此,如果父 ElementInjector
对 emoji
使用了值 🌿
(蕨类),但组件的 providers
数组中有 🍁
(枫叶),则 Angular 将忽略 🍁
(枫叶),而使用 🌿
(蕨类)。
To see this in code, assume that the following value for emoji
is what the parent component were using, as in this service:
要在代码中看到这一点,请先假定 emoji
的以下值就是父组件正在使用的值,如本服务所示:
Imagine that in the child component, you had a different value, 🍁
(maple leaf) but you wanted to use the parent's value instead. This is when you'd use @SkipSelf()
:
想象一下,在子组件中,你有一个不同的值 🍁
(枫叶),但你想使用父项的值。你就要使用 @SkipSelf()
:
In this case, the value you'd get for emoji
would be 🌿
(fern), not 🍁
(maple leaf).
在这个例子中,你获得的 emoji
值将为 🌿
(蕨类),而不是 🍁
(枫叶)。
@SkipSelf()
with @Optional()
合用 @SkipSelf()
和 @Optional()
Use @SkipSelf()
with @Optional()
to prevent an error if the value is null
. In the following example, the Person
service is injected in the constructor. @SkipSelf()
tells Angular to skip the current injector and @Optional()
will prevent an error should the Person
service be null
.
如果值为 null
请同时使用 @SkipSelf()
和 @Optional()
来防止错误。在下面的范例中,将 Person
服务注入到构造函数中。@SkipSelf()
告诉 Angular 跳过当前的注入器,如果 Person
服务为 null
,则 @Optional()
将防止报错。
class Person {
constructor(@Optional() @SkipSelf() parent?: Person) {}
}
@Host()
@Host()
lets you designate a component as the last stop in the injector tree when searching for providers. Even if there is a service instance further up the tree, Angular won't continue looking. Use @Host()
as follows:
@Host()
使你可以在搜索提供者时将当前组件指定为注入器树的最后一站。即使树的更上级有一个服务实例,Angular 也不会继续寻找。使用 @Host()
的例子如下:
Since HostComponent
has @Host()
in its constructor, no
matter what the parent of HostComponent
might have as a
flower.emoji
value,
the HostComponent
will use 🌼
(yellow flower).
由于 HostComponent
在其构造函数中具有 @Host()
,因此,无论 HostComponent
的父级是否可能有 flower.emoji
值,该 HostComponent
都将使用 🌼
(黄色花朵)。
Logical structure of the template
模板的逻辑结构
When you provide services in the component class, services are
visible within the ElementInjector
tree relative to where
and how you provide those services.
在组件类中提供服务时,服务在 ElementInjector
树中的可见性是取决于你在何处以及如何提供这些服务。
Understanding the underlying logical structure of the Angular template will give you a foundation for configuring services and in turn control their visibility.
了解 Angular 模板的基础逻辑结构将为你配置服务并进而控制其可见性奠定基础。
Components are used in your templates, as in the following example:
组件在模板中使用,如以下范例所示:
<app-root>
<app-child></app-child>
</app-root>
Note: Usually, you declare the components and their
templates in separate files. For the purposes of understanding
how the injection system works, it is useful to look at them
from the point of view of a combined logical tree. The term
logical distinguishes it from the render tree (your application
DOM tree). To mark the locations of where the component
templates are located, this guide uses the <#VIEW>
pseudo element, which doesn't actually exist in the render tree
and is present for mental model purposes only.
**注意:**通常,你要在单独的文件中声明组件及其模板。为了理解注入系统的工作原理,从组合逻辑树的视角来看它们是很有帮助的。使用术语“逻辑”将其与渲染树(你的应用程序 DOM 树)区分开。为了标记组件模板的位置,本指南使用 <#VIEW>
伪元素,该元素实际上不存在于渲染树中,仅用于心智模型中。
The following is an example of how the <app-root>
and <app-child>
view trees are combined into a single logical tree:
下面是如何将 <app-root>
和 <app-child>
视图树组合为单个逻辑树的范例:
<app-root>
<#VIEW>
<app-child>
<#VIEW>
...content goes here...
</#VIEW>
</app-child>
<#VIEW>
</app-root>
Understanding the idea of the <#VIEW>
demarcation is especially significant when you configure services in the component class.
当你在组件类中配置服务时,了解这种 <#VIEW>
划界的思想尤其重要。
Providing services in @Component()
在 @Component()
中提供服务
How you provide services via an @Component()
(or @Directive()
)
decorator determines their visibility. The following sections
demonstrate providers
and viewProviders
along with ways to
modify service visibility with @SkipSelf()
and @Host()
.
你如何通过 @Component()
(或 @Directive()
)装饰器提供服务决定了它们的可见性。以下各节演示了 providers
和 viewProviders
以及使用 @SkipSelf()
和 @Host()
修改服务可见性的方法。
A component class can provide services in two ways:
组件类可以通过两种方式提供服务:
-
with a
providers
array使用
providers
数组
@Component({
...
providers: [
{provide: FlowerService, useValue: {emoji: '🌺'}}
]
})
-
with a
viewProviders
array使用
viewProviders
数组
@Component({
...
viewProviders: [
{provide: AnimalService, useValue: {emoji: '🐶'}}
]
})
To understand how the providers
and viewProviders
influence service
visibility differently, the following sections build
a
step-by-step and compare the use of providers
and viewProviders
in code and a logical tree.
为了解 providers
和 viewProviders
对服务可见性的影响有何差异,以下各节将逐步构建一个 并在代码和逻辑树中比较 providers
和 viewProviders
的作用。
NOTE: In the logical tree, you'll see @Provide
, @Inject
, and
@NgModule
, which are not real HTML attributes but are here to demonstrate
what is going on under the hood.
**注意:**在逻辑树中,你会看到 @Provide
,@Inject
和 @NgModule
,这些不是真正的 HTML 属性,只是为了在这里证明其幕后的原理。
-
@Inject(Token)=>Value
demonstrates that ifToken
is injected at this location in the logical tree its value would beValue
.@Inject(Token)=>Value
表示,如果要将Token
注入逻辑树中的此位置,则它的值为Value
。 -
@Provide(Token=Value)
demonstrates that there is a declaration ofToken
provider with valueValue
at this location in the logical tree.@Provide(Token=Value)
表示,在逻辑树中的此位置存在一个值为Value
的Token
提供者的声明。 -
@NgModule(Token)
demonstrates that a fallbackNgModule
injector should be used at this location.@NgModule(Token)
表示,应在此位置使用后备的NgModule
注入器。
Example app structure
应用程序结构范例
The example app has a FlowerService
provided in root
with an emoji
value of 🌺
(red hibiscus).
范例应用程序的 root
提供了 FlowerService
,其 emoji
值为 🌺
(红色芙蓉)。
Consider a simple app with only an AppComponent
and a ChildComponent
.
The most basic rendered view would look like nested HTML elements such as
the following:
考虑一个只有 AppComponent
和 ChildComponent
的简单应用程序。最基本的渲染视图看起来就像嵌套的 HTML 元素,例如:
<app-root> <!-- AppComponent selector -->
<app-child> <!-- ChildComponent selector -->
</app-child>
</app-root>
However, behind the scenes, Angular uses a logical view representation as follows when resolving injection requests:
但是,在幕后,Angular 在解析注入请求时使用如下逻辑视图表示形式:
<app-root> <!-- AppComponent selector -->
<#VIEW>
<app-child> <!-- ChildComponent selector -->
<#VIEW>
</#VIEW>
</app-child>
</#VIEW>
</app-root>
The <#VIEW>
here represents an instance of a template.
Notice that each component has its own <#VIEW>
.
此处的 <#VIEW>
表示模板的实例。请注意,每个组件都有自己的 <#VIEW>
。
Knowledge of this structure can inform how you provide and inject your services, and give you complete control of service visibility.
了解此结构可以告知你如何提供和注入服务,并完全控制服务的可见性。
Now, consider that <app-root>
simply injects the FlowerService
:
现在,考虑 <app-root>
只注入了 FlowerService
:
Add a binding to the <app-root>
template to visualize the result:
将绑定添加到 <app-root>
模板来将结果可视化:
The output in the view would be:
该视图中的输出为:
Emoji from FlowerService: 🌺
In the logical tree, this would be represented as follows:
在逻辑树中,这可以表示成如下形式:
<app-root @NgModule(AppModule)
@Inject(FlowerService) flower=>"🌺">
<#VIEW>
<p>Emoji from FlowerService: {{flower.emoji}} (🌺)</p>
<app-child>
<#VIEW>
</#VIEW>
</app-child>
</#VIEW>
</app-root>
When <app-root>
requests the FlowerService
, it is the injector's job
to resolve the FlowerService
token. The resolution of the token happens
in two phases:
当 <app-root>
请求 FlowerService
时,注入器的工作就是解析 FlowerService
令牌。令牌的解析分为两个阶段:
-
The injector determines the starting location in the logical tree and an ending location of the search. The injector begins with the starting location and looks for the token at each level in the logical tree. If the token is found it is returned.
注入器确定逻辑树中搜索的开始位置和结束位置。注入程序从起始位置开始,并在逻辑树的每个级别上查找令牌。如果找到令牌,则将其返回。
-
If the token is not found, the injector looks for the closest parent
@NgModule()
to delegate the request to.如果未找到令牌,则注入程序将寻找最接近的父
@NgModule()
委派该请求。
In the example case, the constraints are:
在这个例子中,约束为:
-
Start with
<#VIEW>
belonging to<app-root>
and end with<app-root>
.从属于
<app-root>
的<#VIEW>
开始,并结束于<app-root>
。
-
Normally the starting point for search is at the point of injection. However, in this case
<app-root>
@Component
s are special in that they also include their ownviewProviders
, which is why the search starts at<#VIEW>
belonging to<app-root>
. (This would not be the case for a directive matched at the same location).通常,搜索的起点就是注入点。但是,在这个例子中,
<app-root>
@Component
的特殊之处在于它们还包括自己的viewProviders
,这就是为什么搜索从<app-root>
的<#VIEW>
开始的原因。(对于匹配同一位置的指令,情况却并非如此)。 -
The ending location just happens to be the same as the component itself, because it is the topmost component in this application.
结束位置恰好与组件本身相同,因为它就是此应用程序中最顶层的组件。
-
The
AppModule
acts as the fallback injector when the injection token can't be found in theElementInjector
s.当在
ElementInjector
中找不到注入令牌时,就用AppModule
充当后备注入器。
Using the providers
array
使用 providers
数组
Now, in the ChildComponent
class, add a provider for FlowerService
to demonstrate more complex resolution rules in the upcoming sections:
现在,在 ChildComponent
类中,为 FlowerService
添加一个提供者,以便在接下来的小节中演示更复杂的解析规则:
Now that the FlowerService
is provided in the @Component()
decorator,
when the <app-child>
requests the service, the injector has only to look
as far as the <app-child>
's own ElementInjector
. It won't have to
continue the search any further through the injector tree.
现在,在 @Component()
装饰器中提供了 FlowerService
,当 <app-child>
请求该服务时,注入器仅需要查找 <app-child>
自己的 ElementInjector
。不必再通过注入器树继续搜索。
The next step is to add a binding to the ChildComponent
template.
下一步是将绑定添加到 ChildComponent
模板。
To render the new values, add <app-child>
to the bottom of
the AppComponent
template so the view also displays the sunflower:
要渲染新的值,请在 AppComponent
模板的底部添加 <app-child>
,以便其视图也显示向日葵:
Child Component
Emoji from FlowerService: 🌻
In the logical tree, this would be represented as follows:
在逻辑树中,可以把它表示成这样:
<app-root @NgModule(AppModule)
@Inject(FlowerService) flower=>"🌺">
<#VIEW>
<p>Emoji from FlowerService: {{flower.emoji}} (🌺)</p>
<app-child @Provide(FlowerService="🌻")
@Inject(FlowerService)=>"🌻"> <!-- search ends here -->
<#VIEW> <!-- search starts here -->
<h2>Parent Component</h2>
<p>Emoji from FlowerService: {{flower.emoji}} (🌻)</p>
</#VIEW>
</app-child>
</#VIEW>
</app-root>
When <app-child>
requests the FlowerService
, the injector begins
its search at the <#VIEW>
belonging to <app-child>
(<#VIEW>
is
included because it is injected from @Component()
) and ends with
<app-child>
. In this case, the FlowerService
is resolved in the
<app-child>
's providers
array with sunflower 🌻. The injector doesn't
have to look any further in the injector tree. It stops as soon as it
finds the FlowerService
and never sees the 🌺 (red hibiscus).
当 <app-child>
请求 FlowerService
时,注入器从 <app-child>
的 <#VIEW>
开始搜索(包括 <#VIEW>
,因为它是从 @Component()
注入的),并到 <app-child>
结束。在这个例子中,FlowerService
在 <app-child>
的 providers
数组中解析为向日葵🌻。注入器不必在注入器树中进一步查找。一旦找到 FlowerService
,它便停止运行,再也看不到🌺(红芙蓉)。
{@a use-view-providers}
Using the viewProviders
array
使用 viewProviders
数组
Use the viewProviders
array as another way to provide services in the
@Component()
decorator. Using viewProviders
makes services
visible in the <#VIEW>
.
使用 viewProviders
数组是在 @Component()
装饰器中提供服务的另一种方法。使用 viewProviders
使服务在 <#VIEW>
中可见。
The steps are the same as using the providers
array,
with the exception of using the viewProviders
array instead.
除了使用 viewProviders
数组外,其它步骤与使用 providers
数组相同。
For step-by-step instructions, continue with this section. If you can set it up on your own, skip ahead to Modifying service availability.
关于这些步骤的说明,请继续本节。如果你可以自行设置,请跳至修改服务可用性 一节。
The example app features a second service, the AnimalService
to
demonstrate viewProviders
.
该范例应用程序具有第二个服务 AnimalService
来演示 viewProviders
。
First, create an AnimalService
with an emoji
property of 🐳 (whale):
首先,创建一个 AnimalService
与 emoji
的🐳(鲸鱼)属性:
Following the same pattern as with the FlowerService
, inject the
AnimalService
in the AppComponent
class:
遵循与 FlowerService
相同的模式,将 AnimalService
注入 AppComponent
类:
Note: You can leave all the FlowerService
related code in place
as it will allow a comparison with the AnimalService
.
**注意:**你可以保留所有与 FlowerService
相关的代码,因为它可以与 AnimalService
进行比较。
Add a viewProviders
array and inject the AnimalService
in the
<app-child>
class, too, but give emoji
a different value. Here,
it has a value of 🐶 (puppy).
添加一个 viewProviders
数组,并将 AnimalService
也注入到 <app-child>
类中,但是给 emoji
一个不同的值。在这里,它的值为🐶(小狗)。
Add bindings to the ChildComponent
and the AppComponent
templates.
In the ChildComponent
template, add the following binding:
将绑定添加到 ChildComponent
和 AppComponent
模板。在 ChildComponent
模板中,添加以下绑定:
Additionally, add the same to the AppComponent
template:
此外,将其添加到 AppComponent
模板:
Now you should see both values in the browser:
现在,你应该在浏览器中看到两个值:
AppComponent
Emoji from AnimalService: 🐳
Child Component
Emoji from AnimalService: 🐶
The logic tree for this example of viewProviders
is as follows:
此 viewProviders
范例的逻辑树如下:
<app-root @NgModule(AppModule)
@Inject(AnimalService) animal=>"🐳">
<#VIEW>
<app-child>
<#VIEW
@Provide(AnimalService="🐶")
@Inject(AnimalService=>"🐶")>
<!-- ^^using viewProviders means AnimalService is available in <#VIEW>-->
<p>Emoji from AnimalService: {{animal.emoji}} (🐶)</p>
</#VIEW>
</app-child>
</#VIEW>
</app-root>
Just as with the FlowerService
example, the AnimalService
is provided
in the <app-child>
@Component()
decorator. This means that since the
injector first looks in the ElementInjector
of the component, it finds the
AnimalService
value of 🐶 (puppy). It doesn't need to continue searching the
ElementInjector
tree, nor does it need to search the ModuleInjector
.
与 FlowerService
范例一样,<app-child>
@Component()
装饰器中提供了 AnimalService
。这意味着,由于注入器首先在组件的 ElementInjector
中查找,因此它将找到 AnimalService
的值 🐶(小狗)。它不需要继续搜索 ElementInjector
树,也不需要搜索 ModuleInjector
。
providers
vs. viewProviders
providers
与 viewProviders
To see the difference between using providers
and viewProviders
, add
another component to the example and call it InspectorComponent
.
InspectorComponent
will be a child of the ChildComponent
. In
inspector.component.ts
, inject the FlowerService
and AnimalService
in
the constructor:
为了看清 providers
和 viewProviders
的差异,请在范例中添加另一个组件,并将其命名为 InspectorComponent
。InspectorComponent
将是 ChildComponent
的子 ChildComponent
。在 inspector.component.ts
中,将 FlowerService
和 AnimalService
注入构造函数中:
You do not need a providers
or viewProviders
array. Next, in
inspector.component.html
, add the same markup from previous components:
你不需要 providers
或 viewProviders
数组。接下来,在 inspector.component.html
中,从以前的组件中添加相同的 html:
Remember to add the InspectorComponent
to the AppModule
declarations
array.
别忘了将 InspectorComponent
添加到 AppModule
declarations
数组。
Next, make sure your child.component.html
contains the following:
接下来,确保你的 child.component.html
包含以下内容:
The first two lines, with the bindings, are there from previous steps. The
new parts are <ng-content>
and <app-inspector>
. <ng-content>
allows
you to project content, and <app-inspector>
inside the ChildComponent
template makes the InspectorComponent
a child component of
ChildComponent
.
前两行带有绑定,来自之前的步骤。新的部分是 <ng-content>
和 <app-inspector>
。<ng-content>
允许你投影内容,ChildComponent
模板中的 <app-inspector>
使 InspectorComponent
成为 ChildComponent
的子组件。
Next, add the following to app.component.html
to take advantage of content projection.
接下来,将以下内容添加到 app.component.html
中以利用内容投影的优势。
The browser now renders the following, omitting the previous examples for brevity:
现在,浏览器将渲染以下内容,为简洁起见,省略了前面的范例:
//...Omitting previous examples. The following applies to this section.
Content projection: This is coming from content. Doesn't get to see
puppy because the puppy is declared inside the view only.
Emoji from FlowerService: 🌻
Emoji from AnimalService: 🐳
Emoji from FlowerService: 🌻
Emoji from AnimalService: 🐶
These four bindings demonstrate the difference between providers
and viewProviders
. Since the 🐶 (puppy) is declared inside the <#VIEW>,
it isn't visible to the projected content. Instead, the projected
content sees the 🐳 (whale).
这四个绑定说明了 providers
和 viewProviders
之间的区别。由于🐶(小狗)在<#VIEW>中声明,因此投影内容不可见。投影的内容中会看到🐳(鲸鱼)。
The next section though, where InspectorComponent
is a child component
of ChildComponent
, InspectorComponent
is inside the <#VIEW>
, so
when it asks for the AnimalService
, it sees the 🐶 (puppy).
但是下一部分,InspectorComponent
是 ChildComponent
的子组件,InspectorComponent
在 <#VIEW>
内部,因此当它请求 AnimalService
时,它会看到🐶(小狗)。
The AnimalService
in the logical tree would look like this:
逻辑树中的 AnimalService
如下所示:
<app-root @NgModule(AppModule)
@Inject(AnimalService) animal=>"🐳">
<#VIEW>
<app-child>
<#VIEW
@Provide(AnimalService="🐶")
@Inject(AnimalService=>"🐶")>
<!-- ^^using viewProviders means AnimalService is available in <#VIEW>-->
<p>Emoji from AnimalService: {{animal.emoji}} (🐶)</p>
<app-inspector>
<p>Emoji from AnimalService: {{animal.emoji}} (🐶)</p>
</app-inspector>
</#VIEW>
<app-inspector>
<#VIEW>
<p>Emoji from AnimalService: {{animal.emoji}} (🐳)</p>
</#VIEW>
</app-inspector>
</app-child>
</#VIEW>
</app-root>
The projected content of <app-inspector>
sees the 🐳 (whale), not
the 🐶 (puppy), because the
🐶 (puppy) is inside the <app-child>
<#VIEW>
. The <app-inspector>
can
only see the 🐶 (puppy)
if it is also within the <#VIEW>
.
<app-inspector>
的投影内容中看到了🐳(鲸鱼),而不是🐶(小狗),因为🐶(小狗)在 <app-child>
的 <#VIEW>
中。如果 <app-inspector>
也位于 <#VIEW>
则只能看到🐶(小狗)。
{@a modify-visibility}
Modifying service visibility
修改服务可见性
This section describes how to limit the scope of the beginning and
ending ElementInjector
using the visibility decorators @Host()
,
@Self()
, and @SkipSelf()
.
本节讲的是如何使用可见性修饰符 @Host()
,@Self()
和 @SkipSelf()
来限制 ElementInjector
的开始和结束范围。
Visibility of provided tokens
提供者令牌的可见性
Visibility decorators influence where the search for the injection
token begins and ends in the logic tree. To do this, place
visibility decorators at the point of injection, that is, the
constructor()
, rather than at a point of declaration.
可见性装饰器影响搜索注入令牌时在逻辑树中开始和结束的位置。为此,要将可见性装饰器放置在注入点,即 constructor()
,而不是在声明点。
To alter where the injector starts looking for FlowerService
, add
@SkipSelf()
to the <app-child>
@Inject
declaration for the
FlowerService
. This declaration is in the <app-child>
constructor
as shown in child.component.ts
:
为了修改该注入器从哪里开始寻找 FlowerService
,把 @SkipSelf()
加到 <app-child>
的 @Inject
声明 FlowerService
中。该声明在 <app-child>
构造函数中,如 child.component.ts
所示:
constructor(@SkipSelf() public flower : FlowerService) { }
With @SkipSelf()
, the <app-child>
injector doesn't look to itself for
the FlowerService
. Instead, the injector starts looking for the
FlowerService
at the <app-root>
's ElementInjector
, where it finds
nothing. Then, it goes back to the <app-child>
ModuleInjector
and finds
the 🌺 (red hibiscus) value, which is available because the <app-child>
ModuleInjector
and the <app-root>
ModuleInjector
are flattened into one
ModuleInjector
. Thus, the UI renders the following:
使用 @SkipSelf()
,<app-child>
注入器不会寻找自身来获取 FlowerService
。相反,喷射器开始在 <app-root>
的 ElementInjector
中寻找 FlowerService
,在那里它什么也没找到。
然后,它返回到 <app-child>
的 ModuleInjector
并找到🌺(红芙蓉)值,这是可用的,因为 <app-child>
ModuleInjector
和 <app-root>
ModuleInjector
被展开成了一个 ModuleInjector
。因此,UI 将渲染以下内容:
Emoji from FlowerService: 🌺
In a logical tree, this same idea might look like this:
在逻辑树中,这种情况可能如下所示:
<app-root @NgModule(AppModule)
@Inject(FlowerService) flower=>"🌺">
<#VIEW>
<app-child @Provide(FlowerService="🌻")>
<#VIEW @Inject(FlowerService, SkipSelf)=>"🌺">
<!-- With SkipSelf, the injector looks to the next injector up the tree -->
</#VIEW>
</app-child>
</#VIEW>
</app-root>
Though <app-child>
provides the 🌻 (sunflower), the app renders
the 🌺 (red hibiscus) because @SkipSelf()
causes the current
injector to skip
itself and look to its parent.
尽管 <app-child>
提供了🌻(向日葵),但该应用程序渲染了🌺(红色芙蓉),因为 @SkipSelf()
导致当前的注入器跳过了自身并寻找其父级。
If you now add @Host()
(in addition to the @SkipSelf()
) to the
@Inject
of the FlowerService
, the result will be null
. This is
because @Host()
limits the upper bound of the search to the
<#VIEW>
. Here's the idea in the logical tree:
如果现在将 @Host()
(以及 @SkipSelf()
)添加到了 FlowerService
的 @Inject
,其结果将为 null
。这是因为 @Host()
将搜索的上限限制为 <#VIEW>
。这是在逻辑树中的情况:
<app-root @NgModule(AppModule)
@Inject(FlowerService) flower=>"🌺">
<#VIEW> <!-- end search here with null-->
<app-child @Provide(FlowerService="🌻")> <!-- start search here -->
<#VIEW @Inject(FlowerService, @SkipSelf, @Host, @Optional)=>null>
</#VIEW>
</app-parent>
</#VIEW>
</app-root>
Here, the services and their values are the same, but @Host()
stops the injector from looking any further than the <#VIEW>
for FlowerService
, so it doesn't find it and returns null
.
在这里,服务及其值是相同的,但是 @Host()
阻止了注入器对 FlowerService
进行任何高于 <#VIEW>
的查找,因此找不到它并返回 null
。
Note: The example app uses @Optional()
so the app does
not throw an error, but the principles are the same.
**注意:**范例应用程序使用 @Optional()
因此该应用程序不会引发错误,但是其原理是一样的。
@SkipSelf()
and viewProviders
@SkipSelf()
和 viewProviders
The <app-child>
currently provides the AnimalService
in
the viewProviders
array with the value of 🐶 (puppy). Because
the injector has only to look at the <app-child>
's ElementInjector
for the AnimalService
, it never sees the 🐳 (whale).
该 <app-child>
目前提供在 viewProviders
数组中提供了值为 🐶(小狗)的 AnimalService
。由于注入器只需要查看 <app-child>
的 ElementInjector
中的 AnimalService
,它就不会看到🐳(鲸鱼)。
Just as in the FlowerService
example, if you add @SkipSelf()
to the constructor for the AnimalService
, the injector won't
look in the current <app-child>
's ElementInjector
for the
AnimalService
.
就像在 FlowerService
范例中一样,如果将 @SkipSelf()
添加到 AnimalService
的构造函数中,则注入器将不在 AnimalService
的当前 <app-child>
的 ElementInjector
中查找 AnimalService
。
export class ChildComponent {
// add @SkipSelf()
constructor(@SkipSelf() public animal : AnimalService) { }
}
Instead, the injector will begin at the <app-root>
ElementInjector
. Remember that the <app-child>
class
provides the AnimalService
in the viewProviders
array
with a value of 🐶 (puppy):
相反,注入器将从 <app-root>
ElementInjector
开始找。请记住,<app-child>
类在 viewProviders
数组中 AnimalService
中提供了🐶(小狗)的值:
@Component({
selector: 'app-child',
...
viewProviders:
[{ provide: AnimalService, useValue: { emoji: '🐶' } }]
})
The logical tree looks like this with @SkipSelf()
in <app-child>
:
在 <app-child>
中使用 @SkipSelf()
的逻辑树是这样的:
<app-root @NgModule(AppModule)
@Inject(AnimalService=>"🐳")>
<#VIEW><!-- search begins here -->
<app-child>
<#VIEW
@Provide(AnimalService="🐶")
@Inject(AnimalService, SkipSelf=>"🐳")>
<!--Add @SkipSelf -->
</#VIEW>
</app-child>
</#VIEW>
</app-root>
With @SkipSelf()
in the <app-child>
, the injector begins its
search for the AnimalService
in the <app-root>
ElementInjector
and finds 🐳 (whale).
在 <app-child>
中使用 @SkipSelf()
,注入器就会在 <app-root>
的 ElementInjector
中找到 🐳(鲸)。
@Host()
and viewProviders
@Host()
和 viewProviders
If you add @Host()
to the constructor for AnimalService
, the
result is 🐶 (puppy) because the injector finds the AnimalService
in the <app-child>
<#VIEW>
. Here is the viewProviders
array
in the <app-child>
class and @Host()
in the constructor:
如果把 @Host()
添加到 AnimalService
的构造函数上,结果就是🐶(小狗),因为注入器会在 <app-child>
的 <#VIEW>
中查找 AnimalService
服务。这里是 <app-child>
类中的 viewProviders
数组和构造函数中的 @Host()
:
@Component({
selector: 'app-child',
...
viewProviders:
[{ provide: AnimalService, useValue: { emoji: '🐶' } }]
})
export class ChildComponent {
constructor(@Host() public animal : AnimalService) { }
}
@Host()
causes the injector to look until it encounters the edge of the <#VIEW>
.
@Host()
导致注入器开始查找,直到遇到 <#VIEW>
的边缘。
<app-root @NgModule(AppModule)
@Inject(AnimalService=>"🐳")>
<#VIEW>
<app-child>
<#VIEW
@Provide(AnimalService="🐶")
@Inject(AnimalService, @Host=>"🐶")> <!-- @Host stops search here -->
</#VIEW>
</app-child>
</#VIEW>
</app-root>
Add a viewProviders
array with a third animal, 🦔 (hedgehog), to the
app.component.ts
@Component()
metadata:
将带有第三个动物🦔(刺猬)的 viewProviders
数组添加到 app.component.ts
的 @Component()
元数据中:
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ],
viewProviders: [{ provide: AnimalService, useValue: { emoji: '🦔' } }]
})
Next, add @SkipSelf()
along with @Host()
to the constructor for the
Animal Service
in child.component.ts
. Here are @Host()
and @SkipSelf()
in the <app-child>
constructor :
接下来,同时把 @SkipSelf()
和 @Host()
加在 child.component.ts
中 AnimalService
的构造函数中。这是 <app-child>
构造函数中的 @Host()
和 @SkipSelf()
:
export class ChildComponent {
constructor(
@Host() @SkipSelf() public animal : AnimalService) { }
}
When @Host()
and SkipSelf()
were applied to the FlowerService
,
which is in the providers
array, the result was null
because
@SkipSelf()
starts its search in the <app-child>
injector, but
@Host()
stops searching at <#VIEW>
—where there is no
FlowerService
. In the logical tree, you can see that the
FlowerService
is visible in <app-child>
, not its <#VIEW>
.
将 @Host()
和 SkipSelf()
应用于 providers
数组中的 FlowerService
,结果为 null
,因为 @SkipSelf()
会在 <app-child>
的注入器中开始搜索,但是 @Host()
要求它在 <#VIEW>
停止搜索 —— 没有 FlowerService
。在逻辑树中,你可以看到 FlowerService
在 <app-child>
中可见,而在 <#VIEW>
中不可见。
However, the AnimalService
, which is provided in the
AppComponent
viewProviders
array, is visible.
不过,提供在 AppComponent
的 viewProviders
数组中的 AnimalService
,是可见的。
The logical tree representation shows why this is:
逻辑树表示法说明了为何如此:
<app-root @NgModule(AppModule)
@Inject(AnimalService=>"🐳")>
<#VIEW @Provide(AnimalService="🦔")
@Inject(AnimalService, @SkipSelf, @Host, @Optional)=>"🦔">
<!-- ^^@SkipSelf() starts here, @Host() stops here^^ -->
<app-child>
<#VIEW @Provide(AnimalService="🐶")
@Inject(AnimalService, @SkipSelf, @Host, @Optional)=>"🐶">
<!-- Add @SkipSelf ^^-->
</#VIEW>
</app-child>
</#VIEW>
</app-root>
@SkipSelf()
, causes the injector to start its search for
the AnimalService
at the <app-root>
, not the <app-child>
,
where the request originates, and @Host()
stops the search
at the <app-root>
<#VIEW>
. Since AnimalService
is
provided via the viewProviders
array, the injector finds 🦔
(hedgehog) in the <#VIEW>
.
@SkipSelf()
导致注入器从 <app-root>
而不是 <app-child>
处开始对 AnimalService
进行搜索,而 @Host()
会在 <app-root>
的 <#VIEW>
处停止搜索。
由于 AnimalService
是通过 viewProviders
数组提供的,因此注入程序会在 <#VIEW>
找到🦔(刺猬)。
{@a component-injectors}
ElementInjector
use case examples
ElementInjector
用例范例
The ability to configure one or more providers at different levels opens up useful possibilities. For a look at the following scenarios in a working app, see the heroes use case examples.
在不同级别配置一个或多个提供者的能力开辟了很有用的可能性。要查看正在运行的应用中的以下情况,请参阅英雄范例。
Scenario: service isolation
场景:服务隔离
Architectural reasons may lead you to restrict access to a service to the application domain where it belongs.
For example, the guide sample includes a VillainsListComponent
that displays a list of villains.
It gets those villains from a VillainsService
.
出于架构方面的考虑,可能会让你决定把一个服务限制到只能在它所属的那个应用域中访问。
比如,这个例子中包括一个用于显示反派列表的 VillainsListComponent
,它会从 VillainsService
中获得反派列表数据。
If you provided VillainsService
in the root AppModule
(where you registered the HeroesService
),
that would make the VillainsService
visible everywhere in the
application, including the Hero workflows. If you later
modified the VillainsService
, you could break something in a
hero component somewhere.
如果你在根模块 AppModule
中(也就是你注册 HeroesService
的地方)提供 VillainsService
,就会让应用中的任何地方都能访问到 VillainsService
,包括针对英雄的工作流。如果你稍后修改了 VillainsService
,就可能破坏了英雄组件中的某些地方。在根模块 AppModule
中提供该服务将会引入此风险。
Instead, you can provide the VillainsService
in the providers
metadata of the VillainsListComponent
like this:
该怎么做呢?你可以在 VillainsListComponent
的 providers
元数据中提供 VillainsService
,就像这样:
By providing VillainsService
in the VillainsListComponent
metadata and nowhere else,
the service becomes available only in the VillainsListComponent
and its sub-component tree.
在 VillainsListComponent
的元数据中而不是其它地方提供 VillainsService
服务,该服务就会只在 VillainsListComponent
及其子组件树中可用。
VillainService
is a singleton with respect to VillainsListComponent
because that is where it is declared. As long as VillainsListComponent
does not get destroyed it will be the same instance of VillainService
but if there are multilple instances of VillainsListComponent
, then each
instance of VillainsListComponent
will have its own instance of VillainService
.
VillainService
对于 VillainsListComponent
来说是单例的,因为它就是在这里声明的。只要 VillainsListComponent
没有销毁,它就始终是 VillainService
的同一个实例。但是对于 VillainsListComponent
的多个实例,每个 VillainsListComponent
的实例都会有自己的 VillainService
实例。
Scenario: multiple edit sessions
场景:多重编辑会话
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 on several tax returns, switching from one to the other throughout the day.
很多应用允许用户同时进行多个任务。 比如,在纳税申报应用中,申报人可以打开多个报税单,随时可能从一个切换到另一个。
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.
本章要示范的场景仍然是基于《英雄之旅》的。
想象一个外层的 HeroListComponent
,它显示一个超级英雄的列表。
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.
要打开一个英雄的报税单,申报者点击英雄名,它就会打开一个组件来编辑那个申报单。 每个选中的申报单都会在自己的组件中打开,并且可以同时打开多个申报单。
Each tax return component has the following characteristics:
每个报税单组件都有下列特征:
-
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.
能把所做的修改保存到它的报税单中,或者放弃它们。
Suppose that the HeroTaxReturnComponent
had 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 could delegate that management to a helper service, as this example does.
假设 HeroTaxReturnComponent
还有一些管理并还原这些更改的逻辑。
这对于简单的报税单来说是很容易的。
不过,在现实世界中,报税单的数据模型非常复杂,对这些修改的管理可能不得不投机取巧。
你可以把这种管理任务委托给一个辅助服务,就像这个例子中所做的。
The HeroTaxReturnService
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.
报税单服务 HeroTaxReturnService
缓存了单条 HeroTaxReturn
,用于跟踪那个申报单的变更,并且可以保存或还原它。
它还委托给了全应用级的单例服务 HeroService
,它是通过依赖注入机制取得的。
Here is the HeroTaxReturnComponent
that makes use of HeroTaxReturnService
.
下面是正在使用 HeroTaxReturnService
的 HeroTaxReturnComponent
组件。
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.
通过 @Input()
属性可以得到要编辑的报税单,这个属性被实现成了读取器(getter)和设置器(setter)。
设置器根据传进来的报税单初始化了组件自己的 HeroTaxReturnService
实例。
读取器总是返回该服务所存英雄的当前状态。
组件也会请求该服务来保存或还原这个报税单。
This won't work if the service is an application-wide singleton. Every component would share the same service instance, and each component would overwrite the tax return that belonged to another hero.
但如果该服务是一个全应用范围的单例就不行了。 每个组件就都会共享同一个服务实例,每个组件也都会覆盖属于其它英雄的报税单。
To prevent this, configure the component-level injector of HeroTaxReturnComponent
to provide the service, using the providers
property in the component metadata.
要防止这一点,就要在 HeroTaxReturnComponent
元数据的 providers
属性中配置组件级的注入器,来提供该服务。
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, and no tax return gets overwritten.
HeroTaxReturnComponent
有它自己的 HeroTaxReturnService
提供者。
回忆一下,每个组件的实例都有它自己的注入器。
在组件级提供服务可以确保组件的每个实例都得到一个自己的、私有的服务实例,而报税单也不会再被意外覆盖了。
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 .
该场景代码中的其它部分依赖另一些 Angular 的特性和技术,你将会在本文档的其它章节学到。 你可以到查看代码和下载它。
Scenario: specialized providers
场景:专门的提供者
Another reason to re-provide a service at another level is to substitute a more specialized implementation of that service, deeper in the component tree.
在其它层级重新提供服务的另一个理由,是在组件树的深层中把该服务替换为一个更专门化的实现。
Consider a Car component that depends on several services.
Suppose you configured the root injector (marked as A) with generic providers for
CarService
, EngineService
and TiresService
.
考虑一个依赖于一系列服务的 Car 组件。
假设你在根注入器(代号 A)中配置了通用的提供者:CarService
、EngineService
和 TiresService
。
You create a car component (A) that displays a car constructed from these three generic services.
你创建了一个车辆组件(A),它显示一个从另外三个通用服务构造出的车辆。
Then you create a child component (B) that defines its own, specialized providers for CarService
and EngineService
that have special capabilities suitable for whatever is going on in component (B).
然后,你创建一个子组件(B),它为 CarService
和 EngineService
定义了自己特有的提供者,它们具有适用于组件 B 的特有能力。
Component (B) is the parent of another component (C) that defines its own, even more specialized provider for CarService
.
组件 B 是另一个组件 C 的父组件,而组件 C 又定义了自己的,更特殊的CarService
提供者。
Behind the scenes, each component sets up its own injector with zero, one, or more providers defined for that component itself.
在幕后,每个组件都有自己的注入器,这个注入器带有为组件本身准备的 0 个、1 个或多个提供者。
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).
当你在最深层的组件 C 解析 Car
的实例时,它使用注入器 C 解析生成了一个 Car
的实例,使用注入器 B 解析了 Engine
,而 Tires
则是由根注入器 A 解析的。
More on dependency injection
关于依赖注入的更多知识
For more information on Angular dependency injection, see the DI Providers and DI in Action guides.