Angular动态创建Componnet

·  阅读 135

Basic

angular支持动态创建component,即在运行时动态创建component并将其加载到对应的dom中;而不是像通常情况下使用标签将component固定的添加到某个template中。动态创建组件的过程中涉及到两种component:

  • 将要被动态创建出来的component
  • 作为动态插入component载体的父component

动态创建出的component

动态创建的component与普通的组件基本没有区别,同样是包含template,style等,可以省略selector属性。

如果该组建只用来动态创建,而不使用标签的形似固定的插入到dom中,那么可以省略selector属性。

以下我们创建一个简单的message组件,内容比较简单,采用内联template的写法,该组建接受一个input,并在click时向父组件传递信息:

// message.component.ts
import { Component, Input, Output, EventEmitter } from '@angular/core';

@Component({
  // 此处省略了selector和style
  template: `
    <div (click)='outEve.emit(message+ ‘as output’)'>{{message}}</div>
  `
})

export class MessageComponent {
  @Input() message: string;
  @Output() outEve = new EventEmitter();
}
复制代码

parent component - 即用来动态插入component的父节点

而在父组件中,也就是动态生成的地方,我们要考虑以下几个问题:

1. 在哪里加载组件,或者说生成组件首先需要有一个容器
  • 可以将任何dom元素当作组建的容器,通常情况下建议使用标签,因为该标签不会在dom中添加任何额外的元素,可以将其看作一个隐形的容器

     <ng-template #container></ng-template>
    复制代码
  • 通过@ViewChild装饰器,从模板中获取该容器,它支持read查询条件,可以返回不同的实例。

    如使用{ read: ElementRef }获取元素的ElementRef类型的实例如下图:

    @ViewChild('container', { read: ElementRef }) containerEleRef;
    复制代码

    而此处,需要获取ViewContainerRef类型的实例,如下:

     @ViewChild('container', { read: ViewContainerRef }) container;
    复制代码

    通过ViewContainerRef类可以获取容器视图的访问权,这个容器就是用来加载动态组件的宿主。

2. 如何根据子组件类,在上述容器中生成组件
  • 使用ComponentFactoryResolver类动态加载组件,该类中包含一个resolveComponentFactory方法,该方法能够为组件解析出一个ComponentFactory类。而ComponentFactory类中的create方法能够创建真正的component。或者可以简单的理解为ComponentFactory类是一个知道如何创建component 的对象。

    首先,在构造函数中注入ComponentFactoryResolver类:

    import { ComponentFactoryResolver} from '@angular/core';
    constructor(private factoryResolver: ComponentFactoryResolver) { }
    复制代码

    然后, 通过ComponentFactoryResolver的resolveComponentFactory解析出Message组件的ComponentFactory类,ComponentFactory会为MessageComponent创建一个实例:

    const factory = this.factoryResolver.resolveComponentFactory(MessageComponent);
    复制代码

    然后,调用ViewContainerRef类的createComponent的方法,将创建出来的组件实例添加到模板中,createComponent()方法返回一个引用,指向刚刚加载的组件:

    let componentRef = this.container.createComponent(factory);
    复制代码
3. 如何处理子组件的input和output
  • 可以通过上述组件的引用获取组件实例,并处理其input及output 回调等:

    this.componentRef.instance.message = 'any message';
    this.componentRef.instance.outEve.subscribe(res => console.log(res));
    复制代码
4. 将动态加载的组件添加到NgModule的entryComponents中,确保编译器能够正常的生成工厂类。
@NgModule({
  declarations: [
    ...
  ],
  entryComponents:[
    MessageComponent
  ],
  imports: [
  	MessageComponent,
  	DynamicParentComponent,
   	...
  ],
  providers: [
  ...
  ]
复制代码

Parent container component 完整代码:

// dynamic-parent.component.ts
import { Component, OnInit, ComponentFactoryResolver, ComponentRef, ViewChild, ViewContainerRef, OnDestroy } from '@angular/core';
import { MessageComponent } from './message.component';

@Component({
  selector: 'app-dynamic-parent',
  template: `
    <button (click)="createDynamicComponent('success')">create success message component</button>
    <button (click)="createDynamicComponent('warning')">create warning message component</button>
    <ng-template #container></ng-template>
  `
})
export class DynamicParentComponent implements OnInit, OnDestroy {

  @ViewChild('container', { read: ViewContainerRef }) container;

  private componentRef: ComponentRef<any>;
  constructor(private factoryResolver: ComponentFactoryResolver) { }

  ngOnInit(): void {
  }

  createDynamicComponent(type: string) {
    this.container.clear();
    const factory = this.factoryResolver.resolveComponentFactory(MessageComponent);
    this.componentRef = this.container.createComponent(factory);

    this.componentRef.instance.message = type;
    this.componentRef.instance.outEve.subscribe(res => console.log(res));
  }

  ngOnDestroy() {
    this.componentRef.destroy();
  }
}

复制代码

分类:
前端
标签:
收藏成功!
已添加到「」, 点击更改