这是我参与11月更文挑战的第6天,活动详情查看:2021最后一次更文挑战
组件的模板不会永远是固定的,应用可能会在运行期间加载一些新的组件。
确实如此,之前在实现相关业务模块的时候,一般都是固定的UI,在哪个位置添加渲染一个table组件,在哪个位置显示一个form表单。而现在有一个需求,在某个固定的位置,根据不同的需求,展示的组件是不同的,比如:点击了A按钮,需要显示aComponent;点击了B按钮,需要显示bComponent……这就要求在渲染的时候,需要根据不同的操作,动态加载组件。
具体实现该需求需要以下几个步骤:
指令
在添加组件之前,先要定义一个锚点来告诉 Angular 要把组件插入到什么地方。因此首先封装一个指令-dynamicCol。
// 该指令用于动态向行组件中添加列,如input框组件、select组件等
const components = {
button: ButtonComponent,
input: InputComponent
};
@Directive({
selector: '[dynamicCol]',
})
export class DynamicColDirective implements OnInit, OnChanges {
@Input() componentColInfo: any;
// 获取对容器视图的访问权,这个容器就是那些动态加入的组件的宿主。
constructor(
private viewContainerRef: ViewContainerRef,
private resolver: ComponentFactoryResolver,
) {}
public component: ComponentRef<any>;
ngOnChanges() {}
ngOnInit() {
const componentFactory = this.resolver.resolveComponentFactory(components[this.componentColInfo.com]);
this.component = this.viewContainerRef.createComponent(componentFactory);
// 在动态组件创建后,使用动态组件的实例改变动态组件的属性
this.component.instance.componentColInfo = this.componentColInfo;
}
}
在上述指令中,注入了viewContainerRef来获取对容器视图的访问权,这个容器就是那些动态加入的组件的宿主。
ViewContainerRef表示可以将一个或多个视图附着到组件中的容器。
加载组件
<ng-template> 元素就是刚才制作的指令将应用到的地方。在需要动态组件加载的组件中添加该指令,此时Angular 就知道该把组件动态加载到哪里了。
<ng-template> 元素是动态加载组件的最佳选择,因为它不会渲染任何额外的输出。
<div class="example"> <h3>Advertisements</h3> <ng-template dynamicCol></ng-template> </div>
解析组件
使用 ComponentFactoryResolver 来为每个具体的组件解析出一个 ComponentFactory。 然后 ComponentFactory 会为每一个组件创建一个实例。
把 viewContainerRef 指向这个组件的现有实例。
怎么找到这个实例呢? 它指向了 dynamicCol,而这个 dynamicCol就是以前设置过的指令,用来告诉 Angular 该把动态组件插入到什么位置。
要把这个组件添加到模板中,调用 ViewContainerRef 的 createComponent()。
createComponent() 方法返回一个引用,指向这个刚刚加载的组件。 使用这个引用就可以与该组件进行交互,比如设置它的属性或调用它的方法。
【注】对选择器的引用
通常,Angular 编译器会为模板中所引用的每个组件都生成一个 ComponentFactory 类。 但是,对于动态加载的组件,模板中不会出现对它们的选择器的引用。
要想确保编译器照常生成工厂类,就要把这些动态加载的组件添加到 NgModule 的 entryComponents 数组中:
entryComponents: [ HeroJobAdComponent, HeroProfileComponent ],