# Conflicts: # aio/content/guide/ajs-quick-reference.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/architecture.md # aio/content/guide/attribute-directives.md # aio/content/guide/bootstrapping.md # aio/content/guide/browser-support.md # aio/content/guide/cheatsheet.md # aio/content/guide/comparing-observables.md # aio/content/guide/component-interaction.md # aio/content/guide/component-styles.md # aio/content/guide/dependency-injection-in-action.md # aio/content/guide/dependency-injection-pattern.md # aio/content/guide/dependency-injection.md # aio/content/guide/deployment.md # aio/content/guide/displaying-data.md # aio/content/guide/dynamic-component-loader.md # aio/content/guide/elements.md # aio/content/guide/feature-modules.md # aio/content/guide/form-validation.md # aio/content/guide/forms.md # aio/content/guide/glossary.md # aio/content/guide/hierarchical-dependency-injection.md # aio/content/guide/i18n.md # aio/content/guide/lazy-loading-ngmodules.md # aio/content/guide/lifecycle-hooks.md # aio/content/guide/ngmodule-faq.md # aio/content/guide/ngmodule-vs-jsmodule.md # aio/content/guide/ngmodules.md # aio/content/guide/npm-packages.md # aio/content/guide/observables-in-angular.md # aio/content/guide/observables.md # aio/content/guide/pipes.md # aio/content/guide/practical-observable-usage.md # aio/content/guide/providers.md # aio/content/guide/quickstart.md # aio/content/guide/reactive-forms.md # aio/content/guide/router.md # aio/content/guide/rx-library.md # aio/content/guide/security.md # aio/content/guide/service-worker-communications.md # aio/content/guide/service-worker-getting-started.md # aio/content/guide/service-worker-intro.md # aio/content/guide/setup-systemjs-anatomy.md # aio/content/guide/setup.md # aio/content/guide/singleton-services.md # aio/content/guide/structural-directives.md # aio/content/guide/styleguide.md # aio/content/guide/template-syntax.md # aio/content/guide/testing.md # aio/content/guide/typescript-configuration.md # aio/content/guide/universal.md # aio/content/guide/updating.md # aio/content/guide/upgrade.md # aio/content/guide/user-input.md # aio/content/guide/visual-studio-2015.md # aio/content/marketing/docs.md # aio/content/navigation.json # aio/content/tutorial/toh-pt0.md # aio/content/tutorial/toh-pt1.md # aio/content/tutorial/toh-pt2.md # aio/content/tutorial/toh-pt3.md # aio/content/tutorial/toh-pt4.md # aio/content/tutorial/toh-pt5.md # aio/content/tutorial/toh-pt6.md # aio/ngsw-manifest.json # aio/package.json # aio/src/app/custom-elements/api/api-list.component.html # aio/src/app/custom-elements/api/api-list.component.ts # aio/src/index.html # aio/tools/transforms/templates/api/base.template.html # aio/tools/transforms/templates/api/class.template.html # aio/tools/transforms/templates/api/directive.template.html # aio/tools/transforms/templates/api/enum.template.html # aio/tools/transforms/templates/api/includes/class-overview.html # aio/tools/transforms/templates/api/includes/deprecation.html # aio/tools/transforms/templates/api/includes/export-as.html # aio/tools/transforms/templates/api/includes/info-bar.html # aio/tools/transforms/templates/api/includes/interface-overview.html # aio/tools/transforms/templates/api/includes/selectors.html # aio/tools/transforms/templates/api/lib/directiveHelpers.html # aio/tools/transforms/templates/api/lib/githubLinks.html # aio/tools/transforms/templates/api/lib/memberHelpers.html # aio/tools/transforms/templates/api/package.template.html # aio/yarn.lock # packages/common/http/src/module.ts # packages/common/src/common_module.ts # packages/common/src/directives/ng_for_of.ts # packages/common/src/directives/ng_if.ts # packages/common/src/directives/ng_template_outlet.ts # packages/common/src/location/location.ts # packages/common/src/pipes/async_pipe.ts # packages/common/src/pipes/json_pipe.ts # packages/common/src/pipes/number_pipe.ts # packages/common/src/pipes/slice_pipe.ts # packages/core/src/change_detection/change_detector_ref.ts # packages/core/src/di/injectable.ts # packages/core/src/linker/template_ref.ts # packages/core/src/linker/view_container_ref.ts # packages/core/src/metadata/di.ts # packages/core/src/metadata/ng_module.ts # packages/core/src/render/api.ts # packages/forms/src/directives/form_interface.ts # packages/forms/src/directives/ng_form.ts # packages/forms/src/directives/ng_model.ts # packages/forms/src/directives/reactive_directives/form_control_name.ts # packages/forms/src/directives/select_control_value_accessor.ts # packages/forms/src/directives/validators.ts # packages/forms/src/form_providers.ts # packages/forms/src/model.ts # packages/forms/src/validators.ts # packages/router/src/config.ts # packages/router/src/router.ts # packages/router/src/router_module.ts # packages/router/src/router_state.ts
50 KiB
Dependency Injection in Action
This section explores many of the features of dependency injection (DI) in Angular.
本节将会涉及 Angular 依赖注入(DI)的很多特性。
{@a toc}
See the of the code in this cookbook.
要获取本文的代码,参见。
{@a nested-dependencies}
Nested service dependencies
@Injectable和嵌套服务依赖
The consumer of an injected service doesn't need to know how to create that service. It's the job of the DI framework to create and cache dependencies. The consumer just needs to let the DI framework know which dependencies it needs.
这些被注入服务的消费者不需要知道如何创建这个服务,它也不应该在乎。新建和缓存这个服务是依赖注入器的工作。
Sometimes a service depends on other services, which may depend on yet other services. The dependency injection framework resolves these nested dependencies in the correct order. At each step, the consumer of dependencies declares what it requires in its constructor, and lets the framework provide them.
有时候一个服务依赖其它服务...而其它服务可能依赖另外的更多服务。按正确的顺序解析这些嵌套依赖也是框架的工作。 在每一步,依赖的使用者只要在它的构造函数里简单声明它需要什么,框架就会完成所有剩下的事情。
The following example shows that AppComponent
declares its dependence on LoggerService
and UserContext
.
下面的例子往 AppComponent
里注入的 LoggerService
和 UserContext
。
UserContext
in turn depends on both LoggerService
and
UserService
, another service that gathers information about a particular user.
UserContext
有两个依赖 LoggerService
(再一次)和负责获取特定用户信息的 UserService
。
When Angular creates AppComponent
, the DI framework creates an instance of LoggerService
and starts to create UserContextService
.
UserContextService
also needs LoggerService
, which the framework already has, so the framework can provide the same instance. UserContextService
also needs UserService
, which the framework has yet to create. UserService
has no further dependencies, so the framework can simply use new
to instantiate the class and provide the instance to the UserContextService
constructor.
当 Angular 新建 AppComponent
时,依赖注入框架先创建一个 LoggerService
的实例,然后创建 UserContextService
实例。
UserContextService
需要框架已经创建好的 LoggerService
实例和尚未创建的 UserService
实例。
UserService
没有其它依赖,所以依赖注入框架可以直接 new
一个实例。
The parent AppComponent
doesn't need to know about the dependencies of dependencies.
Declare what's needed in the constructor (in this case LoggerService
and UserContextService
)
and the framework resolves the nested dependencies.
父组件 AppComponent
不需要了解这些依赖的依赖。
只要在构造函数中声明自己需要的依赖即可(这里是 LoggerService
和 UserContextService
),框架会帮你解析这些嵌套的依赖。
When all dependencies are in place, AppComponent
displays the user information.
当所有的依赖都就位之后,AppComponent
就会显示该用户的信息。

