首页 > 解决方案 > BehaviorSubject 的行为与字符串和对象不同

问题描述

我试图在 ionic/angular 项目中使用数据服务来跨组件共享数据,但该服务没有像我预期的那样与对象一起工作。

在一个组件(即:父组件)中,当从订阅服务获取属性值的本地对象的值发生更改时,它会将值传播到其他组件,但我认为如果通过更改值可能会发生这种情况使用 next() 服务。

下面是一个空白离子项目中的最小示例,用于比较此场景中的字符串和对象。在父组件中有两种方法,一种更改本地值(发生问题的对象是对象而不是字符串),另一种通过服务更改值(按预期工作)。

我可能做错了什么,behaviourSubject 以这种方式与对象一起工作还是有问题?

数据服务.ts

import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class DataService {

  private strSrc = new BehaviorSubject('default');
  readonly strCurr = this.strSrc.asObservable();

  private objSrc = new BehaviorSubject({ name : 'Default' });
  readonly objCurr = this.objSrc.asObservable();

  constructor() { }

  chgStr(strNew) {
    this.strSrc.next(strNew);
  }

  chgObj(objNew) {
    this.objSrc.next(objNew);
  }
} 

child.component.ts

import { Component, OnInit } from '@angular/core';
import { DataService } from '../../service/data.service';

@Component({
  selector: 'app-child',
  template: `
  <p>child data String: {{ str }}</p>
  <p>child data Object: {{ obj.name }}</p>
  `
})

export class ChildComponent implements OnInit {
  str: string;
  obj: { name: string };

  constructor(private data: DataService) { }

  ngOnInit() {
    this.data.strCurr.subscribe(data => this.str = data);
    this.data.objCurr.subscribe(data => this.obj = data);
  }
}

主页.ts

import { Component, OnInit } from '@angular/core';
import { DataService } from '../service/data.service';

@Component({
  selector: 'app-home',
  template: `<ion-content>
  <p>parent data String: {{ str }}</p>
  <p>parent data Object: {{ obj.name }}</p>
  <br/>
  <app-child></app-child>
    <ion-button (click)="chgLocal()">
      change parent variable
    </ion-button>
    <ion-button (click)="chgBehaviorSubject()" color="secondary">
      change behaviorSubject
    </ion-button>
  </ion-content>`,
})
export class HomePage implements OnInit {
  str: string;
  obj: { name: string };

  constructor(private data: DataService) { }

  ngOnInit() {
    this.data.strCurr.subscribe(data => this.str = data);
    this.data.objCurr.subscribe(data => this.obj = data);
  }

  chgLocal() {
    this.str = 'changed String';
    this.obj.name = 'changed Object';
    // --Output in HTML
    // parent data String: changed String
    // parent data Object: changed Object

    // child data String: default
    // child data Object: changed Object <--PROBLEM!

    // --Expected output in HTML
    // parent data String: changed String
    // parent data Object: changed Object

    // child data String: default
    // child data Object: Defaul
  }

  chgBehaviorSubject() {
    this.data.chgStr('behavior string');
    this.data.chgObj({ name: 'Behavior Object' });
  }
}

标签: angulartypescriptrxjsionic4behaviorsubject

解决方案


问题是由于对象的突变而不是行为主体所独有的。对象是通过引用传递的,当您改变父对象中的对象时,它也会更改引用同一对象的其他位置。

你可以做

this.data.objCurr.subscribe(data => this.obj = {...data}); // Assuming there are no nested objects inside data

在嵌套对象的情况下,您可以使用它,JSON.parse(JSON.stringify(data))尽管它的性能不是很好,并且可以将 lodash 之类的东西用于深度克隆实用程序(或者您也可以编写一个克隆嵌套对象的递归函数)


推荐阅读