【问题标题】:How to load dynamic components based on a property from object?如何根据对象的属性加载动态组件?
【发布时间】:2019-08-22 20:17:07
【问题描述】:

我正在尝试构建一个可能包含不同组件的卡片列表;例如,我有以下对象数组:

{
   title: 'Title',
   descrption: 'Description',
   template: 'table',
},
{
  title: 'Title',
  descrption: 'Description',
  template: 'chart',
}

我将此数组作为来自服务的响应,然后我需要将每个对象与基于 template 属性的组件匹配,例如,第一项应匹配 TableComponent 和第二个给ChartComponent

我正在尝试遵循有关 Dynamic Component Loading 的 Angular 文档,但我不确定如何告诉方法如何将数组中的每个对象与特定组件匹配。

在我的父组件中,我创建了一个锚点,组件应该使用指令加载:

<ng-template appCheckpointHost></ng-template>

我正在尝试使用示例中显示的ComponentFactoryResolver

loadComponent() {
    const componentFactory = this.componentFactoryResolver.resolveComponentFactory(ChartCheckpointComponent);
    const viewContainerRef = this.checkHost.viewContainerRef;
  }

示例展示了“服务”每三秒运行一次,获取一个随机项,并显示出来的场景;但我想要做的是在父组件加载时获取所有项目,并使用其各自的组件呈现每个项目。

有什么想法可以让它发挥作用吗?

【问题讨论】:

  • 你可以使用 ngSwitch(angular.io/api/common/NgSwitch) 根据模板值渲染组件
  • @ajaiJothi 是的有点想避免ngSwitch;我不知道,但这感觉像是使用动态加载的更有效方式?不过可能是错的,不知道两者的优缺点
  • 您需要在模板或 ts 文件中使用 switch 语句(如果您使用的是 componentFactory) - 因为您需要将组件引用传递给 resolveComponentFactory。我没有看到基于字符串值动态定位组件的方法

标签: javascript angular typescript single-page-application angular8


【解决方案1】:

你可以像这样创建一个字典:

const nameToComponentMap = {
  table: TableComponent,
  chart: ChartComponent 
};

然后只需使用这个字典来确定应该呈现哪个组件,具体取决于您的 items 数组中特定 itemtemplate 属性:

const componentTypeToRender = nameToComponentMap[item.template];
this.componentFactoryResolver.resolveComponentFactory(componentTypeToRender);

【讨论】:

  • 这看起来不错,并且对于多个组件来说,因为它是一个大数组,我应该将this.componentFactoryResolver.resolveComponentFactory(componentTypeToRender); 放在forEach 循环中?听起来对我来说是最明显的方式
  • 是的,这里没有什么难的。您可以根据item.template 遍历数组并传递专用类型
  • 好的,太好了;我的坏习惯是把事情复杂化,非常感谢,这似乎很容易解决它
【解决方案2】:

您可以查看我的博客here

首先我需要创建一个指令来引用我们视图中的模板实例

import { Directive, ViewContainerRef } from "@angular/core";

@Directive({
    selector: "[dynamic-ref]"
})
export class DynamicDirective {
    constructor(public viewContainerRef: ViewContainerRef) {}
}

然后我们像这样简单地将指令放在视图中

 <ng-template dynamic-ref></ng-template>

我们将指令 dynamic-ref 放到 ng-content 中,这样我们就可以让 Angular 知道组件将在哪里渲染

接下来我将创建一个服务来生成组件并销毁它

import {
    ComponentFactoryResolver,
    Injectable,
    ComponentRef
} from "@angular/core";
@Injectable()
export class ComponentFactoryService {
    private componentRef: ComponentRef<any>;
    constructor(private componentFactoryResolver: ComponentFactoryResolver) {}

    createComponent(
        componentInstance: any,
        viewContainer: any
    ): ComponentRef<any> {
        const componentFactory = this.componentFactoryResolver.resolveComponentFactory(
            componentInstance
        );
        const viewContainerRef = viewContainer.viewContainerRef;
        viewContainerRef.clear();
        this.componentRef = viewContainerRef.createComponent(componentFactory);
        return this.componentRef;
    }

    destroyComponent() {
        if (this.componentRef) {
            this.componentRef.destroy();
        }
    }
}

最后在我们的组件中我们可以这样调用服务

@ViewChild(DynamicDirective) dynamic: DynamicDirective;

constructor(
        private componentFactoryService: ComponentFactoryService
    ) {

    }

ngOnInit(){
       const dynamiCreateComponent = this.componentFactoryService.createComponent(TestComponent, this.dynamic);
       (<TestComponent>dynamiCreateComponent.instance).data = 1;
       (<TestComponent>dynamiCreateComponent.instance).eventOutput.subscribe(x => console.log(x));
}

ngOnDestroy(){
  this.componentFactoryService.destroyComponent();
}

/////////////////////////////////
export class TestComponent {
  @Input() data;
  @Output() eventOutput: EventEmitter<any> = new EventEmitter<any>();

  onBtnClick() {
    this.eventOutput.emit("Button is click");
  }      
}

【讨论】:

  • 如何修改它以允许同时渲染多个不同的组件?根据我所看到的,我想它必须与ngOnInit() 中的代码有关,也许为我的数组中的每个元素运行这些行?
最近更新 更多