{@a service-scope}
Limit service scope to a component subtree
An Angular application has multiple injectors, arranged in a tree hierarchy that parallels the component tree. Each injector creates a singleton instance of a dependency. That same instance is injected wherever that injector provides that service. A particular service can be provided and created at any level of the injector hierarchy, which means that there can be multiple instances of a service if it is provided by multiple injectors.
Angular 应用程序有多个依赖注入器,组织成一个与组件树平行的树状结构。 每个注入器都会创建依赖的一个单例。在所有该注入器负责提供服务的地方,所提供的都是同一个实例。 可以在注入器树的任何层级提供和建立特定的服务。这意味着,如果在多个注入器中提供该服务,那么该服务也就会有多个实例。
Dependencies provided by the root injector can be injected into any component anywhere in the application. In some cases, you might want to restrict service availability to a particular region of the application. For instance, you might want to let users explicitly opt in to use a service, rather than letting the root injector provide it automatically.
由根注入器提供的依赖可以注入到应用中任何地方的任何组件中。 但有时候你可能希望把服务的有效性限制到应用程序的一个特定区域。 比如,你可能希望用户明确选择一个服务,而不是让根注入器自动提供它。
You can limit the scope of an injected service to a branch of the application hierarchy
by providing that service at the sub-root component for that branch.
This example shows how to make a different instance of HeroService
available to HeroesBaseComponent
by adding it to the providers
array of the @Component()
decorator of the sub-component.
通过在组件树的子级根组件中提供服务,可以把一个被注入服务的作用域局限在应用程序结构中的某个分支中。
这个例子中展示了为子组件和根组件 AppComponent
提供服务的相似之处,它们的语法是相同的。
这里通过列入 providers
数组,在 HeroesBaseComponent
中提供了 HeroService
:
When Angular creates HeroesBaseComponent
, it also creates a new instance of HeroService
that is visible only to that component and its children, if any.
当 Angular 新建 HeroBaseComponent
的时候,它会同时新建一个 HeroService
实例,该实例只在该组件及其子组件(如果有)中可见。
You could also provide HeroService
to a different component elsewhere in the application.
That would result in a different instance of the service, living in a different injector.
也可以在应用程序别处的不同的组件里提供 HeroService
。这样就会导致在不同注入器中存在该服务的不同实例。
Examples of such scoped HeroService
singletons appear throughout the accompanying sample code,
including HeroBiosComponent
, HeroOfTheMonthComponent
, and HeroesBaseComponent
.
Each of these components has its own HeroService
instance managing its own independent collection of heroes.
这个例子中,局部化的 HeroService
单例,遍布整份范例代码,包括 HeroBiosComponent
、HeroOfTheMonthComponent
和 HeroBaseComponent
。
这些组件每个都有自己的 HeroService
实例,用来管理独立的英雄库。
{@a multiple-service-instances}
Multiple service instances (sandboxing)
多个服务实例(sandboxing)
Sometimes you want multiple instances of a service at the same level of the component hierarchy.
在同一个级别的组件树里,有时需要一个服务的多个实例。
A good example is a service that holds state for its companion component instance. You need a separate instance of the service for each component. Each service has its own work-state, isolated from the service-and-state of a different component. This is called sandboxing because each service and component instance has its own sandbox to play in.
一个用来保存其伴生组件的实例状态的服务就是个好例子。 每个组件都需要该服务的单独实例。 每个服务有自己的工作状态,与其它组件的服务和状态隔离。这叫做沙箱化,因为每个服务和组件实例都在自己的沙箱里运行。
{@a hero-bios-component}
In this example, HeroBiosComponent
presents three instances of HeroBioComponent
.
在这个例子中,HeroBiosComponent
呈现了 HeroBioComponent
的三个实例。
Each HeroBioComponent
can edit a single hero's biography.
HeroBioComponent
relies on HeroCacheService
to fetch, cache, and perform other persistence operations on that hero.
每个 HeroBioComponent
都能编辑一个英雄的生平。HeroBioComponent
依赖 HeroCacheService
服务来对该英雄进行读取、缓存和执行其它持久化操作。
Three instances of HeroBioComponent
can't share the same instance of HeroCacheService
,
as they'd be competing with each other to determine which hero to cache.
这三个 HeroBioComponent
实例不能共享同一个 HeroCacheService
实例。否则它们会相互冲突,争相把自己的英雄放在缓存里面。
Instead, each HeroBioComponent
gets its own HeroCacheService
instance
by listing HeroCacheService
in its metadata providers
array.
它们应该通过在自己的元数据(metadata)providers
数组里面列出 HeroCacheService
, 这样每个 HeroBioComponent
就能拥有自己独立的 HeroCacheService
实例了。
The parent HeroBiosComponent
binds a value to heroId
.
ngOnInit
passes that ID to the service, which fetches and caches the hero.
The getter for the hero
property pulls the cached hero from the service.
The template displays this data-bound property.
父组件 HeroBiosComponent
把一个值绑定到 heroId
。ngOnInit
把该 id
传递到服务,然后服务获取和缓存英雄。hero
属性的 getter 从服务里面获取缓存的英雄,并在模板里显示它绑定到属性值。
Find this example in live code
and confirm that the three HeroBioComponent
instances have their own cached hero data.
到在线例子中找到这个例子,确认三个 HeroBioComponent
实例拥有自己独立的英雄数据缓存。

