Angular学习笔记65:动态创建组件

在使用一个组件的时候。可以通过在组件的元数据的selector中设置的标签来使用。也可以动态的创建一个组件来使用。

通过在组件的元数据的 selector 中设置的标签来使用组件

以可编辑的table(EditTableComponent) 这个组件为例,这个组件的元数据中selector 设置的标签为:“app-edit-table”;

在这里插入图片描述

所以在其他组件中使用这个组件的时候,只需要在组件的模版文件中的模版文件中通过

<app-edit-table></app-edit-table> 

的方式就可以在父组件中创建这个子组件了。

在父组件中动态的创建组件

在动态创建组件的时候,主要用到了Angular中的ViewContainerRef 和 ComponentFactoryResolver 这两个类中的API来实现。

创建一个父组件,在这个父组件中动态创建子组件

使用命令

ng g c dynamic-com

来创建组件

wujiayudeMacBook-Pro:pages wjy$ ng g c dynamic-com
CREATE src/app/pages/dynamic-com/dynamic-com.component.less (0 bytes)
CREATE src/app/pages/dynamic-com/dynamic-com.component.html (30 bytes)
CREATE src/app/pages/dynamic-com/dynamic-com.component.spec.ts (657 bytes)
CREATE src/app/pages/dynamic-com/dynamic-com.component.ts (289 bytes)
UPDATE src/app/pages/pages.module.ts (2471 bytes)

在组件 dynamic-com 中注入 ViewContainerRef

修改组件的模版文件:

<ng-template #dynamic></ng-template>

由于 元素不会渲染出任何额外的输出,所以它是动态加载组件的最佳选择。

修改组件的类文件:

import {
  AfterContentInit,
  Component,
  ComponentFactoryResolver,
  ComponentRef,
  OnInit,
  ViewChild,
  ViewContainerRef
} from '@angular/core';

@Component({
  selector: 'app-dynamic-com',
  templateUrl: './dynamic-com.component.html',
  styleUrls: ['./dynamic-com.component.less']
})
export class DynamicComComponent implements OnInit, AfterContentInit {
  @ViewChild('dynamic', {read: ViewContainerRef}) dynamicCom: ViewContainerRef;

  constructor() {
  }

  ngOnInit() {
  }

  ngAfterContentInit() {
   
  }

}

组件注入了 ViewContainerRef 来获取对容器视图的访问权,这个容器就是那些动态加入的组件的宿主,在这里,动态创建的组件 “EditTableComponent” 就会以 dynamic 为容器,将动态创建的 “EditTableComponent” 的组件加入到其中。

创建两个 editTable 组件的变量;

修改组件的类文件

import {
  AfterContentInit,
  Component,
  ComponentFactoryResolver,
  ComponentRef,
  OnInit,
  ViewChild,
  ViewContainerRef
} from '@angular/core';
import {EditTableComponent} from '../edit-table/edit-table.component';

@Component({
  selector: 'app-dynamic-com',
  templateUrl: './dynamic-com.component.html',
  styleUrls: ['./dynamic-com.component.less']
})
export class DynamicComComponent implements OnInit, AfterContentInit {
  @ViewChild('dynamic', {read: ViewContainerRef}) dynamicCom: ViewContainerRef;
  public comOne: ComponentRef<EditTableComponent>;
  public comTwo: ComponentRef<EditTableComponent>;

  constructor(private comFactoryResolver: ComponentFactoryResolver) {
  }

  ngOnInit() {
  }

  ngAfterContentInit() {
    
  }

}

创建组件

修改类文件

import {
  AfterContentInit,
  Component,
  ComponentFactoryResolver,
  ComponentRef,
  OnInit,
  ViewChild,
  ViewContainerRef
} from '@angular/core';
import {EditTableComponent} from '../edit-table/edit-table.component';

@Component({
  selector: 'app-dynamic-com',
  templateUrl: './dynamic-com.component.html',
  styleUrls: ['./dynamic-com.component.less']
})
export class DynamicComComponent implements OnInit, AfterContentInit {
  @ViewChild('dynamic', {read: ViewContainerRef}) dynamicCom: ViewContainerRef;
  public comOne: ComponentRef<EditTableComponent>;
  public comTwo: ComponentRef<EditTableComponent>;

