zoukankan      html  css  js  c++  java
  • [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

  • 相关阅读:
    SkyWorking基础:6.2版本安装部署
    ZooKeeper基础:快速部署
    Linux基础:uniq命令总结
    Linux基础:seq命令总结
    JavaScript BOM对象
    JavaScript常用项目(更新至19.11.17)
    JavaScript触发器
    JavaScript选择器和节点操作
    小花梨的取石子游戏【博弈】
    杨辉三角打表
  • 原文地址:https://www.cnblogs.com/wei-dong/p/13066696.html
Copyright © 2011-2022 走看看