我一直在为我的应用程序使用Observable作为数据服务,最近遇到了“错误”。我有一个主要的BehaviorSubject对象,用于保存我的主模型,并使用 distinctUntilChanged 在主模型的嵌套属性上创建其他嵌套的Observables(以便我可以轻松地仅将更改订阅到那些子属性)。>
我的服务公开了更新嵌套数据的方法。我能够毫无问题地更新数据,但是问题是嵌套的Observable不会触发detail $订阅的更新(请参阅下面的TAP2评论)。
我认为这是由于我从主BehaviorSubject获取值,并在发出下一个值之前直接对其进行了更新。我知道如果我复制对象然后更新并发出它可以正常工作,但是我不确定这是否是实现它的最佳方法。
是否有其他(更好)的实现方式?我真的更喜欢用一个BehaviorSubject来保存我的数据,这是最好的方法,我可以弄清楚如何做到这一点而不必深深地克隆对象。
我用工作样本创建了一个堆叠闪电战: https://stackblitz.com/edit/components-issue-trqau3
这是我的代码:
模型
export interface MyModel {
name: string;
detail: MyModelDetail;
}
export interface MyModelDetail {
brand: string;
items: any[];
}
服务
export class MyServiceService {
private _data = new BehaviorSubject<MyModel>(null);
public data$ = this._data.asObservable();
public detail$ = this.data$
.pipe(
map(a => a.detail),
tap(a => console.log('tap1', JSON.stringify(a))),
distinctUntilChanged((a, b) => {
let dist = JSON.stringify(a) === JSON.stringify(b);
return dist;
}),
tap(a => console.log('tap2', JSON.stringify(a))), //TAP 2 doesn't get called
);
public init(data: MyModel) {
this._data.next(data);
}
public updateDetail(detailData: MyModelDetail) {
let data = this._data.getValue();
data.detail.items.push(...detailData.items);
this._data.next(data); //Tap 2 doesn't get called
}
}
组件
export class AppComponent {
data$: Observable<MyModel> = this.service.data$;
detail$: Observable<MyModelDetail> = this.service.detail$;
constructor(private service: MyServiceService) {
service.init({
name: 'test',
detail: {
brand: 'apple',
items: ['imac']
}
});
}
updateDetail() {
this.service.updateDetail({
brand: 'google',
items: ['iphone']
});
}
}
谢谢!
答案 0 :(得分:1)
数据指向相同的数据对象引用,因此,如果您更新了它,它也会更新存储在from PySide2 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(803, 616)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.verticalLayout = QtWidgets.QVBoxLayout(self.centralwidget)
self.verticalLayout.setObjectName("verticalLayout")
self.splitter_image_list = QtWidgets.QSplitter(self.centralwidget)
self.splitter_image_list.setOrientation(QtCore.Qt.Horizontal)
self.splitter_image_list.setObjectName("splitter_image_list")
self.gv_wing_image = QtWidgets.QGraphicsView(self.splitter_image_list)
self.gv_wing_image.setObjectName("gv_wing_image")
self.listWidget = QtWidgets.QListWidget(self.splitter_image_list)
self.listWidget.setObjectName("listWidget")
self.verticalLayout.addWidget(self.splitter_image_list)
self.h_layout_buttons = QtWidgets.QHBoxLayout()
self.h_layout_buttons.setObjectName("h_layout_buttons")
self.btn_edit_tps = QtWidgets.QPushButton(self.centralwidget)
self.btn_edit_tps.setObjectName("btn_edit_tps")
self.h_layout_buttons.addWidget(self.btn_edit_tps)
self.btn_label_wings = QtWidgets.QPushButton(self.centralwidget)
self.btn_label_wings.setObjectName("btn_label_wings")
self.h_layout_buttons.addWidget(self.btn_label_wings)
self.verticalLayout.addLayout(self.h_layout_buttons)
MainWindow.setCentralWidget(self.centralwidget)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.menuBar = QtWidgets.QMenuBar(MainWindow)
self.menuBar.setGeometry(QtCore.QRect(0, 0, 803, 25))
self.menuBar.setObjectName("menuBar")
MainWindow.setMenuBar(self.menuBar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
MainWindow.setWindowTitle(QtWidgets.QApplication.translate("MainWindow", "MainWindow", None, -1))
self.btn_edit_tps.setText(QtWidgets.QApplication.translate("MainWindow", "Edit Existing TPS", None, -1))
self.btn_label_wings.setText(QtWidgets.QApplication.translate("MainWindow", "Label New Wings", None, -1))
中的数据。因此BehaviorSubject
总是在比较同一对象。您需要创建一个如下所示的新数据对象(可能会降低性能),或者确保始终创建不可变的数据对象。
distinctUntilChange
答案 1 :(得分:1)
在Angular中,使用对象引用进行比较。因此,当您更改嵌套属性时,不会触发更改。只有第一个孩子会触发更改事件。您需要创建一个新对象,然后将其传递到BehaviorSubject
。您可以使用lodash
cloneDeep
来执行对象的深层复制。
import {cloneDeep} from lodash;
public updateDetail(detailData: MyModelDetail) {
const data = cloneDeep(this._data.getValue());
data.detail.items.push(...detailData.items);
this._data.next(data);
}