  constructor(private comFactoryResolver: ComponentFactoryResolver) {
  }

  ngOnInit() {
  }

  ngAfterContentInit() {
    const childComp = this.comFactoryResolver.resolveComponentFactory(EditTableComponent);
    this.comOne = this.dynamicCom.createComponent(childComp);
    this.comOne.instance.dataSet = [{
      key: '999',
      name: '动态创建组件',
      status: 'edit',
      age: 32,
      address: '来自动态的数据'
    }];
    this.comOne.instance.backInfo.subscribe((info) => {
      console.log(info);
    });
    this.comTwo = this.dynamicCom.createComponent(childComp);
    this.comTwo.instance.dataSet = [{
      key: '1000',
      name: '动态创建组件2',
      status: 'edit',
      age: 32,
      address: '来自动态的数据2'
    }];
  }

}


这里通过了 ComponentFactoryResolver 的实例(comFactoryResolver)调用 resolveComponentFactory() 为 EditTableComponent 组件解析出一个 ComponentFactory,然后ComponentFactory 会为组件EditTableComponent创建一个实例。再将ViewContainerRef指向创建的实例,通过调用 ViewContainerRef 的 createComponent()将这个组件添加到模板中。对于创建出来的 comOne 这个组件,可以通过 comOne.instance属性直接访问comOne的 public 型属性,也可以通过 subscribe() 的箭头函数来订阅 comOne 的事件。

最终的效果如下:

在这里插入图片描述

销毁组件

通过代码动态创建出来的组件,可以手动去销毁。只要调用 comOne.destory() 就可以了。

修改模版文件:

<ng-template #dynamic></ng-template>

<button nz-button (click)="handleDestroyCom()">销毁组件</button>

实现handleDestroyCom 方法,修改类文件

import {
  AfterContentInit,
  Component,
  ComponentFactoryResolver,
  ComponentRef,
  OnInit,
  ViewChild,
  ViewContainerRef
} from '@angular/core';
import {EditTableComponent} from '../edit-table/edit-table.component';

@Component({
  selector: 'app-dynamic-com',
  templateUrl: './dynamic-com.component.html',
  styleUrls: ['./dynamic-com.component.less']
})
export class DynamicComComponent implements OnInit, AfterContentInit {
  @ViewChild('dynamic', {read: ViewContainerRef}) dynamicCom: ViewContainerRef;
  public comOne: ComponentRef<EditTableComponent>;
  public comTwo: ComponentRef<EditTableComponent>;

  constructor(private comFactoryResolver: ComponentFactoryResolver) {
  }

  ngOnInit() {
  }

  ngAfterContentInit() {
    const childComp = this.comFactoryResolver.resolveComponentFactory(EditTableComponent);
    this.comOne = this.dynamicCom.createComponent(childComp);
    this.comOne.instance.dataSet = [{
      key: '999',
      name: '动态创建组件',
      status: 'edit',
      age: 32,
      address: '来自动态的数据'
    }];
    this.comOne.instance.backInfo.subscribe((info) => {
      console.log(info);
    });
    this.comTwo = this.dynamicCom.createComponent(childComp);
    this.comTwo.instance.dataSet = [{
      key: '1000',
      name: '动态创建组件2',
      status: 'edit',
      age: 32,
      address: '来自动态的数据2'
    }];
  }

  // 销毁组件按钮回调函数
  handleDestroyCom() {
    this.comOne.destroy();
  }

}

实现的效果如下:

在这里插入图片描述

渲染组件的顺序问题

在 createComponent() 中是可以有第二个参数的,第二个参数是用来控制组件渲染的顺序的。
这个顺序的索引是从 0 开始的,表示新组件的宿主视图要插入到当前容器的哪个位置。 如果没有指定,就把新的视图追加到最后。

上述代码以上传到 GitHub 的DynamicComComponent 组件中可以看到

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值