Kindly explain the difference between @Self and @Host.
The angular API documentation gives some idea. But it\'s not clear to me.
The example provided for Self<
Angular resolves dependencies by searching for them within the hierarchy of element injectors starting on the injector for the current element, then moving onto that for the parent element if it's not found there, and so on. If the dependency is still not found, it moves onto the module injectors. If it isn't found there, an error is thrown. https://angular.io/guide/hierarchical-dependency-injection#host
@Self and @Host are modifiers that tell Angular on which injectors it should stop looking for dependencies.
@Self tells Angular that it should only look within the injector on the current element. An important point to note regarding this is that every element has just one injector that is shared by every directive that is attached to it. Thus, in this template snippet:
Assuming that dir-1
corresponds to Directive1, and dir-2
corresponds to Directive2,
if Directive1 registers a provider, then Directive2 will be able to inject that service, and vice-versa.
If a dependency has the @Self modifier, this means that Angular will only look within the current element's injector for a provider. Unless the @Optional modifier is also present, an error will be thrown if it can't find it.
The use-case for @Self is if you want a service to be injected into a directive, or component, only if another directive on the same element provides it. (The directive can obviously supply the service itself, but that seems to make the use of @Self a bit redundant).
https://stackblitz.com/edit/angular-di-test-4-6jxjas?file=src%2Fapp%2Fapp.component.html
Consider this template in app.component.html
Lorem Ipsum..
Let my-directive-alpha
correspond to MyDirectiveAlpha, my-directive-beta
correspond to MyDirectiveBeta, and my-directive-gamma
to MyDirectiveGamma.
When MyDirectiveGamma attempts to inject MehProvider:
constructor(@Self() meh: MehProvider) {
console.log("gamma directive constructor:", meh.name);
}
Both MyDirectiveAlpha and MyDirectiveBeta configure MehProvider within their providers array. If you delete my-directive-beta from the template, you'll get an error saying that Angular can't find MehProvider. If you then remove the @Self decorator from MyDirectiveGamma, Angular will find MehProvider from within the MyDirectiveAlpha. Thus, the @Self modifier restricts Angular to looking at the injector on the current element.
@Host tells Angular that it should stop looking for providers beyond the injector for the current template. For the purposes of this article, I call this the template injector, but Angular's documentation does not use this term. This injector contains those providers from the viewProviders array of the component. A component may also have a providers array, which configures an injector that I will call the component injector.
So for this component:
With this template:
my component
lorem ipsum...
Assuming my-dir-1
corresponds to MyDirective1, and my-dir-2
corresponds to MyDirective2, if MyDirective2 attempts to inject a dependency annotated with the @Host modifier:
constructor(@Host() foo: FooProvider) {
...
}
Then Angular will search through all element injectors up through the tree of elements, but not go beyond the template injector of MyComponent. If the provider is not found, again assuming that the @Optional modifier is not present, then an error will be thrown.
An error will still be thrown even if the provider exists within the component injector because Angular will not search there. Thus we can conclude that the component injector is a level above the template injector.
The use case for @Host is to ensure that the containing component of a directive has control of how a particular service is injected.
https://stackblitz.com/edit/angular-di-host-modifier-proof?file=src%2Fapp%2Fmy-component%2Fmy-component.component.ts
Consider MyComponent:
@Component({
selector: "my-component",
providers: [{provide: FooProvider, useValue: {name: 'FooProvider from providers'}}],
viewProviders: [{provide: FooProvider, useValue: {name: 'FooProvider from view-providers'}}],
template: `
This is my component
Lorem Ipsum...
`,
})
export class MyComponent {}
Let my-directive
correspond to MyDirective.
Given that MyDirective attempts to inject FooProvider and uses the @Host modifier:
constructor(@Host() foo: FooProvider) {
console.log("my directive:", foo.name);
}
The actual instance of FooProvider that is injected is that from within viewProviders array. If we comment out this array, we get an error that tells us Angular cannot find the provider, even though it still exists within the providers array. Thus @Host prevents Angular from looking beyond the template injector of a component for providers.