{ "id": "guide/hierarchical-dependency-injection", "title": "Hierarchical injectors", "contents": "\n\n\n
Injectors in Angular have rules that you can leverage to\nachieve the desired visibility of injectables in your apps.\nBy understanding these rules, you can determine in which\nNgModule, Component or Directive you should declare a provider.
\nThere are two injector hierarchies in Angular:
\nModuleInjector
hierarchyβconfigure a ModuleInjector
\nin this hierarchy using an @NgModule()
or @Injectable()
annotation.ElementInjector
hierarchyβcreated implicitly at each\nDOM element. An ElementInjector
is empty by default\nunless you configure it in the providers
property on\n@Directive()
or @Component()
.ModuleInjector
linkThe ModuleInjector
can be configured in one of two ways:
@Injectable()
providedIn
property to\nrefer to @NgModule()
, or root
.@NgModule()
providers
array.@Injectable()
linkUsing the @Injectable()
providedIn
property is preferable\nto the @NgModule()
providers
\narray because with @Injectable()
providedIn
, optimization\ntools can perform\ntree-shaking, which removes services that your app isn't\nusing and results in smaller bundle sizes.
Tree-shaking is especially useful for a library\nbecause the application which uses the library may not have\na need to inject it. Read more\nabout tree-shakable providers\nin Introduction to services and dependency injection.
\nModuleInjector
is configured by the @NgModule.providers
and\nNgModule.imports
property. ModuleInjector
is a flattening of\nall of the providers arrays which can be reached by following the\nNgModule.imports
recursively.
Child ModuleInjector
s are created when lazy loading other @NgModules
.
Provide services with the providedIn
property of @Injectable()
as follows:
The @Injectable()
decorator identifies a service class.\nThe providedIn
property configures a specific ModuleInjector
,\nhere root
, which makes the service available in the root
ModuleInjector
.
There are two more injectors above root
, an\nadditional ModuleInjector
and NullInjector()
.
Consider how Angular bootstraps the app with the\nfollowing in main.ts
:
The bootstrapModule()
method creates a child injector of\nthe platform injector which is configured by the AppModule
.\nThis is the root
ModuleInjector
.
The platformBrowserDynamic()
method creates an injector\nconfigured by a PlatformModule
, which contains platform-specific\ndependencies. This allows multiple apps to share a platform\nconfiguration.\nFor example, a browser has only one URL bar, no matter how\nmany apps you have running.\nYou can configure additional platform-specific providers at the\nplatform level by supplying extraProviders
using the platformBrowser()
function.
The next parent injector in the hierarchy is the NullInjector()
,\nwhich is the top of the tree. If you've gone so far up the tree\nthat you are looking for a service in the NullInjector()
, you'll\nget an error unless you've used @Optional()
because ultimately,\neverything ends at the NullInjector()
and it returns an error or,\nin the case of @Optional()
, null
. For more information on\n@Optional()
, see the @Optional()
section of this guide.
The following diagram represents the relationship between the\nroot
ModuleInjector
and its parent injectors as the\nprevious paragraphs describe.
While the name root
is a special alias, other ModuleInjector
s\ndon't have aliases. You have the option to create ModuleInjector
s\nwhenever a dynamically loaded component is created, such as with\nthe Router, which will create child ModuleInjector
s.
All requests forward up to the root injector, whether you configured it\nwith the bootstrapModule()
method, or registered all providers\nwith root
in their own services.
@Injectable()
vs. @NgModule()
linkIf you configure an app-wide provider in the @NgModule()
of\nAppModule
, it overrides one configured for root
in the\n@Injectable()
metadata. You can do this to configure a\nnon-default provider of a service that is shared with multiple apps.
Here is an example of the case where the component router\nconfiguration includes\na non-default location strategy\nby listing its provider\nin the providers
list of the AppModule
.
ElementInjector
linkAngular creates ElementInjector
s implicitly for each DOM element.
Providing a service in the @Component()
decorator using\nits providers
or viewProviders
\nproperty configures an ElementInjector
.\nFor example, the following TestComponent
configures the ElementInjector
\nby providing the service as follows:
Note: Please see the\nresolution rules\nsection to understand the relationship between the ModuleInjector
tree and\nthe ElementInjector
tree.
When you provide services in a component, that service is available via\nthe ElementInjector
at that component instance.\nIt may also be visible at\nchild component/directives based on visibility rules described in the resolution rules section.
When the component instance is destroyed, so is that service instance.
\n@Directive()
and @Component()
linkA component is a special type of directive, which means that\njust as @Directive()
has a providers
property, @Component()
does too.\nThis means that directives as well as components can configure\nproviders, using the providers
property.\nWhen you configure a provider for a component or directive\nusing the providers
property,\nthat provider belongs to the ElementInjector
of that component or\ndirective.\nComponents and directives on the same element share an injector.
When resolving a token for a component/directive, Angular\nresolves it in two phases:
\nElementInjector
hierarchy (its parents)ModuleInjector
hierarchy (its parents)When a component declares a dependency, Angular tries to satisfy that\ndependency with its own ElementInjector
.\nIf the component's injector lacks the provider, it passes the request\nup to its parent component's ElementInjector
.
The requests keep forwarding up until Angular finds an injector that can\nhandle the request or runs out of ancestor ElementInjector
s.
If Angular doesn't find the provider in any ElementInjector
s,\nit goes back to the element where the request originated and looks\nin the ModuleInjector
hierarchy.\nIf Angular still doesn't find the provider, it throws an error.
If you have registered a provider for the same DI token at\ndifferent levels, the first one Angular encounters is the one\nit uses to resolve the dependency. If, for example, a provider\nis registered locally in the component that needs a service,\nAngular doesn't look for another provider of the same service.
\nAngular's resolution behavior can be modified with @Optional()
, @Self()
,\n@SkipSelf()
and @Host()
. Import each of them from @angular/core
\nand use each in the component class constructor when you inject your service.
For a working app showcasing the resolution modifiers that\nthis section covers, see the
Resolution modifiers fall into three categories:
\n@Optional()
@SkipSelf()
@Host()
and @Self()
By default, Angular always starts at the current Injector
and keeps\nsearching all the way up. Modifiers allow you to change the starting\n(self) or ending location.
Additionally, you can combine all of the modifiers except @Host()
and @Self()
and of course @SkipSelf()
and @Self()
.
@Optional()
link@Optional()
allows Angular to consider a service you inject to be optional.\nThis way, if it can't be resolved at runtime, Angular simply\nresolves the service as null
, rather than throwing an error. In\nthe following example, the service, OptionalService
, isn't provided in\nthe service, @NgModule()
, or component class, so it isn't available\nanywhere in the app.
@Self()
linkUse @Self()
so that Angular will only look at the ElementInjector
for the current component or directive.
A good use case for @Self()
is to inject a service but only if it is\navailable on the current host element. To avoid errors in this situation,\ncombine @Self()
with @Optional()
.
For example, in the following SelfComponent
, notice\nthe injected LeafService
in\nthe constructor.
In this example, there is a parent provider and injecting the\nservice will return the value, however, injecting the service\nwith @Self()
and @Optional()
will return null
because\n@Self()
tells the injector to stop searching in the current\nhost element.
Another example shows the component class with a provider\nfor FlowerService
. In this case, the injector looks no further\nthan the current ElementInjector
because it finds the FlowerService
and returns the yellow flower πΌ.
@SkipSelf()
link@SkipSelf()
is the opposite of @Self()
. With @SkipSelf()
, Angular\nstarts its search for a service in the parent ElementInjector
, rather than\nin the current one. So if the parent ElementInjector
were using the value πΏ
(fern)\nfor emoji
, but you had π
(maple leaf) in the component's providers
array,\nAngular would ignore π
(maple leaf) and use πΏ
(fern).
To see this in code, assume that the following value for emoji
is what the parent component were using, as in this service:
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()
:
In this case, the value you'd get for emoji
would be πΏ
(fern), not π
(maple leaf).
@SkipSelf()
with @Optional()
linkUse @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
.
@Host()
link@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:
Since HostComponent
has @Host()
in its constructor, no\nmatter what the parent of HostComponent
might have as a\nflower.emoji
value,\nthe HostComponent
will use πΌ
(yellow flower).
When you provide services in the component class, services are\nvisible within the ElementInjector
tree relative to where\nand how you provide those services.
Understanding the underlying logical structure of the Angular\ntemplate will give you a foundation for configuring services\nand in turn control their visibility.
\nComponents are used in your templates, as in the following example:
\nNote: Usually, you declare the components and their\ntemplates in separate files. For the purposes of understanding\nhow the injection system works, it is useful to look at them\nfrom the point of view of a combined logical tree. The term\nlogical distinguishes it from the render tree (your application\nDOM tree). To mark the locations of where the component\ntemplates are located, this guide uses the <#VIEW>
\npseudo element, which doesn't actually exist in the render tree\nand is present for mental model purposes only.
The following is an example of how the <app-root>
and <app-child>
view trees are combined into a single logical tree:
Understanding the idea of the <#VIEW>
demarcation is especially significant when you configure services in the component class.
@Component()
linkHow you provide services via an @Component()
(or @Directive()
)\ndecorator determines their visibility. The following sections\ndemonstrate providers
and viewProviders
along with ways to\nmodify service visibility with @SkipSelf()
and @Host()
.
A component class can provide services in two ways:
\nproviders
arrayviewProviders
arrayTo understand how the providers
and viewProviders
influence service\nvisibility differently, the following sections build\na providers
and viewProviders
\nin code and a logical tree.
NOTE: In the logical tree, you'll see @Provide
, @Inject
, and\n@NgModule
, which are not real HTML attributes but are here to demonstrate\nwhat is going on under the hood.
@Inject(Token)=>Value
demonstrates that if Token
is injected at\nthis location in the logical tree its value would be Value
.@Provide(Token=Value)
demonstrates that there is a declaration of\nToken
provider with value Value
at this location in the logical tree.@NgModule(Token)
demonstrates that a fallback NgModule
injector\nshould be used at this location.The example app has a FlowerService
provided in root
with an emoji
\nvalue of πΊ
(red hibiscus).
Consider a simple app with only an AppComponent
and a ChildComponent
.\nThe most basic rendered view would look like nested HTML elements such as\nthe following:
However, behind the scenes, Angular uses a logical view\nrepresentation as follows when resolving injection requests:
\nThe <#VIEW>
here represents an instance of a template.\nNotice that each component has its own <#VIEW>
.
Knowledge of this structure can inform how you provide and\ninject your services, and give you complete control of service visibility.
\nNow, consider that <app-root>
simply injects the FlowerService
:
Add a binding to the <app-root>
template to visualize the result:
The output in the view would be:
\nIn the logical tree, this would be represented as follows:
\nWhen <app-root>
requests the FlowerService
, it is the injector's job\nto resolve the FlowerService
token. The resolution of the token happens\nin two phases:
@NgModule()
to delegate the request to.In the example case, the constraints are:
\n<#VIEW>
belonging to <app-root>
and end with <app-root>
.<app-root>
@Component
s\nare special in that they also include their own viewProviders
,\nwhich is why the search starts at <#VIEW>
belonging to <app-root>
.\n(This would not be the case for a directive matched at the same location).AppModule
acts as the fallback injector when the\ninjection token can't be found in the ElementInjector
s.providers
arraylinkNow, in the ChildComponent
class, add a provider for FlowerService
\nto demonstrate more complex resolution rules in the upcoming sections:
Now that the FlowerService
is provided in the @Component()
decorator,\nwhen the <app-child>
requests the service, the injector has only to look\nas far as the <app-child>
's own ElementInjector
. It won't have to\ncontinue the search any further through the injector tree.
The next step is to add a binding to the ChildComponent
template.
To render the new values, add <app-child>
to the bottom of\nthe AppComponent
template so the view also displays the sunflower:
In the logical tree, this would be represented as follows:
\nWhen <app-child>
requests the FlowerService
, the injector begins\nits search at the <#VIEW>
belonging to <app-child>
(<#VIEW>
is\nincluded because it is injected from @Component()
) and ends with\n<app-child>
. In this case, the FlowerService
is resolved in the\n<app-child>
's providers
array with sunflower π». The injector doesn't\nhave to look any further in the injector tree. It stops as soon as it\nfinds the FlowerService
and never sees the πΊ (red hibiscus).
viewProviders
arraylinkUse the viewProviders
array as another way to provide services in the\n@Component()
decorator. Using viewProviders
makes services\nvisible in the <#VIEW>
.
The steps are the same as using the providers
array,\nwith the exception of using the viewProviders
array instead.
For step-by-step instructions, continue with this section. If you can\nset it up on your own, skip ahead to Modifying service availability.
\nThe example app features a second service, the AnimalService
to\ndemonstrate viewProviders
.
First, create an AnimalService
with an emoji
property of π³ (whale):
Following the same pattern as with the FlowerService
, inject the\nAnimalService
in the AppComponent
class:
Note: You can leave all the FlowerService
related code in place\nas it will allow a comparison with the AnimalService
.
Add a viewProviders
array and inject the AnimalService
in the\n<app-child>
class, too, but give emoji
a different value. Here,\nit has a value of πΆ (puppy).
Add bindings to the ChildComponent
and the AppComponent
templates.\nIn the ChildComponent
template, add the following binding:
Additionally, add the same to the AppComponent
template:
Now you should see both values in the browser:
\nThe logic tree for this example of viewProviders
is as follows:
Just as with the FlowerService
example, the AnimalService
is provided\nin the <app-child>
@Component()
decorator. This means that since the\ninjector first looks in the ElementInjector
of the component, it finds the\nAnimalService
value of πΆ (puppy). It doesn't need to continue searching the\nElementInjector
tree, nor does it need to search the ModuleInjector
.
providers
vs. viewProviders
linkTo see the difference between using providers
and viewProviders
, add\nanother component to the example and call it InspectorComponent
.\nInspectorComponent
will be a child of the ChildComponent
. In\ninspector.component.ts
, inject the FlowerService
and AnimalService
in\nthe constructor:
You do not need a providers
or viewProviders
array. Next, in\ninspector.component.html
, add the same markup from previous components:
Remember to add the InspectorComponent
to the AppModule
declarations
array.
Next, make sure your child.component.html
contains the following:
The first two lines, with the bindings, are there from previous steps. The\nnew parts are <ng-content>
and <app-inspector>
. <ng-content>
allows\nyou to project content, and <app-inspector>
inside the ChildComponent
\ntemplate makes the InspectorComponent
a child component of\nChildComponent
.
Next, add the following to app.component.html
to take advantage of content projection.
The browser now renders the following, omitting the previous examples\nfor brevity:
\nThese four bindings demonstrate the difference between providers
\nand viewProviders
. Since the πΆ (puppy) is declared inside the <#VIEW>,\nit isn't visible to the projected content. Instead, the projected\ncontent sees the π³ (whale).
The next section though, where InspectorComponent
is a child component\nof ChildComponent
, InspectorComponent
is inside the <#VIEW>
, so\nwhen it asks for the AnimalService
, it sees the πΆ (puppy).
The AnimalService
in the logical tree would look like this:
The projected content of <app-inspector>
sees the π³ (whale), not\nthe πΆ (puppy), because the\nπΆ (puppy) is inside the <app-child>
<#VIEW>
. The <app-inspector>
can\nonly see the πΆ (puppy)\nif it is also within the <#VIEW>
.
This section describes how to limit the scope of the beginning and\nending ElementInjector
using the visibility decorators @Host()
,\n@Self()
, and @SkipSelf()
.
Visibility decorators influence where the search for the injection\ntoken begins and ends in the logic tree. To do this, place\nvisibility decorators at the point of injection, that is, the\nconstructor()
, rather than at a point of declaration.
To alter where the injector starts looking for FlowerService
, add\n@SkipSelf()
to the <app-child>
@Inject
declaration for the\nFlowerService
. This declaration is in the <app-child>
constructor\nas shown in child.component.ts
:
With @SkipSelf()
, the <app-child>
injector doesn't look to itself for\nthe FlowerService
. Instead, the injector starts looking for the\nFlowerService
at the <app-root>
's ElementInjector
, where it finds\nnothing. Then, it goes back to the <app-child>
ModuleInjector
and finds\nthe πΊ (red hibiscus) value, which is available because the <app-child>
\nModuleInjector
and the <app-root>
ModuleInjector
are flattened into one\nModuleInjector
. Thus, the UI renders the following:
In a logical tree, this same idea might look like this:
\nThough <app-child>
provides the π» (sunflower), the app renders\nthe πΊ (red hibiscus) because @SkipSelf()
causes the current\ninjector to skip\nitself and look to its parent.
If you now add @Host()
(in addition to the @SkipSelf()
) to the\n@Inject
of the FlowerService
, the result will be null
. This is\nbecause @Host()
limits the upper bound of the search to the\n<#VIEW>
. Here's the idea in the logical tree:
Here, the services and their values are the same, but @Host()
\nstops the injector from looking any further than the <#VIEW>
\nfor FlowerService
, so it doesn't find it and returns null
.
Note: The example app uses @Optional()
so the app does\nnot throw an error, but the principles are the same.
@SkipSelf()
and viewProviders
linkThe <app-child>
currently provides the AnimalService
in\nthe viewProviders
array with the value of πΆ (puppy). Because\nthe injector has only to look at the <app-child>
's ElementInjector
\nfor the AnimalService
, it never sees the π³ (whale).
Just as in the FlowerService
example, if you add @SkipSelf()
\nto the constructor for the AnimalService
, the injector won't\nlook in the current <app-child>
's ElementInjector
for the\nAnimalService
.
Instead, the injector will begin at the <app-root>
\nElementInjector
. Remember that the <app-child>
class\nprovides the AnimalService
in the viewProviders
array\nwith a value of πΆ (puppy):
The logical tree looks like this with @SkipSelf()
in <app-child>
:
With @SkipSelf()
in the <app-child>
, the injector begins its\nsearch for the AnimalService
in the <app-root>
ElementInjector
\nand finds π³ (whale).
@Host()
and viewProviders
linkIf you add @Host()
to the constructor for AnimalService
, the\nresult is πΆ (puppy) because the injector finds the AnimalService
\nin the <app-child>
<#VIEW>
. Here is the viewProviders
array\nin the <app-child>
class and @Host()
in the constructor:
@Host()
causes the injector to look until it encounters the edge of the <#VIEW>
.
Add a viewProviders
array with a third animal, π¦ (hedgehog), to the\napp.component.ts
@Component()
metadata:
Next, add @SkipSelf()
along with @Host()
to the constructor for the\nAnimal Service
in child.component.ts
. Here are @Host()
\nand @SkipSelf()
in the <app-child>
\nconstructor :
When @Host()
and SkipSelf()
were applied to the FlowerService
,\nwhich is in the providers
array, the result was null
because\n@SkipSelf()
starts its search in the <app-child>
injector, but\n@Host()
stops searching at <#VIEW>
βwhere there is no\nFlowerService
. In the logical tree, you can see that the\nFlowerService
is visible in <app-child>
, not its <#VIEW>
.
However, the AnimalService
, which is provided in the\nAppComponent
viewProviders
array, is visible.
The logical tree representation shows why this is:
\n@SkipSelf()
, causes the injector to start its search for\nthe AnimalService
at the <app-root>
, not the <app-child>
,\nwhere the request originates, and @Host()
stops the search\nat the <app-root>
<#VIEW>
. Since AnimalService
is\nprovided via the viewProviders
array, the injector finds π¦\n(hedgehog) in the <#VIEW>
.
ElementInjector
use case exampleslinkThe ability to configure one or more providers at different levels\nopens up useful possibilities.\nFor a look at the following scenarios in a working app, see the
Architectural reasons may lead you to restrict access to a service to the application domain where it belongs.\nFor example, the guide sample includes a VillainsListComponent
that displays a list of villains.\nIt gets those villains from a VillainsService
.
If you provided VillainsService
in the root AppModule
\n(where you registered the HeroesService
),\nthat would make the VillainsService
visible everywhere in the\napplication, including the Hero workflows. If you later\nmodified the VillainsService
, you could break something in a\nhero component somewhere.
Instead, you can provide the VillainsService
in the providers
metadata of the VillainsListComponent
like this:
By providing VillainsService
in the VillainsListComponent
metadata and nowhere else,\nthe service becomes available only in the VillainsListComponent
and its sub-component tree.
VillainService
is a singleton with respect to VillainsListComponent
\nbecause that is where it is declared. As long as VillainsListComponent
\ndoes not get destroyed it will be the same instance of VillainService
\nbut if there are multilple instances of VillainsListComponent
, then each\ninstance of VillainsListComponent
will have its own instance of VillainService
.
Many applications allow users to work on several open tasks at the same time.\nFor example, in a tax preparation application, the preparer could be working on several tax returns,\nswitching from one to the other throughout the day.
\nThis guide demonstrates that scenario with an example in the Tour of Heroes theme.\nImagine an outer HeroListComponent
that displays a list of super heroes.
To open a hero's tax return, the preparer clicks on a hero name, which opens a component for editing that return.\nEach selected hero tax return opens in its own component and multiple returns can be open at the same time.
\nEach tax return component has the following characteristics:
\nSuppose that the HeroTaxReturnComponent
had logic to manage and restore changes.\nThat would be a pretty easy task for a simple hero tax return.\nIn the real world, with a rich tax return data model, the change management would be tricky.\nYou could delegate that management to a helper service, as this example does.
The HeroTaxReturnService
caches a single HeroTaxReturn
, tracks changes to that return, and can save or restore it.\nIt also delegates to the application-wide singleton HeroService
, which it gets by injection.
Here is the HeroTaxReturnComponent
that makes use of HeroTaxReturnService
.
The tax-return-to-edit arrives via the @Input()
property, which is implemented with getters and setters.\nThe setter initializes the component's own instance of the HeroTaxReturnService
with the incoming return.\nThe getter always returns what that service says is the current state of the hero.\nThe component also asks the service to save and restore this tax return.
This won't work if the service is an application-wide singleton.\nEvery component would share the same service instance, and each component would overwrite the tax return that belonged to another hero.
\nTo prevent this, configure the component-level injector of HeroTaxReturnComponent
to provide the service, using the providers
property in the component metadata.
The HeroTaxReturnComponent
has its own provider of the HeroTaxReturnService
.\nRecall that every component instance has its own injector.\nProviding 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.
The rest of the scenario code relies on other Angular features and techniques that you can learn about elsewhere in the documentation.\nYou can review it and download it from the
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.
\nConsider a Car component that depends on several services.\nSuppose you configured the root injector (marked as A) with generic providers for\nCarService
, EngineService
and TiresService
.
You create a car component (A) that displays a car constructed from these three generic services.
\nThen you create a child component (B) that defines its own, specialized providers for CarService
and EngineService
\nthat have special capabilities suitable for whatever is going on in component (B).
Component (B) is the parent of another component (C) that defines its own, even more specialized provider for CarService
.
Behind the scenes, each component sets up its own injector with zero, one, or more providers defined for that component itself.
\nWhen you resolve an instance of Car
at the deepest component (C),\nits injector produces an instance of Car
resolved by injector (C) with an Engine
resolved by injector (B) and\nTires
resolved by the root injector (A).
For more information on Angular dependency injection, see the DI Providers and DI in Action guides.
\n\n \n