{@a qualify-dependency-lookup}
Qualify dependency lookup with parameter decorators
使用参数装饰器来限定依赖查找方式
When a class requires a dependency, that dependency is added to the constructor as a parameter. When Angular needs to instantiate the class, it calls upon the DI framework to supply the dependency. By default, the DI framework searches for a provider in the injector hierarchy, starting at the component's local injector of the component, and if necessary bubbling up through the injector tree until it reaches the root injector.
-
The first injector configured with a provider supplies the dependency (a service instance or value) to the constructor.
-
If no provider is found in the root injector, the DI framework returns null to the constructor.
There are a number of options for modifying the default search behavior, using parameter decorators on the service-valued parameters of a class constructor.
{@a optional}
Make a dependency @Optional
and limit search with @Host
Dependencies can be registered at any level in the component hierarchy.
When a component requests a dependency, Angular starts with that component's injector
and walks up the injector tree until it finds the first suitable provider.
Angular throws an error if it can't find the dependency during that walk.
In some cases, you need to limit the search or accommodate a missing dependency.
You can modify Angular's search behavior with the @Host
and @Optional
qualifying
decorators on a service-valued parameter of the component's constructor.
-
The
@Optional
property decorator tells Angular to return null when it can't find the dependency. -
The
@Host
property decorator stops the upward search at the host component. The host component is typically the component requesting the dependency. However, when this component is projected into a parent component, that parent component becomes the host. The following example covers this second case.
These decorators can be used individually or together, as shown in the example.
This HeroBiosAndContactsComponent
is a revision of HeroBiosComponent
which you looked at above.
Focus on the template:
注意看模板:
Now there's a new <hero-contact>
element between the <hero-bio>
tags.
Angular projects, or transcludes, the corresponding HeroContactComponent
into the HeroBioComponent
view,
placing it in the <ng-content>
slot of the HeroBioComponent
template.
在 <hero-bio>
标签中是一个新的 <hero-contact>
元素。Angular 就会把相应的 HeroContactComponent
投影(transclude)进 HeroBioComponent
的视图里,
将它放在 HeroBioComponent
模板的 <ng-content>
标签槽里。
The result is shown below, with the hero's telephone number from HeroContactComponent
projected above the hero description.
从 HeroContactComponent
获得的英雄电话号码,被投影到上面的英雄描述里,就像这样:

