Suppose we have the following:
@Directive({ selector: "[appSome]" })
export class SomeDirective {
public constructor(
private viewContainerRef: ViewContainerRef,
private injector: Injector,
) {
console.log(`injector === viewContainerRef.injector: ${injector === viewContainerRef.injector}`);
console.log(`injector === viewContainerRef.parentInjector: ${injector === viewContainerRef.parentInjector}`);
console.log(`viewContainerRef.injector === viewContainerRef.parentInjector: ${viewContainerRef.injector === viewContainerRef.parentInjector}`);
}
}
What is the difference between those 3 injectors?
this.injectorthis.viewContainerRef.injectorthis.viewContainerRef.parentInjectorIn the test above they all are different instances.
First of all, the Injector you're getting in constructor is so-called Merge Injector.
Here is it's definition:
class Injector_ implements Injector {
constructor(private view: ViewData, private elDef: NodeDef|null) {}
...
}
Angular just takes view data and node definition and can instantiate Injector instance whenever it's demanded via createInjector function:
export function createInjector(view: ViewData, elDef: NodeDef): Injector {
return new Injector_(view, elDef);
}
Now let come back to your directive:
SomeDirective
|
deps
/ \
Injector ViewContainer
To create directive instance Angular resolves dependenies through dedicated function resolveDep
export function resolveDep(view, elDef) {
...
case ViewContainerRefTokenKey:
return asElementData(searchView, elDef.nodeIndex).viewContainer;
...
case InjectorRefTokenKey:
return createInjector(searchView, elDef);
...
}
Let's assume you have a component like:
@Component({
selector: 'my-app',
template: '<h2 appSome>Hello</h2>'
})
export class AppComponent {}
In this case:
SomeDirective
|
deps
/ \
Injector ViewContainer
|| ||
\/ \/
resolveDep(AppComponent view, h2 elDef) resolveDep(AppComponent view, h2 elDef)
|| ||
\/ \/
createInjector viewContainerRef (AppComponent view, h2 elDef)
(created early)
||
\/
new Injector(AppComponent view, h2 elDef)
ViewContainerRef instance was created early during view node creation. Since you require ViewContainerRef through DI Angular marks h2 node with special flag and this way it can instantiate ViewContainerRef and store this instance in h2 node data.
if (nodeDef.flags & 16777216 /* EmbeddedViews */) {
nodeData.viewContainer = createViewContainerData(view, nodeDef, nodeData);
}
where createViewContainerData:
export function createViewContainerData(
view: ViewData, elDef: NodeDef, elData: ElementData): ViewContainerData {
return new ViewContainerRef_(view, elDef, elData);
}
So what we have here: Injector and ViewContainer that point to the same view and the same elDef.
Now let's look at ViewContainerRef definition:
class ViewContainerRef_ implements ViewContainerData {
...
constructor(private _view: ViewData, private _elDef: NodeDef, private _data: ElementData) {}
...
get injector(): Injector { return new Injector_(this._view, this._elDef); }
get parentInjector(): Injector {
let view = this._view;
let elDef = this._elDef.parent;
while (!elDef && view) {
elDef = viewParentEl(view);
view = view.parent !;
}
return view ? new Injector_(view, elDef) : new Injector_(this._view, null);
}
...
}
injector === viewContainerRef.injector => fail
Because viewContainerRef.injector getter creates new instance of Injector with the same view and elDef.
So the following is true:
injector.view === viewContainerRef.injector.view
injector.elDef === viewContainerRef.injector.elDef
injector === viewContainerRef.parentInjector => fail
Because parentInjector getter will get new instance of Injector with parent view and parent elDef.
Parent view here is host view and elDef is my-app.
viewContainerRef.injector === viewContainerRef.parentInjector => fail
Should be obvious that they are not equal since point to the different view and elDef too and are created through new operator.
Finally, you can read:
What you always wanted to know about Angular Dependency Injection tree
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With