In my main app.ts
I've declared a global provider :
providers: [{provide: Dependency, useValue: createDependency('AppModule provider')}]
(Where createDependency
is just a function that returns a class which has a getName()
method.)
I also have a components :
<my-app-component-3>Hello from 3</my-app-component-3>
Code :
@Component({
selector: 'my-app-component-3',
template: `
<div>Component3:
<ng-content></ng-content>
: <span [innerHTML]="dependency?.getName()"></span>
</div>
`,
})
export class Component3 {
constructor(@Host() @Optional() public dependency: Dependency) {}
}
The result is:
Component3: Hello from 3 :
But I expect the result to be :
Component3: Hello from 3 :AppModule provider
Because basically the app structure is :
<my-app>
<my-app-component-3>
</my-app-component-3>
</my-app>
Question:
Why doesn't @Host()
match the parent provider ?
(which is : providers: [{provide: Dependency, useValue: createDependency('AppModule provider')}]
)
To my knowledge - the injector should seek for a Dependency
in this manner :
So why doesn't it find it ?
Notice
I already know that if I remove @host
- it does reach the top. My question is why adding @host - is not reaching the top - despite the fact thatmy-component3
is under my-app
!!
Check out A curios case of the @Host decorator and Element Injectors in Angular for in-depth explanation of how @Host decorator works and where Element Injectors come into this picture.
In order for it to work you should define dependencies in the in the parent component and using viewProviders:
Here is what the comments inside metadata.ts say:
So basically it says that a host element injector and all injectors above are not used when resolving a dependency. So if your
MyApp
component has the following template:and the resulting components tree look like this:
neither
MyApp
component's injector nor App module injectors are used to resolve dependency for themy-app-component-3
.However, there's the following interesting code in the ProviderElementContext._getDependency that performs one additional check:
which basically checks if the provider is defined in the
viewProviders
and resolves it if found. That's whyviewProviders
work.So, here is the lookup tree:
Usage
This decorator is mostly used for directives to resolve providers from the parent injector within the current component view. Even the unit test is written only to test directives. Here is a real example from the
forms
module how it's decorator is used.Consider this template for the
A
component:NgModel
directive wants to resolve a provider supplied by theform
directive. But if the provider is not available, there's no need to go outside of a current componentA
.So
NgModel
is defined like this:While
form
directive is defined like this:Also, a directive can inject dependencies defined by its hosting component if they are defined with
viewProviders
. For example, ifMyApp
component is defined like this:the
Dependency
will be resolved.