Angular – 通过提交表单来动态加载同一组件的多个实例以将组件的新实例添加到显示 [英] Angular – Dynamically load multiple instances of the same component by submitting a form to add a new instance of a component to the display

查看:44
本文介绍了Angular – 通过提交表单来动态加载同一组件的多个实例以将组件的新实例添加到显示的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

问题

我正在尝试动态加载同一组件的多个实例,并在用户提交表单时向屏幕添加/加载一个新组件.这背后的想法是,用户可以提供有关表单的详细信息,然后该表单将显示在所创建的该组件的特定实例上.

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

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

然而事实证明这很困难,目前我在上面的屏幕截图中实现这一目标的方式只是在 app.component.html 中单独显示每个组件,这根本不是动态的并且没有用户控制:

<div class='status-box-container'><app-status-box></app-status-box>//我要显示的组件<app-status-box></app-status-box>//我要显示的组件<app-status-box></app-status-box>//我要显示的组件

我尝试过的事情

我已经学习了一些教程,但上述教程的结果要么不完全符合我的要求,要么根本不起作用,要么对我来说太复杂而无法理解.

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

接下来,我尝试 这个现有的堆栈溢出问题,但我发现这不起作用并且非常复杂,其中大部分我不明白.虽然它非常接近我想要实现的目标.

最后,我尝试关注这个stackblitz我找到,但它不像我想要的那样动态,因为组件的每个实例都被硬编码到不同的变量中,我也无法让它工作.

观察

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

app.component.ts 中的第二部分通常看起来像这样:

@ViewChild('messageContainer', { read: ViewContainerRef, static: true })消息容器:ViewContainerRef;构造函数(私有 cfr:ComponentFactoryResolver){}私有创建组件(compClass){const compFactory = this.cfr.resolveComponentFactory(compClass);返回 this.messageContainer.createComponent(compFactory);;}私有 loadNewComponentList(){this.messages.forEach((msg, idx) =>{this.destroyCompFromList(this.componentList[idx]);const comp = this.createComponent(componentMap[this._crtComp]);(comp.instance as any).message = msg;comp.changeDetectorRef.detectChanges();this.componentList[idx] = comp;});}

这取自 我尝试的第一个教程.

现在我不太明白这段代码做了什么,但从它的角度来看,似乎 ComponentFactoryResolver 将负责创建相同组件的多个实例.而 ViewContainerRef 似乎是 html 模板中使用的.

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

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

谢谢.

解决方案

您走对了.简要总结 Angular 的官方指南,您应该创建一个指令来轻松获取HTML 元素的 ViewContainerRef - 组件将作为其兄弟元素注入.然后在需要时使用 ComponentFactoryResolver 创建组件.

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

@Directive({选择器:'[appDynamicStatusBox]'})导出类 DynamicStatusBoxDirective 实现 OnInit、OnDestroy {@Input() 配置:MyData;componentRef: ComponentRef;构造函数(私有解析器:ComponentFactoryResolver,公共 viewContainerRef:ViewContainerRef){}ngOnInit(): 无效 {const factory = this.resolver.resolveComponentFactory(StatusBoxComponent);this.viewContainerRef.clear();this.componentRef = this.viewContainerRef.createComponent(工厂);this.componentRef.instance.someProperty= this.config.prop1;this.componentRef.instance.someOtherProperty= this.config.prop2;}ngOnDestroy(): 无效 {this.componentRef.destroy();}}

<ng-container *ngFor="let config of myConfigs;让我=索引"appDynamicStatusBox [config]="config[i]"></ng-container>

Problem

I’m trying to dynamically load multiple instances of the same component, with a new component being added/loaded to the screen when a user submits a form. The idea behind this is that a user can provide details on the form that is then displayed on that particular instance of that component that is created.

My initial idea was to have some sort of data structure such as an array or map for key pair values, whereby I could add a new instance of my component alongside the form data to my data structure when the user submits the form. Then angular could somehow display every instance of a component that resides in the data structure like the following image below:

It’s important to note that the form is actually its own separate component that’s in the form of modal dialog. This form dialog is opened when the green Add Status Box button is pressed, then the user can submit the form and (hopefully) be able to create a new instance of the status box component with the data provided from the submitted form.

However this is proving difficult, currently the way I’m achieving this in the screenshot above is by merely displaying each component individually in the app.component.html, which isn’t dynamic at all and has no user control:

<app-header></app-header>
<div class='status-box-container'>
    <app-status-box></app-status-box> //component I’m trying to display
    <app-status-box></app-status-box> //component I’m trying to display
    <app-status-box></app-status-box> //component I’m trying to display
</div>

Things I’ve tried

I’ve followed a few tutorials already but either the outcome of the said tutorial doesn’t quite do what I’m asking, doesn’t work at all or it’s too complicated for me to understand.

First I tried to follow this official guide from the Angular website but this only displays one component at a time and dynamically changes its content every 3 seconds rather than displaying multiple instances of one component at the same time.

Next, I tried to follow this stackblitz from this existing stack overflow question, but I found this to not work and to be very complicated, most of which I didn’t understand. Although it’s quite close to what I’m trying to achieve.

Lastly, I tried to follow this stackblitz I found, but it’s not as dynamic as I want it to be because each instance of the component is hardcoded into different variables, I also couldn't get it to work.

Observations

One thing I have noticed though is that all three approaches have two parts in common. Firstly in the app.component.html they either use <ng-container #messageContainer></ng-container> or <ng-template #messageContainer></ng-template>. To my understanding, this is where the dynamic components would be loaded into the html template.

The second part in app.component.ts usually looks something like this:

@ViewChild('messageContainer', { read: ViewContainerRef, static: true })
messageContainer: ViewContainerRef;

constructor (private cfr: ComponentFactoryResolver) { }

private createComponent (compClass) 
{
  const compFactory = this.cfr.resolveComponentFactory(compClass);
  return this.messageContainer.createComponent(compFactory);;
}

private loadNewComponentList () 
{
  this.messages.forEach((msg, idx) => 
  {
    this.destroyCompFromList(this.componentList[idx]);
    const comp = this.createComponent(componentMap[this._crtComp]);

    (comp.instance as any).message = msg;

    comp.changeDetectorRef.detectChanges();

    this.componentList[idx] = comp;
  });
}

This is taken from the first tutorial I tried.

Now I don’t really understand what this code does, but from looking at it, it seems as though the ComponentFactoryResolver would be responsible for creating multiple instances of identical components. And the ViewContainerRef seems to be what’s used in the html template.

This seems to be the direction I should head in but I don’t understand what these imported classes do, and I’m not even sure if this approach would be suitable for my specific requirements.

I was hoping someone could show me a simple solution to this problem, or at least adapt/simplify the tutorials to meet my specific requirements.

Thanks.

解决方案

You're on the right track. To briefly sum up the official guide from Angular you should create a directive to easily get the ViewContainerRef of an HTML element - the component will be injected as a sibling to this. Then use ComponentFactoryResolver to create your components when needed.

Personally would make directive the responsible for also creating the component since it allows you to easily use it with an *ngFor and separate all the dynamic stuff from the regular components

@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();
    }
}

<ng-container *ngFor="let config of myConfigs; let i=index" appDynamicStatusBox [config]="config[i]"></ng-container>

这篇关于Angular – 通过提交表单来动态加载同一组件的多个实例以将组件的新实例添加到显示的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