Angular - 从父组件向动态子组件发出事件

Posted

技术标签:

【中文标题】Angular - 从父组件向动态子组件发出事件【英文标题】:Angular - emit event from parent component to dynamic child components 【发布时间】:2018-09-30 15:10:50 【问题描述】:

编辑:我采取了不同的方法来解决这个问题:根据接受的答案,在 save() 上的父级中检索动态子组件值。

我正在尝试让父组件发出一个事件/可观察的,以便其子动态组件在侦听此类事件时触发一个动作。

我了解到不能将 @Input()@Output() 装饰器与动态组件一起使用,所以...

这是我的用例大纲:

    动态子组件由一组 4 个输入元素组成。 (完成) 父组件具有添加动态子组件的Add 按钮。此按钮可用于添加尽可能多的动态子组件实例。 (完成) 动态子组件有Delete 按钮来删除它的实例。 (完成) 父组件具有对象数组类型的属性。每个数组项都是一个包含动态组件输入值的对象。 (做) 父组件具有Save 按钮,单击该按钮应将事件发送到动态组件实例,因此每个都可以将其输入值保存为对象。 (即 3 个实例 => [ a:..., b:..., c:..., d:..., ..., ... ];5 个实例 => 5 个数组项等等)。 (做)

我正在尝试从“保存”按钮(上面的#5)发出父事件,并让动态子组件的每个现有实例侦听该事件,然后对父数组执行 .push 操作(# 4)。

这可能不是最佳实践,但尚未设计任何其他方式来确保在发生潜在不确定的随机数量的添加/删除实例操作后为现有动态组件实例保存值。

父组件:

html

...
<button (click)="addDetailRow()" type="button">Add Detail Row</button>
<div #detailContainer></div>
...
<button (click)="save()">Save</button>
...

打字稿

...
detailRowArray: Array<Object>;
...
addDetailRow() 
    let comp = this._cfr.resolveComponentFactory(DetailRowComponent);
    let detailRowComp = this.container.createComponent(comp);

    detailRowComp.instance._ref = detailRowComp;
    detailRowComp.instance.detailRowArray = this.detailRowArray;

save() 
    // TODO: here emit an event/observable

...

动态子组件:

html

<div><input type="text" name="a" [(ngModel)]="detail_item.a" #a></div>
<div><input type="text" name="b" [(ngModel)]="detail_item.b" #b></div>
<div><input type="text" name="c" [(ngModel)]="detail_item.c" #c></div>
<div>
  <input type="text" name="d" [(ngModel)]="detail_item.d" #d>
  <span>
    <button (click)="removeRow()">Remove Row</button>
  </span>
</div>

打字稿

...
detail_item:  [key: string]: any;  = ;
detailRowArray: Array<Object>;
...
removeRow() 
  ...

...
// TODO: trigger this function when parent emits event/observable
saveToParentArray() 
     this.detailRowArray.push(this.detail_item);

附:代码库使用模板驱动的表单,所以不能在其中使用 FormArray 左右,(我刚刚熟悉 angular 2+)。感谢您的关注。

【问题讨论】:

您可以在动态组件上拥有输入/输出,您只需自己连接它们。您的动态组件最好使用输出(EventEmitters 的公共属性)将更改广播到父级。父母应该了解孩子,但反之则不然。 感谢@DanielWSrimpel。关于如何将Input()Output() 连接到动态组件的任何提示? 【参考方案1】:

使用对象而不是数字或字符串变量来更新具有动态值的子组件。

【讨论】:

【参考方案2】:

好的,我采取了不同的解决路径:

在对提供的选项进行一些研究后,我不再希望将事件从父组件发送到动态子组件实例;

相反,我获取了现有的 ViewContainerRefvariable,并使用它的 API 沿着 for 循环检索了动态组件实例,然后我从中检索了 4 个输入元素的集合,然后以编程方式构造了预期的对象,并将其作为项目推送到父级的detailRowArray 数组。问题解决了。

下面是简化版的代码:

  save() 
    // Temporary variables to construct array of arrays
    let parentNodeArray: any[] = [];
    let inputs: any[] = [];
    // loop to retrieve each existing instance of the dynamic component into temp variable
    for (let i = 0; i < this.container.length; i++) 
      let comp: any;
      comp = this.container.get(i);
      parentNodeArray.push(comp.rootNodes[0]);

    
    // loop to retrieve values from the set of 4 input elements per instance
    // into temp array var (mixed with good ol' JS)
    parentNodeArray.forEach((elem: any) => 
      inputs.push( Array.prototype.slice.call( elem.querySelectorAll('input') ).map(node => 
        return node.value; );
      );
    );

【讨论】:

【参考方案3】:

方法一:

数据数组可以存在于父组件和子组件中,接受它作为输入并修改它。

方法二:

理想情况下(如 cmets 中所述)您应该以另一种方式进行。 当用户向子组件输入数据时,我们可以触发从子组件到父组件的事件

方法三:

但是如果你仍然想从父组件中子组件中执行某些操作,你可以调用子组件实例并调用它们。

detailRowComp[1].instance.saveToParentArray()

但这是多余的,不是一个好习惯。选择以上两种之一。

【讨论】:

感谢@Skeptor 的回答。 方法 1:请详细说明如何让数组同时存在于父级和动态子级中?在嵌入式子组件中,我只是在父组件中声明 Input(),然后将其传递给模板。对于动态组件(在添加到父级之前没有父级上下文)这是如何完成的?

以上是关于Angular - 从父组件向动态子组件发出事件的主要内容,如果未能解决你的问题,请参考以下文章

如何从父级向子级发出事件?

从动态创建的子组件向父组件发出事件

Angular 表单:如何动态添加/删除子表单组件

Angular中从父组件到子组件的数据共享

在Vue.js中从父组件执行子方法

Vuejs将动态数据从父组件传递给子组件