Angular动态组件加载

773 阅读3分钟

这是我参与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 ],