Angular - 从父组件向动态子组件发出事件
Angular - emit event from parent component to dynamic child components
编辑:我采取了不同的方法来解决这个问题:根据接受的答案,在 save()
上检索父组件中的动态子组件值。
我正在尝试让父组件发出 event/observable 以便其子动态组件在收听此类内容时触发操作。
我了解到无法将 @Input()
或 @Output()
装饰器与动态组件一起使用,所以...
这是我的用例概述:
- 动态子组件由一组 4 个输入元素组成。 (完成)
- 父组件有
Add
添加动态子组件的按钮。此按钮可用于添加尽可能多的动态子组件实例。 (完成)
- 动态子组件有
Delete
按钮来删除它的实例。 (完成)
- 父组件具有 属性 对象数组类型。每个数组项都是一个包含动态组件输入值的对象。 (要做)
- 父组件有
Save
按钮,单击该按钮时应将事件发送到动态组件实例,因此每个组件都可以将其输入值保存为一个对象。 (即 3 个实例 => [ {a:..., b:..., c:..., d:...}, {...}, {...} ]
;5 个实例 => 5 个数组项等等)。 (要做)
我正在尝试从“保存”按钮(上面的#5)发出父事件,并让动态子组件的每个现有实例侦听该事件,然后对父数组(上面的#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);
}
P.S。代码库使用模板驱动的形式,所以不可能在其中使用 FormArray 等,(我刚刚熟悉 angular 2+)。感谢您的关注。
方法一:
数据数组可以存在于父组件中,子组件接受它作为输入并修改它。
方法二:
理想情况下(如评论中所述)您应该以其他方式进行。
当用户将数据输入子组件时,我们可以触发从子组件到父组件的事件
方法三:
但是如果你还想在父组件的子组件中执行某些操作,你可以只调用子组件实例并调用它们。
detailRowComp[1].instance.saveToParentArray()
但这是多余的,不是一个好的做法。以上两项任选其一。
好的,我选择了不同的解析路径:
在对提供的选项进行一些研究之后,我不再希望将事件从父组件实例发送到动态子组件实例;
相反,我获取了现有的 ViewContainerRef
变量,并在 for
循环中使用它的 API 我检索了动态组件实例,我又从中检索了一组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; });
);
});
使用对象而不是数字或字符串变量来使用动态值更新子组件。
编辑:我采取了不同的方法来解决这个问题:根据接受的答案,在 save()
上检索父组件中的动态子组件值。
我正在尝试让父组件发出 event/observable 以便其子动态组件在收听此类内容时触发操作。
我了解到无法将 @Input()
或 @Output()
装饰器与动态组件一起使用,所以...
这是我的用例概述:
- 动态子组件由一组 4 个输入元素组成。 (完成)
- 父组件有
Add
添加动态子组件的按钮。此按钮可用于添加尽可能多的动态子组件实例。 (完成) - 动态子组件有
Delete
按钮来删除它的实例。 (完成) - 父组件具有 属性 对象数组类型。每个数组项都是一个包含动态组件输入值的对象。 (要做)
- 父组件有
Save
按钮,单击该按钮时应将事件发送到动态组件实例,因此每个组件都可以将其输入值保存为一个对象。 (即 3 个实例 =>[ {a:..., b:..., c:..., d:...}, {...}, {...} ]
;5 个实例 => 5 个数组项等等)。 (要做)
我正在尝试从“保存”按钮(上面的#5)发出父事件,并让动态子组件的每个现有实例侦听该事件,然后对父数组(上面的#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);
}
P.S。代码库使用模板驱动的形式,所以不可能在其中使用 FormArray 等,(我刚刚熟悉 angular 2+)。感谢您的关注。
方法一:
数据数组可以存在于父组件中,子组件接受它作为输入并修改它。
方法二:
理想情况下(如评论中所述)您应该以其他方式进行。 当用户将数据输入子组件时,我们可以触发从子组件到父组件的事件
方法三:
但是如果你还想在父组件的子组件中执行某些操作,你可以只调用子组件实例并调用它们。
detailRowComp[1].instance.saveToParentArray()
但这是多余的,不是一个好的做法。以上两项任选其一。
好的,我选择了不同的解析路径:
在对提供的选项进行一些研究之后,我不再希望将事件从父组件实例发送到动态子组件实例;
相反,我获取了现有的 ViewContainerRef
变量,并在 for
循环中使用它的 API 我检索了动态组件实例,我又从中检索了一组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; });
);
});
使用对象而不是数字或字符串变量来使用动态值更新子组件。