Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Injector vs ViewContainerRef.injector vs ViewContainerRef.parentInjector

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?

  1. this.injector
  2. this.viewContainerRef.injector
  3. this.viewContainerRef.parentInjector

In the test above they all are different instances.

like image 880
Alexander Abakumov Avatar asked May 22 '26 16:05

Alexander Abakumov


1 Answers

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);
  }
  ...
}

Case 1

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

Case 2

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.

Case 3

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

like image 151
yurzui Avatar answered May 25 '26 06:05

yurzui



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!