Here's HeroContactComponent
, which demonstrates the qualifying decorators.
Focus on the constructor parameters.
The @Host()
function decorating the heroCache
constructor property ensures that
you get a reference to the cache service from the parent HeroBioComponent
.
Angular throws an error if the parent lacks that service, even if a component higher
in the component tree includes it.
@Host()
函数是 heroCache
属性的装饰器,确保从其父组件 HeroBioComponent
得到一个缓存服务。如果该父组件不存在这个服务,Angular 就会抛出错误,即使组件树里的再上级有某个组件拥有这个服务,Angular 也会抛出错误。
A second @Host()
function decorates the loggerService
constructor property.
The only LoggerService
instance in the app is provided at the AppComponent
level.
The host HeroBioComponent
doesn't have its own LoggerService
provider.
另一个 @Host()
函数是属性 loggerService
的装饰器。
在本应用程序中只有一个在 AppComponent
级提供的 LoggerService
实例。
该宿主 HeroBioComponent
没有自己的 LoggerService
提供商。
Angular throws an error if you haven't also decorated the property with @Optional()
.
When the property is marked as optional, Angular sets loggerService
to null and the rest of the component adapts.
如果没有同时使用 @Optional()
装饰器的话,Angular 就会抛出错误。多亏了 @Optional()
,Angular 把 loggerService
设置为 null,并继续执行组件而不会抛出错误。
Here's HeroBiosAndContactsComponent
in action.
下面是 HeroBiosAndContactsComponent
的执行结果:

