Angular BehaviorSubject数据服务>嵌套数据问题

时间:2019-08-22 23:57:19

标签: angular rxjs

我一直在为我的应用程序使用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']
    });
  }
}

谢谢!

2 个答案:

答案 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);
      }