动态创建组件的应用场景很多,比如动态广告、像浏览器一样动态打开多个页面等等等等
要动态创建组件主要需要用到angular中的ComponentFactoryResolver、ComponentFactory、ViewContainerRef、 ComponentRef这几个类
class ComponentFactoryResolver {
static NULL: ComponentFactoryResolver
resolveComponentFactory<T>(component: Type<T>): ComponentFactory<T>
}
class ViewContainerRef {
get element(): ElementRef
get injector(): Injector
get parentInjector(): Injector
clear(): void
get(index: number): ViewRef|null
get length(): number
createEmbeddedView<C>(templateRef: TemplateRef<C>, context?: C, index?: number): EmbeddedViewRef<C>
createComponent<C>(componentFactory: ComponentFactory<C>, index?: number, injector?: Injector, projectableNodes?: any[][], ngModule?: NgModuleRef<any>): ComponentRef<C>
insert(viewRef: ViewRef, index?: number): ViewRef
move(viewRef: ViewRef, currentIndex: number): ViewRef
indexOf(viewRef: ViewRef): number
remove(index?: number): void
detach(index?: number): ViewRef|null
}
class ComponentRef<C> {
get location(): ElementRef
get injector(): Injector
get instance(): C
get hostView(): ViewRef
get changeDetectorRef(): ChangeDetectorRef
get componentType(): Type<any>
destroy(): void
onDestroy(callback: Function): void
}
假设我们要在组件A中动态创建组件B,那么我们一般会这样做:
1、在组件A中设置一个<ng-template>,并在组件类中获取它。
2、在组件A中导入组件B,导入上面说的那几个类
3、在组件A中动态创建组件B的方法里使用ComponentFactoryResolver的resolveComponentFactory方法解析组件B获得组件B的ComponentFactory,balabalabala...具体如下
4、在所在模块中导入entryComponents: [BComponent]
@NgModule({ ... declarations: [...,BComponent], entryComponents: [BComponent] })
import { Component, OnInit, ViewChild, ComponentRef, ComponentFactory,
ComponentFactoryResolver, ViewContainerRef } from '@angular/core';
import { BComponent } from '../b/b.component';
@Component({
selector: 'app-article',
template: `<ng-template #RefB></ng-template><button (click)="createComponent()">create</button> `
})
export class ArticleComponent implements OnInit {
@ViewChild('RefB', { read: ViewContainerRef }) container: ViewContainerRef;
constructor(
private componentFactoryResolver: ComponentFactoryResolver) { }
ngOnInit() {}
createComponent() {
const componentFactory: ComponentFactory<BComponent> = this.componentFactoryResolver.resolveComponentFactory(BComponent);
const componentRef: ComponentRef<BComponent> = this.container.createComponent(componentFactory);
}
}
至此我们完成了对组件B的基本动态创建方法,我们还可以通过componentRef的instance方法来访问组件B中的属性和方法;
但是当我们不仅仅是希望能动态创建,同时还想要能让动态创建出的组件B能和组件A进行通讯,就需要使用@Input、@Output。这与平时我们所使用的过程有一点区别,因为动态创建出的组件无法绑定属性,则需要在父组件A通过订阅才能实现通讯。
//input
componentRef.instance.type = type;
//output
componentRef.instance.output.subscribe(event => console.log(event));
以前做过一个很复杂的的动态创建的组件交互中的Input需要使用Subject订阅才能实现的,记不清楚了具体是什么场景了,用来作甚么的了