Angular – 通过提交表单将组件的新实例添加到显示中,动态加载同一组件的多个实例

Sne*_*ike 3 forms multiple-instances angular-components angular angular-dynamic-components

问题更新链接

\n

问题

\n

I\xe2\x80\x99m 尝试动态加载同一组件的多个实例,当用户提交表单时,新组件将添加/加载到屏幕上。其背后的想法是,用户可以提供表单的详细信息,然后将其显示在所创建的组件的特定实例上。

\n

我最初的想法是拥有某种数据结构,例如键对值的数组或映射,这样当用户提交表单时,我可以将组件的新实例与表单数据一起添加到我的数据结构中。然后 Angular 可以以某种方式显示驻留在数据结构中的组件的每个实例,如下图所示:

\n

在此输入图像描述

\n

需要注意的是,该表单实际上是它自己的独立组件,以模式对话框的形式存在。当按下绿色的“添加状态框”按钮时,将打开此表单对话框,然后用户可以提交表单并(希望)能够使用提交的表单提供的数据创建状态框组件的新实例。

\n

然而这被证明是困难的,目前我\xe2\x80\x99m 在上面的屏幕截图中实现这一点的方法是仅在 app.component.html 中单独显示每个组件它根本不是动态的,并且具有无用户控制:

\n
<app-header></app-header>\n<div class=\'status-box-container\'>\n    <app-status-box></app-status-box> //component I\xe2\x80\x99m trying to display\n    <app-status-box></app-status-box> //component I\xe2\x80\x99m trying to display\n    <app-status-box></app-status-box> //component I\xe2\x80\x99m trying to display\n</div>\n
Run Code Online (Sandbox Code Playgroud)\n

我尝试过的事情

\n

我\xe2\x80\x99已经遵循了一些教程,但是所述教程的结果\xe2\x80\x99t完全不符合我\xe2\x80\x99m的要求,\xe2\x80\x99根本不起作用,或者它\xe2\x80\x99 对我来说太复杂了,无法理解。

\n

首先,我尝试遵循Angular 网站上的官方指南,但这一次仅显示一个组件,并且每 3 秒动态更改其内容,而不是同时显示一个组件的多个实例。

\n

接下来,我尝试从这个现有的堆栈溢出问题中跟踪这个 stackblitz,但我发现这不起作用并且非常复杂,其中大部分我不理解\xe2\x80\x99。尽管它\xe2\x80\x99s非常接近我\xe2\x80\x99m试图实现的目标。

\n

最后,我尝试遵循我发现的 stackblitz,但它\xe2\x80\x99s 并不像我希望的那样动态,因为组件的每个实例都被硬编码到不同的变量中,我也无法让它工作。

\n

观察结果

\n

但我注意到的一件事是,所有三种方法都有两个共同点。首先在app.component.html中,他们使用<ng-container #messageContainer></ng-container><ng-template #messageContainer></ng-template>。据我了解,这是将动态组件加载到 html 模板中的地方。

\n

app.component.ts 中的第二部分通常如下所示:

\n
@ViewChild(\'messageContainer\', { read: ViewContainerRef, static: true })\nmessageContainer: ViewContainerRef;\n\nconstructor (private cfr: ComponentFactoryResolver) { }\n\nprivate createComponent (compClass) \n{\n  const compFactory = this.cfr.resolveComponentFactory(compClass);\n  return this.messageContainer.createComponent(compFactory);;\n}\n\nprivate loadNewComponentList () \n{\n  this.messages.forEach((msg, idx) => \n  {\n    this.destroyCompFromList(this.componentList[idx]);\n    const comp = this.createComponent(componentMap[this._crtComp]);\n\n    (comp.instance as any).message = msg;\n\n    comp.changeDetectorRef.detectChanges();\n\n    this.componentList[idx] = comp;\n  });\n}\n
Run Code Online (Sandbox Code Playgroud)\n

这是我尝试的第一个教程中的内容。

\n

现在我不\xe2\x80\x99真正理解这段代码的作用,但从它的角度来看,它似乎ComponentFactoryResolver负责创建相同组件的多个实例。似乎ViewContainerRef是 html 模板中使用的\xe2\x80\x99s。

\n

这似乎是我应该前进的方向,但我不了解这些导入的类的作用,而且我什至不确定这种方法是否适合我的特定要求。

\n

我希望有人可以向我展示一个简单的解决方案来解决这个问题,或者至少调整/简化教程以满足我的特定要求。

\n

谢谢。

\n

小智 5

你走在正确的轨道上。简单总结一下Angular 的官方指南,您应该创建一个指令来轻松获取ViewContainerRefHTML 元素 - 该组件将作为其同级组件注入。然后使用ComponentFactoryResolver在需要时用于创建您的组件。

就个人而言,指令还负责创建组件,因为它允许您轻松地将其与*ngFor常规组件一起使用并将所有动态内容与常规组件分开

@Directive({
    selector: '[appDynamicStatusBox]'
})
export class DynamicStatusBoxDirective implements OnInit, OnDestroy {
    @Input() config: MyData;

    componentRef: ComponentRef<StatusBoxComponent>;

    constructor(private resolver: ComponentFactoryResolver, public viewContainerRef: ViewContainerRef) {}

    ngOnInit(): void {
        const factory = this.resolver.resolveComponentFactory(StatusBoxComponent);
        this.viewContainerRef.clear();
        this.componentRef = this.viewContainerRef.createComponent(factory);
        this.componentRef.instance.someProperty= this.config.prop1;
        this.componentRef.instance.someOtherProperty= this.config.prop2;
    }

    ngOnDestroy(): void {
        this.componentRef.destroy();
    }
}
Run Code Online (Sandbox Code Playgroud)
<ng-container *ngFor="let config of myConfigs; let i=index" appDynamicStatusBox [config]="config[i]"></ng-container>
Run Code Online (Sandbox Code Playgroud)