Angular 动态组件

前言

Angular 动态组件

代码结构

├── app
│ ├── dynamic-component
│ │  ├── dynamic-child.component.ts
│ │  ├── dynamic-child.component.html
│ │  ├── dynamic-parent.component.ts
│ │  └── dynamic-parent.component.html

样式文件省略

<div>
  <p>在父组件中动态调用组件,实现调用子组件的方法以及清空子组件</p>
  <!-- <dynamic-child></dynamic-child> -->
  <ng-template #childContainer></ng-template>
  <button (click)="createChildSon()">添加子组件</button>
  <button *ngIf="childRef" (click)="clearChild()">添加子组件</button>
  <button *ngIf="childRef" (click)="callChildSonFn()">调用子组件方法</button>
</div>

dynamic-component/dynamic-parent.component.ts

import { Component, ViewChild, ViewContainerRef, ComponentFactoryResolver, ComponentFactory, ComponentRef } from '@angular/core';
import { DynamicChildComponent } from './dynamic-child.component'
@Component({
  selector: 'dynamic-parent',
  templateUrl: './dynamic-parent.component.html',
  styleUrls: ['./dynamic-parent.component.less']
})
export class DynamicParentComponent{
  constructor(private resolve : ComponentFactoryResolver ){}
  // 参数 { read: ViewContainerRef } 定义查询的类型,这里的 ViewContainerRef 表示一个容器类型,但它不能被 Angular 推断,所以如果是被查询的元素是一个容器类型,那么必需定义,
  // 其它类型则可以不声明,Angular 会自动推断
  @ViewChild("childContainer", { read: ViewContainerRef }) childContainerRef: ViewContainerRef;
  childRef: ComponentRef<DynamicChildComponent>
  createChildSon(){
    this.clearChild ()
    const factory: ComponentFactory<DynamicChildComponent> = this.resolve.resolveComponentFactory(DynamicChildComponent)
    // childRef 保存 createComponent() 方法返回的子组件的引用,可以通过这个了组件引用来调用子组件中的属性或者方法
    this.childRef = this.childContainerRef.createComponent<DynamicChildComponent>(factory);
    // childComponentRef.instance 获取子组件实例
    this.childRef.instance.title =  `创建时间:${Date.now()}` // 设置子组件属性
  }
  callChildSonFn () {
    // 调用子组件方法
    this.childRef.instance.setTitle('这个是子组件实例')
  }
  clearChild () { 
    // 清空子组件
    this.childContainerRef.clear()
  }
}

dynamic-component/dynamic-child.component.html

<div>
  这里是子组件{{title}}
</div>

基于简单明子,这里子组件只添加了一个标签及些许文字。

dynamic-component/dynamic-child.component.ts

import { Component, Input } from '@angular/core';
@Component({
  selector: 'dynamic-child',
  templateUrl: './dynamic-child.component.html',
  styleUrls: ['./dynamic-child.component.less']
})
export class DynamicChildComponent {
  @Input() title: string
  setTitle (str: string){
      this.title = str
  }
}

不过此时点击按钮动态生成组件时会报错:

ERROR Error: No component factory found for DynamicChildComponent. Did you add it to @NgModule.entryComponents?

根据它的提示我们也很容易找到问题的根源。我们在 app.module.ts 中添的 NgModule 中 添加 entryComponents 属性,并引入动态组件:

// ......
@NgModule({
  declarations: [
    AppComponent,
    DynamicParentComponent,
    DynamicChildComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule
  ],
  providers: [],
  entryComponents: [DynamicChildComponent],
  bootstrap: [AppComponent]
})
// ......

如果你有阅读官方的文档你会发现,它的实现方式有些不一样,它是通过多添加个指令层来处理的,不管怎么样,实现套路基本一样。