If you comment out the @Host()
decorator, Angular walks up the injector ancestor tree
until it finds the logger at the AppComponent
level.
The logger logic kicks in and the hero display updates
with the "!!!" marker to indicate that the logger was found.
如果注释掉 @Host()
装饰器,Angular 就会沿着注入器树往上走,直到在 AppComponent
中找到该日志服务。日志服务的逻辑加入进来,更新了英雄的显示信息,这表明确实找到了日志服务。

If you restore the @Host()
decorator and comment out @Optional
,
the app throws an exception when it cannot find the required logger at the host component level.
EXCEPTION: No provider for LoggerService! (HeroContactComponent -> LoggerService)
Supply a custom provider with @Inject
Using a custom provider allows you to provide a concrete implementation for implicit dependencies, such as built-in browser APIs. The following example uses an InjectionToken
to provide the localStorage browser API as a dependency in the BrowserStorageService
.
The factory
function returns the localStorage
property that is attached to the browser window object. The Inject
decorator is a constructor parameter used to specify a custom provider of a dependency. This custom provider can now be overridden during testing with a mock API of localStorage
instead of interactive with real browser APIs.
Modify the provider search with @Self
and @SkipSelf
Providers can also be scoped by injector through constructor parameter decorators. The following example overrides the BROWSER_STORAGE
token in the Component
class providers
with the sessionStorage
browser API. The same BrowserStorageService
is injected twice in the constructor, decorated with @Self
and @SkipSelf
to define which injector handles the provider dependency.
Using the @Self
decorator, the injector only looks at the component's injector for its providers. The @SkipSelf
decorator allows you to skip the local injector and look up in the hierarchy to find a provider that satisfies this dependency. The sessionStorageService
instance interacts with the BrowserStorageService
using the sessionStorage
browser API, while the localStorageService
skips the local injector and uses the root BrowserStorageService
that uses the localStorage
browswer API.
{@a component-element}
Inject the component's DOM element
Although developers strive to avoid it, many visual effects and third-party tools, such as jQuery, require DOM access. As a result, you might need to access a component's DOM element.
偶尔,可能需要访问一个组件对应的 DOM 元素。尽量避免这样做,但还是有很多视觉效果和第三方工具(比如 jQuery)需要访问 DOM。
To illustrate, here's a simplified version of HighlightDirective
from
the Attribute Directives page.
要说明这一点,请在属性型指令HighlightDirective
的基础上,编写一个简化版。
The directive sets the background to a highlight color when the user mouses over the DOM element to which the directive is applied.
当用户把鼠标移到 DOM 元素上时,指令将该元素的背景设置为一个高亮颜色。
Angular sets the constructor's el
parameter to the injected ElementRef
.
(An ElementRef
is a wrapper around a DOM element,
whose nativeElement
property exposes the DOM element for the directive to manipulate.)
Angular 把构造函数参数 el
设置为注入的 ElementRef
,该 ElementRef
代表了宿主的 DOM 元素, 它的 nativeElement
属性把该 DOM 元素暴露给了指令。
The sample code applies the directive's myHighlight
attribute to two <div>
tags,
first without a value (yielding the default color) and then with an assigned color value.
下面的代码把指令的 myHighlight
属性(Attribute)填加到两个 <div>
标签里,一个没有赋值,一个赋值了颜色。
The following image shows the effect of mousing over the <hero-bios-and-contacts>
tag.
下图显示了鼠标移到 <hero-bios-and-contacts>
标签的效果:

