首页 > 解决方案 > 在 Angular7 应用程序中使用 ComponentFactoryResolver 是个好主意吗?

问题描述

我想创建一个动态加载不同组件的 Angular 7 Web 应用程序,如本官方文档中所示: https ://angular.io/guide/dynamic-component-loader

但我不确定使用ComponentFactoryResolver. 我没用过,不知道稳定不,性能也不知道。

我想对此发表一些意见,如果有人知道任何替代方案。我不想用native innerHTML

我正在尝试使用动态步骤创建自定义和通用向导。这个向导有

这些步骤是动态的。基于一些业务逻辑,例如用户在之前步骤中的输入。

我当前的实现:
我将只显示我使用的部分,ComponentFactoryResolver以使其易于理解和阅读:)

export class WizComponent implements OnInit { 
    
  public wizContentItems: WizContentItem[] = undefined;
  public currentContentItem: WizContentItem = undefined;
  public currentContentItemNumber: number  = -1;

  public currentWizContentComponent: WizContentComponent = undefined;

  private componentRef: any;

  @Output() public onStepChanged = new EventEmitter<StepPosition>();

  private _position: StepPosition = StepPosition.First;

  constructor(private componentFactoryResolver: ComponentFactoryResolver, private viewContainerRef: ViewContainerRef) { }

  public ngOnInit() {
  } 

    public onSelectStep(contentItem: WizContentItem) {
        console.log("step was clicked");
        console.log(contentItem);
    
        if (this.currentContentItem !== undefined &&
          !this.validateStep(this.currentContentItem)) {
          return;
        }
    
        if (this.currentWizContentComponent !== undefined ) {
          this.currentContentItem.stepProgressStatus = this.currentWizContentComponent.stepProgressStatus;
      }
    
        contentItem.stepState = StepState.Active;
        const componentFactory = this.componentFactoryResolver.resolveComponentFactory(contentItem.component);
    
        this.viewContainerRef.clear();
        this.componentRef = this.viewContainerRef.createComponent(componentFactory);
        (<WizContentComponent>this.componentRef.instance).data = contentItem.data;
        (<WizContentComponent>this.componentRef.instance).stepState = contentItem.stepState;
    
        this.currentWizContentComponent = (<WizContentComponent>this.componentRef.instance);
    
        if (this.currentContentItem != null) {
          this.currentContentItem.stepState = StepState.Empty;
        }
    
        this.currentContentItem = contentItem;
        this.currentContentItem.stepState = StepState.Active;
    
        // Get currentContentItemNumber based currentContentItem
        this.currentContentItemNumber = this.wizContentItems.findIndex(wizContentItem => wizContentItem === this.currentContentItem);
    
        this.stepChanged();
      }

   public onNextClick(event: Event) {

    if ((this.currentContentItemNumber + 1) < this.wizContentItems.length) {
      let nextContentItem = this.wizContentItems[this.currentContentItemNumber + 1];
      if (nextContentItem.stepState === StepState.Disabled) {
        nextContentItem = this.getNextActiveItem(this.currentContentItemNumber + 1);
      }
      if (nextContentItem != null) {
        this.onSelectStep(nextContentItem);
      }
    }
  }

  public onPreviousClick(event: Event) {
    if ((this.currentContentItemNumber - 1) >= 0) {
      let previousContentItem = this.wizContentItems[this.currentContentItemNumber - 1];
      if (previousContentItem.stepState === StepState.Disabled) {
        previousContentItem = this.getPreviousActiveItem(this.currentContentItemNumber - 1);
      }
      if (previousContentItem !== null) {
        this.onSelectStep(previousContentItem);
      }
    }
  }

  public getCurrentStepPosition(): StepPosition {
    return this._position;
  }

  private validateStep(contentItem: WizContentItem): boolean {
    return (<WizContentImplComponent>this.componentRef.instance).isValid();
  }

  private stepChanged(): void {

    this._position = undefined;
    if (this.currentContentItemNumber <= 0) {
      this._position = StepPosition.First;
    } else if (this.currentContentItemNumber >= this.wizContentItems.length) {
      this._position = StepPosition.Last;
    } else {
      this._position = StepPosition.Middle;
    }

    if ((<WizContentComponent>this.componentRef.instance).isSummary) {
      this._position = StepPosition.Summary;
    }
    this.onStepChanged.emit(this._position);
  }

  private getNextActiveItem(itemNumber: number): WizContentItem {

    if (this.wizContentItems.length <= (itemNumber + 1)) {
      return null;
    }

    let nextContentItem = null;
    for (let i = (itemNumber); i < this.wizContentItems.length; i++) {
      if ( this.wizContentItems[i].stepState !== StepState.Disabled ) {
        nextContentItem = this.wizContentItems[i];
        break;
      }
    }

    return nextContentItem;
  }

  private getPreviousActiveItem(itemNumber: number): WizContentItem {
    if ((itemNumber - 1) < 0 ) {
      return null;
    }

    let previousContentItem = null;
    for (let i = (itemNumber - 1); i >= 0; i--) {
      if ( this.wizContentItems[i].stepState !== StepState.Disabled ) {
        previousContentItem = this.wizContentItems[i];
        break;
      }
    }

    return previousContentItem;
  }
}

谢谢!!

标签: angularangular7angular-components

解决方案


是的,使用它很好,ComponentFactoryResolver这就是它在官方文档中的原因。自 Angular 2 以来它就在内部是稳定的。它没有显着的性能影响。

许多 Angular 库在内部也使用它Angular Material library。检查组件开发工具包 (CDK) 中的门户及其在 GitHub中的源代码,您可以在其中看到它用于显示其中的动态内容。

关于您的问题,如果使用 来做NgSwitch或创建组件更好,ComponetFactoryResolver很难回答,因为这取决于您要做什么,并且您没有解释您的场景到底是什么。我会说,在大多数情况下,您应该使用它,ComponentFactoryResolver因为它允许您动态添加任何组件,并且您没有一个大型组件,其中包含NgSwitch所有可能的动态组件。只有在您拥有非常少量的动态组件并且您不希望添加新组件的情况下,使用NgSwitch.


推荐阅读