從最末端開始往回講。
createComponent<C>(componentFactory: ComponentFactory<C>,
index?: number,
injector?: Injector,
projectableNodes?: any[][]): ComponentRef<C>;
透過ViewContainerRef
的createComponent
可以在rumtime建立component。
createComponent
的第一個參數是ComponentFactory
。
injector
可以改變DI結構,當前injector
可以透過@angular/core
的Injector
service得到。
ViewContainerRef
可以透過@angular/core
的ViewContainerRef
service得到。
在NgModule
的entryComponents
列表裡的component,Angular會再載入module時為他們建立component factory。
以下是NgModule
的entryComponents
的註解。
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
。
方法很單純,就是原本的寫法搬進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 {}