在Angular7应用程序中使用ComponentFactoryResolver是个好主意吗?

A. *_*nis 3 angular-components angular angular7

我想创建一个角度为7(前端)的Web应用程序,以动态加载不同类型的组件,例如本例 https://angular.io/guide/dynamic-component-loader

但是我不确定使用它是否是个好主意ComponentFactoryResolver。我从没用过,也不知道它是否稳定以及性能如何。

如果您知道替代方案,我希望您对此有意见。我不想用native innerHTML

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

  • 标头组件
  • 向导步骤
  • 一个“容器”,现在我使用ng模板显示每个步骤的内容,女巫是一个单独的组件。在某些情况下,复杂的组件
  • 向导按钮(上一个下一个)和最后一步中的操作按钮(如保存等)

这些步骤是动态的。根据某些业务逻辑,可以弹出或删除先前步骤中用户输入的内容

我目前的实现方式如下,
我将仅向您展示我正在使用的部分,以使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;
  }
}
Run Code Online (Sandbox Code Playgroud)

谢谢!!

Eli*_*nen 8

作为对先前答案的补充,为了更好地比较这两种方法,可能值得添加一些有关每种情况下发生的情况的详细信息。

使用FactoryResolver服务“创建”组件的步骤

  1. 使用该resolveComponentFactory() 方法实例化一个组件类:该方法将组件类型作为参数,并查找相应的“组件工厂”。
    注意:组件工厂是 Angular 为每个声明的组件创建的类,目的是实例化新组件
  2. 使用类的createComponent() 方法将新组件“附加”到视图ViewContainerRef

有关信息:https : //angular.io/guide/dynamic-component-loader#resolving-components

结构指令( ngIf, ngSwitch...) “创建”组件时应用的步骤:

  1. 该指令使用提供的模板创建嵌入视图。为此,它还使用ViewContainerRef类( createEmbeddedView()方法)。
  2. 如果这个视图包含一个组件选择器,Angular 会实例化一个新的组件类,也使用相应的工厂,它将被附加到视图中。

=> 这两种方法大致经过相同的步骤(实际上“结构指令”方法添加了一个额外的步骤,即创建嵌入式视图,我认为可以忽略不计)。

因此,在我看来,从两个选项中选择一个最有价值的原因是用例,我将其总结如下:

结构指令( ngIf, ngSwitch...):

  • 当组件很少时很有用

FactoryResolver 服务:

  • 避免了很长的组件列表(如上一个答案中所述)
  • 更好的关注点分离(模板或父组件可能不需要知道可能被实例化的所有组件的列表)
  • 需要延迟加载动态组件(我推荐这个以获取更多信息:https://blog.angularindepth.com/here-is-what-you-need-to-know-about-dynamic-components-in-angular-ac1e96167f9e )


Ale*_*esD 7

是的,最好使用,ComponentFactoryResolver这就是为什么它在官方文档中。自Angular 2起它就位于内部,它是稳定的。对性能没有重大影响。

许多Angular库在内部也使用它,而Angular Material库也是如此。检查Component Development Kit(CDK)内Portal及其在GitHub中的源代码,您可以其中看到它用于在其中显示动态内容。

关于您的问题,NgSwitch使用或做组件是更好还是ComponetFactoryResolver很难回答,因为这取决于您要执行的操作,而您并未解释具体情况。我要说的是,在大多数情况下,您应该使用,ComponentFactoryResolver因为它允许您动态添加任何组件,并且没有一个大组件,NgSwitch而所有可能的动态组件都很大。仅在动态组件数量很少并且不希望添加新组件的情况下,使用创建它们可能更容易NgSwitch