{@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. 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.
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.
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.
如果注入器无法根据令牌在自己内部找到对应的提供商,它便将请求移交给它的父级注入器,这个过程不断重复,直到没有更多注入器为止。 如果没找到,注入器就抛出一个错误...除非这个请求是可选的。
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.
{@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.
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.
HeroOfTheMonthComponent
例子示范了一些替代方案,展示了为什么需要它们。
它看起来很简单:一些属性和一个日志输出。

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 to associate a definition object with a DI token.
这段代码的背后有很多值得深入思考的地方。
The providers
array shows how you might use the different provider-definition keys;
useValue
, useClass
, useExisting
, or useFactory
.
{@a usevalue}
Value providers: 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.
使用该技巧来进行运行期常量设置,比如网站的基础地址和功能标志等。 你通常在单元测试中使用值-提供商,用一个假的或模仿的(服务)来取代一个生产环境的服务。
The HeroOfTheMonthComponent
example has two value providers.
HeroOfTheMonthComponent
例子有两个值-提供商。
第一个提供了一个 Hero
类的实例;第二个指定了一个字符串资源:
-
The first provides an existing instance of the
Hero
class to use for theHero
token, rather than requiring the injector to create a new instance withnew
or use its own cached instance. Here, the token is the class itself. -
The second specifies a literal string resource to use for the
TITLE
token. TheTITLE
provider token is not a class, but is instead a special kind of provider lookup key called an injection token, represented by anInjectionToken
instance.
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.
TITLE
提供商的令牌不是一个类。它是一个特别类型的提供商查询键,名叫InjectionToken.
你可以把 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.
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
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.
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 reduces the API to two members:
想象一下如果 LoggerService
有个很大的 API 接口(虽然它其实只有三个方法,一个属性),通过使用 MinimalLogger
类-接口别名,就能成功的把这个 API 接口缩小到只暴露两个成员:
The following example puts MinimalLogger
to use in a simplified version of HeroOfTheMonthComponent
.
现在,在一个简化版的 HeroOfTheMonthComponent
中使用它。
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.
实际上,Angular 确实想把 logger
参数设置为注入器里 LoggerService
的完整版本。只是在之前的提供商注册里使用了 useClass
,
所以该完整版本被 DateLoggerService
取代了。
This is illustrated in the following image, which displays the logging date.
在下面的图片中,显示了日志日期,可以确认这一点:

{@a usefactory}
Factory providers: useFactory
The useFactory
provider key lets you create a dependency object by calling a factory function,
as in the following example.
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.
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
.
runnersUpFactory
自身不是提供商工厂函数。真正的提供商工厂函数是 runnersUpFactory
返回的函数。
The provider factory function (returned by runnersUpFactory()
) returns the actual dependency object,
the string of names.
- The function takes a winning
Hero
and aHeroService
as arguments.
这个返回的函数需要一个 Hero
和一个 HeroService
参数。
Angular supplies these arguments from injected values identified by
the two tokens in the deps
array.
- The function returns the string of names, which Angular than injects into
the
runnersUp
parameter ofHeroOfTheMonthComponent
.
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.
{@a tokens}
Provider token alternatives: class interface and '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.
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, 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.
这种用法的类叫做类-接口。它关键的好处是:提供了接口的强类型,能像正常类一样把它当做提供商令牌使用。
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.
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.
注意,只要不实现它,不管添加多少成员,它永远不会增长大小。
{@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
来打破这种循环: