【问题标题】:Angular: check if Observable / BehaviorSubject has relevant changesAngular:检查 Observable / BehaviorSubject 是否有相关变化
【发布时间】:2021-12-31 04:02:00
【问题描述】:

我有一个在会员服务中观察到的用户对象。

我只想在用户对象发生相关更改时更新我的​​服务。

为了了解我的用户对象是否有相关变化,我将我的本地用户对象与观察到的用户对象进行比较。并且总是在之后分配新对象。

但是,这不起作用。

export class MemberService {
  private subscription?: Subscription;
  user?: User;

  constructor(public auth: AuthService) {
    this.subscription = this.auth.user$.subscribe((user) => {
      const updateServices = this.hasUserRelevantChanges(user)

      // apparently this line always fires before the functioncall above?!
      this.user = user;

      if (updateServices) {
         this.updateMyServices();
      }
    });
  }

  ngOnDestroy() {
    this.subscription?.unsubscribe();
  }

  hasUserRelevantChanges(user: User | undefined): boolean {
      return user?.subscription !== this.user?.subscription ||
          user?.username !== this.user?.username ||
          user?.isBanned !== this.user?.isBanned;
  }

  updateMyServices(): void {
    // Updating my services!!!
  }
}

export class AuthService {
  public readonly user$: Observable<User| undefined> = this.user.asObservable();
  user: BehaviorSubject<User| undefined> = new BehaviorSubject<User| undefined>(undefined);

    constructor(private httpHandler: HttpHandlerService) { ... }

  handleUser(): void {
    this.httpHandler.login().subscribe(
        (user: User) => this.user.next(user));
  }

  updateUserData(newUser: User): void {
    this.user.next(Object.assign(this.user.value, newUser));
  }
}

为什么我的函数hasUserRelevantChanges() 总是比较相同的新对象?本地的 this.user 总是在这个检查中保存新的值,即使分配 this.user = user 是在之后出现的?

那么,与旧的/以前的用户对象相比,我如何了解我的新 user 对象的相关值是否发生了变化?

【问题讨论】:

    标签: angular typescript rxjs


    【解决方案1】:

    “为什么我的函数 hasUserRelevantChanges() 总是比较相同的新对象?”

    这是因为您总是将新值分配给 this.user 变量。理想情况下,您需要使用新的user 更新它,如果它不同的话。换句话说,它必须移动到if 块内。

    this.subscription = this.auth.user$.subscribe((user) => {
      const updateServices = this.hasUserRelevantChanges(user)
    
      if (updateServices) {
        this.user = user;
        this.updateMyServices();
      }
    });
    

    更新:pairwise + filter

    在您的场景中,您最好使用 RxJS pairwise + filter 运算符来检查条件。

    this.subscription = this.auth.user$.pipe(
      pairwise(),
      filter(([user1, user2]) => (
        user1?.subscription !== user2?.subscription ||
        user1?.username !== user2?.username ||
        user1?.isBanned !== user2?.isBanned;
      ))
    ).subscribe(([user1, user2]) => {    // <-- Update 2: ignore `user1`
      this.updateMyServices(user2);
    );
    

    如果您实际上是在将对象的所有属性与其之前的值进行比较,则可以将整个 pairwise + filter 替换为 distinctUntilChanged 运算符。

    【讨论】:

    • 但是我在分配完成之前调用了hasUserRelevantChanges(user),如果分配已经完成,函数的行为如何?:/
    • @TeaCup:我不太确定你的作业已经完成是什么意思。是什么让你如此相信?另一方面,我使用 RxJS 运算符 pairwise + filter 更新了 IMO 更适合您的要求的答案。
    • 我已按照建议进行更改,现在使用成对过滤器,但每次更改时我仍然收到两次相同的对象;(
    • @TeaCup:确实,使用pairwise 会发出两个值,previous 和 current。如filter(([user1, user2]) =&gt; () 语句所示,您可以在订阅回调中获取两个用户并忽略之前的值user1。我已经更新了答案。
    【解决方案2】:

    使用RxJs 运算符来执行此操作。您不需要将用户存储在代码中的某个位置,但您可以使用pipepairwisefilter。所以它会是这样的:

    this.auth.user$.pipe(
               pairwise(), 
               filter(users => userChanged(users[0], users[1]))
               ... // other operators to handle change - tap, map etc
               )
    

    如果你把逻辑移到合适的操作符上会更简洁。

    编辑:Pairwise 返回两个值:previous 和 current,filter - 顾名思义,过滤事件并仅在传递的回调返回 true 时发出 next

    Edit2:您应该实现 userChanged 或调整您的 hasUserRelevantChanges,现在您需要传递 2 个参数。

    Edit3:为什么您的代码不起作用?因为Object.assign 不会创建新的对象引用而是更改原始对象 - 所以实际上,您每次都在检查同一个对象。

    更新:传播运算符:

    updateUserData(newUser: User): void {
    this.user.next({...this.user.value, ...newUser});
    

    }

    【讨论】:

    • 对象浅拷贝是个好点。如果 OP 希望忽略 RxJS 运算符,他们可以尝试制作一个基本的 deep object copy
    • 我尝试使用成对过滤器,但不幸的是过滤器比较也会一直比较相同的对象,它不存储旧对象的值;(
    • 只需删除 Object.assign 并使用扩展运算符。它将创建具有更新值的新对象。我将编辑我的答案。
    猜你喜欢
    • 2020-07-28
    • 2018-11-19
    • 2019-12-12
    • 1970-01-01
    • 2018-11-12
    • 2017-01-22
    • 2017-05-18
    相关资源
    最近更新 更多