Skip to content

Instantly share code, notes, and snippets.

@yujuiting
Last active November 5, 2016 07:34
Show Gist options
  • Save yujuiting/af8e77246d44b1123db65f27d1e18c70 to your computer and use it in GitHub Desktop.
Save yujuiting/af8e77246d44b1123db65f27d1e18c70 to your computer and use it in GitHub Desktop.

如何在Angular動態載入、建立Component

從最末端開始往回講。

ViewContainerRef#createComponent

createComponent<C>(componentFactory: ComponentFactory<C>,
                   index?: number,
                   injector?: Injector,
                   projectableNodes?: any[][]): ComponentRef<C>;

透過ViewContainerRefcreateComponent可以在rumtime建立component。
createComponent的第一個參數是ComponentFactory

injector可以改變DI結構,當前injector可以透過@angular/coreInjector service得到。

ViewContainerRef可以透過@angular/coreViewContainerRef service得到。


ComponentFactory

NgModuleentryComponents列表裡的component,Angular會再載入module時為他們建立component factory。 以下是NgModuleentryComponents的註解。

Specifies a list of components that should be compiled when this module is defined. For each component listed here, Angular will create a ComponentFactory and store it in the ComponentFactoryResolver.

@NgModule({
  entryComponents: [
    DynamicCreatedComponent
  ]
})

可以透過ComponentFactoryResolver這個service找到component factory。

class {
  constructor(private cfr: ComponentFactoryResolver) {
    cfr.resolveComponentFactory(DynamicCreatedComponent)
  }
}

如果你的環境有多個module,要記得找對module。

ComponentFactory是在載入module時建立的,所以entryComponents裡的東西越多,這module載入時間就越長。 一種處理方式是動態建立NgModule


動態建立NgModule

方法很單純,就是原本的寫法搬進method裡。

import { NgModule } from '@angular/core';

// `NgModule` 同時也是介面
createModule(metadata: NgModule): any {
  let decorator = NgModule(metadata);
  let moduleType = decorator(class {});
  return moduleType
}

之後就可以動態產生module,並透過Compiler compile。

import { Compiler, Injector } from '@angular/core';

class {
  constructor(private compiler: Compiler,
              private injector: Injector) {}

  {
    let moduleType = createModule({
      declarations: [DynamicCreatedComponent],
      entryComponents: [DynamicCreatedComponent]
    });

    /**
     * Compiler提供Async和Sync方法以及要不要為所有component建立component factory
     */
    return this.compiler.compileModuleAsync(moduleType).then( moduleFactory => {
      // 提供當前的injector,可以是NULL,不會壞掉。
      let moduleRef = moduleFactory.create(this.injector);
      /**
       * 兩個方式產生 component factory.
       * 一是加入entryComponents
       * 二是compiler.compileModuleAndAllComponents
       */
      let resolver = moduleRef.componentFactoryResolver;
      let factory = resolver.resolveComponentFactory(DynamicCreatedComponent);
    });
  }
}

NgModule載入後會被compiler緩存,如果你只是單純動態載入module,不需要擔心任何事情。 但如果透過動態建立module並載入,必須自己處理這一塊,不然會遇到declarations衝突。


不負責任範例

import { Component, ViewContainerRef, NgModule, ViewCild,
         ComponentFactoryResolver, ComponentFactory, ComponentRef } from '@angular/core';

@Component({
  selector: 'dynamic-component-outlet',
  template: ''
})
class DynamicComponentOutlet {
  constructor(public viewContainerRef: ViewContainerRef) {}
}

@Component({
  template: 'Dynamic Created'
})
class DynamicCreatedComponent {}

@Component({
  selector: 'app-root',
  template: '<dynamic-component-outlet></dynamic-component-outlet>'
})
class AppComponent {
  @ViewCild(DynamicComponentOutlet)
  outlet: DynamicComponentOutlet;
  constructor(private componentFactoryResolver: ComponentFactoryResolver) {}
  createComponent() {
    let componentFactory: ComponentFactory = this.componentFactoryResolver
                                                 .resolveComponentFactory(DynamicCreatedComponent);
    let componentRef: ComponentRef<DynamicCreatedComponent> = this.outlet
                                                                  .viewContainerRef
                                                                  .createComponent(componentFactory);
  }
}

@NgModule({
  declarations: [
    DynamicComponentOutlet,
    DynamicCreatedComponent,
    AppComponent
  ],
  entryComponents: [
    DynamicCreatedComponent
  ],
  bootstrap: [AppComponent]
})
class AppModule {}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment