[Anglar]-使用ComponentFactoryResolver动态产生Component

      Angular提供了ComponentFactoryResolver,来协助我们在项目中动态实现Component,而不是把页面需要的所有的组件都写道view里,根据不同的条件来判断显示目标component,当遇到程序现实复杂的需求时非常好用,写出的代码易理解也易维护。今天我们就来看看如何透过ComponentFactoryResolver来动态产生需要的Component。

需求说明

  首先看看以下画面,我们希望点选radio时,可以依照不同的选择切换不同的Component。

  

  先不考虑动态产生,只有3个component时,代码可以很简单通过ngIf来判断component是否要被产生,可读性也不至于太差。

 1 <input type="radio" id="showComponentA" name="showComponent" value="componentA" [(ngModel)]="selectedComponentName"/>
 2 <label for="showComponentA">组件 A</label>
 3 
 4 <input type="radio" id="showComponentB" name="showComponent" value="componentB" [(ngModel)]="selectedComponentName"/>
 5 <label for="showComponentB">组件 B</label>
 6 
 7 <input type="radio" id="showComponentC" name="showComponent" value="componentC" [(ngModel)]="selectedComponentName"/>
 8 <label for="showComponentC">组件 C</label>
 9 
10 <app-component-a *ngIf="selectedComponentName === 'componentA'"></app-component-a>
11 <app-component-b *ngIf="selectedComponentName === 'componentB'"></app-component-b>
12 <app-component-c *ngIf="selectedComponentName === 'componentC'"></app-component-c>
View Code

  不过当选想变得很多,或者可选项通过后端决定等等较为复杂的情况时,代码很容易变得杂乱不好维护。身为优秀的程序员,自然不希望这种情况发生,因此我们需要通过动态的方式,来产生component。也就是今天的主角--ComponentFactoryResolver。

建立DynamicComponentDirective

  首先我们先建立一个directive,并注入ViewContainerRef,ViewContainerRef可以让我们知道目前所在的HTML元素包含的view内容,也可以通过它来改变view的结果(例如:动态的产生component,或者动态的移除component等等)。

  执行命令: ng generate directive DynamicComponent

  

 1 import { Directive, ViewContainerRef } from '@angular/core';
 2 
 3 @Directive({
 4   selector: '[appDynamicComponent]'
 5 })
 6 export class DynamicComponentDirective {
 7 
 8   constructor(public viewcontainerRef: ViewContainerRef) { }
 9 
10 }
View Code

  接着我们需要套用这个directive到需要动态产生component的容器上,我们可以简单地套用ng-template就好,把原来的view代码改成如下

 1 <input type="radio" id="showComponentA" name="showComponent" value="componentA" (change)="displayComponent('componentA')"/>
 2 <label for="showComponentA">组件 A</label>
 3 
 4 <input type="radio" id="showComponentB" name="showComponent" value="componentB" (change)="displayComponent('componentB')"/>
 5 <label for="showComponentB">组件 B</label>
 6 
 7 <input type="radio" id="showComponentC" name="showComponent" value="componentC" (change)="displayComponent('componentC')"/>
 8 <label for="showComponentC">组件 C</label>
 9 
10 <ng-template appDynamicComponent></ng-template>
View Code

  原来的3行components就浓缩成只剩下一行了,同时我们也不用看到一堆脏脏的ngIf了,view的呈现顿时清爽可许多;同时我们替radiobox加入change事件,要决定动态产生哪一个component,而接下来就是动态产生的重头戏。

使用ComponentFactoryResolver动态产生Component

 1 import { Component, ViewChild, ComponentFactoryResolver } from '@angular/core';
 2 import { DynamicComponentDirective } from './dynamic-component.directive';
 3 import { DynamicComponentService } from './dynamic-component.service';
 4 
 5 @Component({
 6   selector: 'app-root',
 7   templateUrl: './app.component.html',
 8   styleUrls: ['./app.component.css'],
 9   providers: [DynamicComponentService]
10 })
11 export class AppComponent {
12   @ViewChild(DynamicComponentDirective) componentHost: DynamicComponentDirective;
13   constructor(private dynamicComponentService: DynamicComponentService,
14               private componentFactoryResolver: ComponentFactoryResolver) {
15   }
16   displayComponent(componentName: string) {
17     const componentFactory = this.componentFactoryResolver.resolveComponentFactory(
18       this.dynamicComponentService.getComponent(componentName)
19     );
20     const viewContainerRef = this.componentHost.viewcontainerRef;
21     viewContainerRef.clear();
22     viewContainerRef.createComponent(componentFactory);
23   }
24 }
View Code

  在这边我们做了这样几件事:

  1.使用ViewChild取得要动态创建component的directive(componentHost)

  2.注入ComponentFactoryResolver

  3.在displayComponent中,使用ComponentFactoryResolver.ResolveComponentFactory来创建一个ComponentFactory

  4.通过componentHost的ViewContainerRef,将内容清空(viewcontainerRef.clear())

  5.通过viewcontainerRef.creatComponent(componentFactory),产生我们需要的component并放入componetHost之中

  至于 如何产生哪一个component,这里我们额外建立了一个DynamicComponentService服务,来决定产生哪一个component

  执行命令: ng generate service DynamicComponent

 1 import { Injectable } from '@angular/core';
 2 import { ComponentAComponent } from './component-a/component-a.component';
 3 import { ComponentBComponent } from './component-b/component-b.component';
 4 import { ComponentCComponent } from './component-c/component-c.component';
 5 
 6 @Injectable({
 7   providedIn: 'root'
 8 })
 9 export class DynamicComponentService {
10   private components = {
11     componentA: ComponentAComponent,
12     componentB: ComponentBComponent,
13     componentC: ComponentCComponent
14   };
15   constructor() { }
16 
17   getComponent(componentName: string) {
18     return this.components[componentName];
19   }
20 }
View Code

在Module加入entryComponents

接下来就是最后一步了,由于我们的Component是动态产生,而不是直接透过View上的selector产生的,为了确保能够产生动态的Component,我们还需要在所属的Module中加入一个entryComponents阵列

 1 import { BrowserModule } from '@angular/platform-browser';
 2 import { NgModule } from '@angular/core';
 3 import { FormsModule } from '@angular/forms';
 4 
 5 import { AppComponent } from './app.component';
 6 import { ComponentAComponent } from './component-a/component-a.component';
 7 import { ComponentBComponent } from './component-b/component-b.component';
 8 import { ComponentCComponent } from './component-c/component-c.component';
 9 import { DynamicComponentDirective } from './dynamic-component.directive';
10 
11 @NgModule({
12   declarations: [
13     AppComponent,
14     ComponentAComponent,
15     ComponentBComponent,
16     ComponentCComponent,
17     DynamicComponentDirective
18   ],
19   imports: [
20     BrowserModule,
21     FormsModule
22   ],
23   providers: [],
24   bootstrap: [AppComponent],
25   entryComponents: [
26     ComponentAComponent,
27     ComponentBComponent,
28     ComponentCComponent
29   ]
30 })
31 export class AppModule { }
View Code

 完整代码地址:https://github.com/1weidong/component-factory-resolver/tree/master

 

基于vue3最新标准,实现后台前端综合解决方案 + 前端技术书籍 原价368, 限时39.9

qq649149488

posted @ 2020-06-08 22:34  进军码农  阅读(2660)  评论(0编辑  收藏  举报