Injector vs ViewContainerRef.injector vs ViewContainerRef.parentInjector

时间:2018-06-12 18:49:17

标签: angular typescript dependency-injection

假设我们有以下内容:

@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}`);
    }
}

这3个注射器有什么区别?

  1. this.injector
  2. this.viewContainerRef.injector
  3. this.viewContainerRef.parentInjector
  4. 在上面的测试中,它们都是不同的实例。

1 个答案:

答案 0 :(得分:3)

首先,您在构造函数中获得的 Injector 是所谓的Merge Injector

这是definition

class Injector_ implements Injector {
  constructor(private view: ViewData, private elDef: NodeDef|null) {}
  ...
}

Angular只接受视图数据和节点定义,并且只要通过createInjector函数需要实例化Injector实例:

export function createInjector(view: ViewData, elDef: NodeDef): Injector {
  return new Injector_(view, elDef);
}

现在让我们回到你的指令:

                                 SomeDirective 
                                     |
                                    deps
                               /            \
                        Injector         ViewContainer

创建指令实例Angular通过专用函数resolveDep

解析依赖关系
export function resolveDep(view, elDef) {
  ...
  case ViewContainerRefTokenKey:
     return asElementData(searchView, elDef.nodeIndex).viewContainer;
  ...
  case InjectorRefTokenKey:
     return createInjector(searchView, elDef);
  ...
}

假设您有一个类似的组件:

@Component({
  selector: 'my-app',
  template: '<h2 appSome>Hello</h2>'
})
export class AppComponent {} 

在这种情况下:

                                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实例是在视图节点创建期间创建的。由于您需要ViewContainerRef通过具有特殊标志的DI Angular标记h2节点,因此它可以在h2节点数据中实例化ViewContainerRefstore此实例。

if (nodeDef.flags & 16777216 /* EmbeddedViews */) {
   nodeData.viewContainer = createViewContainerData(view, nodeDef, nodeData);
}

其中createViewContainerData

export function createViewContainerData(
    view: ViewData, elDef: NodeDef, elData: ElementData): ViewContainerData {
  return new ViewContainerRef_(view, elDef, elData);
}

所以我们在这里:InjectorViewContainer指向相同的视图和相同的elDef。

现在让我们看一下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);
  }
  ...
}
<案例1
injector === viewContainerRef.injector  => fail

因为 viewContainerRef.injector getter使用相同的视图和elDef创建Injector的新实例

以下是真的:

injector.view === viewContainerRef.injector.view
injector.elDef === viewContainerRef.injector.elDef
<案例2
injector === viewContainerRef.parentInjector => fail

因为parentInjector getter将获得具有父视图和父elDef的Injector的新实例。

此处的父视图是主视图,elDef是我的应用程序。

<案例3
viewContainerRef.injector === viewContainerRef.parentInjector  => fail

由于指向不同的视图和elDef,并且通过new运算符创建,显然它们不相等。

最后,您可以阅读:

<强> What you always wanted to know about Angular Dependency Injection tree