如何修改 RxJS 主题中的 Map?

Fer*_*sen 5 rxjs typescript angular

Map我想向一些消费者展示一个。该地图包含 s 列表File及其上传状态:

export interface FileProgress {
    file: File;
    sent: boolean;
}
Run Code Online (Sandbox Code Playgroud)

因此,它Map存储在BehaviorSubject我将使用以下方法公开的a 中.asObservable()

private readonly fileProgressSubject: BehaviorSubject<Map<string, FileProgress>>;

我的问题是,对底层数据结构执行更新的最佳方法是什么?

如果BehaviorSubject包含一个简单类型,例如mySubject: BehaviorSubject<boolean>,我只需调用即可mySubject.next(true),瞧我已经更新了该值。

但更新Map我的内部fileProgressSubject就比较麻烦了。我需要访问地图,然后对其进行更改,所以这可以工作,但感觉不对:

const temp = fileProgressSubject.getValue();
temp.delete(someFileKey); // EXAMPLE OPERATION - DELETION
fileProgressSubject.next(temp);
Run Code Online (Sandbox Code Playgroud)

这是更新我的底层的“正确”方法吗Map?我读过,调用.getValue()aSubject是强制使用反应式构造,并且可能意味着您没有做正确的事情。

我的另一个想法是,由于这BehaviorSubject将作为可观察对象公开,因此即使我可以看到 Typescript 编译器不允许这样做:

const fileObservable = fileProgressSubject.asObservable();

fileObservable.pipe(
    map(fileProgressMap => fileProgressMap.delete(someFileName)), // DELETE A FILE FROM THE MAP
).subscribe(fileProgressSubject);
Run Code Online (Sandbox Code Playgroud)

因此,基本上,通过可观察对象传输底层数据结构的当前值,对其进行更改,然后将新值填充Map到主题中。但同样,这似乎不可能,不知道为什么。

那么,处理此类情况的最佳方法是什么?

Mik*_*keJ 4

如果只是为了状态管理,底层数据结构可能应该对消费者透明。这样,如果您决定有一天从 a 更改Map为其他内容,您将不需要找到所有消费者并更新它们。

一种更清晰、更可维护的方法可能是让文件进度状态的所有者(我假设是 Angular 服务)公开简单的方法,允许消费者将其文件添加到进度状态,然后在状态发生变化时更新其状态。

所以你的状态管理服务看起来像这样:

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

export interface FileProgress {
  file: File;
  sent: boolean;
}

Injectable({
  providedIn: 'root'
})
export class FileProgressService {
  private _fileProgressMap = new Map<string, FileProgress>();
  get fileProgressMap(): ReadonlyMap<string, FileProgress> {
    return this._fileProgressMap;
  }

  private _fileProgress$ = new BehaviorSubject<ReadonlyMap<string, FileProgress>>(this.fileProgressMap);
  get fileProgress$(): Observable<ReadonlyMap<string, FileProgress>> {
    return this._fileProgress$.asObservable();
  }

  addFile(fileKey: string, file: File) {
    const initialProgress: FileProgress = {
      file,
      sent: false
    };

    this._fileProgressMap.set(fileKey, initialProgress);
    this.notify();
  }

  updateFileProgress(fileKey: string, isSent: boolean) {
    this._fileProgressMap.get(fileKey).sent = isSent;
    this.notify();
  }

  private notify() {
    this._fileProgress$.next(this.fileProgressMap);
  }
}
Run Code Online (Sandbox Code Playgroud)

您向消费者公开简单addFile的方法。updateFileProgress

该服务恰好维护 a 中的状态Map,但消费者不需要知道这一点,因此只要 aaddFileupdateFileProgress方法签名不改变,它就可以随时更改,而无需更新消费者。

您还公开了一个fileProgress$Observable,消费者可以订阅该 Observable 以接收状态更新。由于您只想通过公开的方法来控制状态,因此 Observable 会发出一个ReadonlyMap围绕状态管理的只读包装器Map,因此消费者无法直接更新底层数据结构。

你的消费者都可以订阅 Observable fileProgress$,但你应该考虑他们是否真的需要。您的所有消费者是否都需要知道其他文件的sent状态何时发生更改,或者您是否通过 Observable 执行此操作只是为了为消费者提供一种获取状态管理对象并为其特定文件更新它的方法?如果是后者,那么可能甚至不需要让他们订阅,因为公开的添加/更新方法应该为他们提供所需的一切。