Angular 2 Components:如何处理循环输入和输出 [英] Angular 2 Components: How to handle circular Inputs and Outputs
问题描述
现状:
我有一个 parent
和一个 child
组件.
I have a parent
and a child
component.
parent
使用它的 @Input
初始化 child
的数据.当用户使用 @Output
编辑数据时,child
通知父级.由于数据是不可变的,child
必须将数据与该通知一起发送.
The parent
initializes the child
's data using its @Input
. And the child
notifies the parent, when the user edited the data using the @Output
. And because the data is immutable, the child
has to send the data along with that notification.
当 parent
收到通知时,它会检查提交的数据是否有效,然后设置它(这也会将新值传播到其他一些子组件).
When the parent
got notified, it will check if the submitted data is valid, and then will set it (this will propagate the new value to some other child components as well).
问题:
在parent
里面设置新数据的时候,当然也会交给刚刚提交数据的child
组件.这将触发 child
的 ngOnChanges
,然后触发 UI 的重绘.
When setting the new data inside the parent
, it will of course also give it to the child
component which just submitted the data. This will trigger the child
's ngOnChanges
, which then triggers a repaint of the UI.
一些背景:
parent
有几个不同的child
组件,它们都依赖相同的myItem
数据,可以编辑这些数据然后通知<代码>父更改.
The parent
has several different child
components, which all rely on the same myItem
data and can edit this data and then notify the parent
on change.
这是代码的简化版本,应该会显示问题.
Here's a simplified version of the code, that should show up the problem.
父组件:
template:
<child [input]="myItem" (output)="onMyItemChange($event)">
code:
ngOnInit() {
this.myItem = getDataViaHTTP();
}
onMyItemChange($event) {
if($event.myItem.isValid()) {
this.myItem = $event.myItem;
}
}
子组件:
template:
<input [(ngModel)]="myItem.name" (ngModelChange)="modelChange($event)">
code:
@Input() input;
@Output() output = new EventEmitter();
myItem;
ngOnChanges(changes) {
this.myItem = changes.input.currentValue.toMutableJS();
}
modelChange($event) {
this.output.emit(this.myItem.toImmutableJS())
}
<小时>
如您所见,child
组件从 @Input
获取数据并使其可变.在将它发送回 parent
之前,它将再次使其不可变.
As you can see, the child
component takes the data from the @Input
and makes it mutable. And before sending it back to the parent
it will make it immutable again.
是否有任何模式可以防止这些循环事件?
Is there any pattern to prevent these circular events?
推荐答案
如果我们坚持使用双向事件触发器,我想不出有什么办法可以打破圈子.尤其是有多个孩子.
I can't think of a way to break the away from the circle if we stick with bi-directionaly event trigger. Especially with multiple children.
我能想到的一种方法是父母和孩子都使用共享数据服务.数据是一劳永逸的,因为各方都在使用相同的数据.
One way I can think of is both parent and children use a share data service. Data is change once and for all, as all parties are using the same data.
globaldata.service.ts
import { Injectable } from '@angular/core';
interface ShareObj {
[id: string]: any;
}
@Injectable()
export class GlobalDataService {
shareObj: ShareObj = {};
}
app.module.ts(假设这是你的根模块)
import { GlobalDataService } from './globaldata.service';
//
// skip ..
//
@NgModule({
//
// skip ..
//
provider:[GlobalDataService]
})
export class AppModule {}
parent.component.ts(假设非 root,多个实例,app.module 的一部分)
template:
<child [parent]="myId"></child>
code:
import { GlobalDataService } from './globaldata.service';
//
// skip ..
//
// use uuid to generate unique id
private uuid = require('node-uuid');
myId = this.uuid.v1();
constructor(private gd: GlobalDataService){
// This can be string, array or object
this.gd.shareObj[myId]='data';
}
child.component.ts
template:
<input [(ngModel)]="gd.shareObj[parent]">
code:
import { GlobalDataService } from './globaldata.service';
//
// skip ..
//
constructor(private gd: GlobalDataService){}
@Input() parent;
方法二——广播队列
使用 RxJs 主题订阅,就像一个广播队列.我实际上已经用示例创建了一个包:
Method 2 - broadcast queue
Use RxJs subject subscription, like a broadcast queue. I have actually created a package with example:
https://github.com/J-Siu/ng2-simple-mq
https://github.com/J-Siu/ng2-simple-mq-示例
想法:
- 父母和所有孩子都将订阅同一个队列
- 在向队列广播时包含发件人 ID,您可以使用订阅 ID 作为发件人 ID,如果您使用我的包,因为它是一个 uuid.
- 回调将检查发件人 ID,如果消息来自自己,则不采取任何行动
父级(假设非 root、多个实例、app.module 的一部分)
import {Component, OnInit} from '@angular/core';
import {SimpleMQ} from 'ng2-simple-mq';
template:
<child [parent]="myId"></child>
code:
export class SomeComponent implements OnInit {
title = 'Some Component';
// use uuid to generate unique id
private uuid = require('node-uuid');
myId = this.uuid.v1();
myItem = {};
constructor(private smq: SimpleMQ) { }
ngOnInit() {
this.smq.subscribe(this.myId, e => this.receiveBroadcast(e));
}
broadcast() {
let msg = {
id: this.myId,
msg: 'some messages or object go here'
};
// Publish to queue name 'this.myId'
this.smq.publish(this.myId, msg);
}
receiveBroadcast(m) {
if (m.id !== this.myId) {
// msg from soneone else, lets do something
this.myItem = m.msg; // Update local data
console.log(m.Id + ' received: ' + m.msg);
}
}
}
儿童
import {Component, Input, OnInit} from '@angular/core';
import {SimpleMQ} from 'ng2-simple-mq';
template:
<input [(ngModel)]="myItem.name" (ngModelChange)="broadcast()">
code:
export class SomeComponent implements OnInit {
title = 'Some Component';
@Input() parent;
// use uuid to generate unique id
private uuid = require('node-uuid');
myId = this.uuid.v1();
myItem = {};
constructor(private smq: SimpleMQ) { }
ngOnInit() {
this.smq.subscribe(parent, e => this.receiveBroadcast(e));
}
broadcast() {
let msg = {
id: this.myId,
msg: this.myItem // send the whole object
};
// Publish to queue name = parent id
this.smq.publish(parent, msg);
}
receiveBroadcast(m) {
if (m.id !== this.myId) {
// msg from soneone else, lets do something
this.myItem = m.msg; // Update local data
console.log(m.Id + ' received: ' + m.msg);
}
}
}
这篇关于Angular 2 Components:如何处理循环输入和输出的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!