更新异步管道中使用的可观察对象的一部分

Updating part of an observable used in async pipe

我们有一个包含多个部分的页面。每个部分可以有一张卡片或一张table。可观察对象使用异步管道在 html 中绑定数据。现在我需要更新 table 之一而不刷新整个可观察对象。

  loadPageData(Id): void {
    this.sections$ = this.psService.getLoadPageData(Id).pipe(
      switchMap(sections => forkJoin(
        sections.map(section => this.psService.getSectionData(section.sectionID).pipe(
          map(card => Object.assign({}, section, { card })),
          switchMap(c => c.card.map(card => iif(() => card.cardTypeId === 3,
            from(this.psService.getTable(this.tablePerodID).pipe(map(table => Object.assign({}, c, { table })))),
            of(c))))
        ).concatAll()
        ))
      ));
  }

  updateTable(sectionId, tablePerodID) {
    let tableData;
    this.psService.getTable(tablePerodID).subscribe((data) => {
      tableData = data;
    });
    this.sections$.forEach(sections => {
      sections.forEach(section => {
      if (section.sectionID === sectionId) {
        section.table = sectionData;
      }
    });
    });
  }

而 HTML 就像:

<div *ngFor="let section of sections$ | async;">
  <div *ngFor="let card of section.card | sort:'displayOrder';">
    <app-card></app-card>
    <div *ngIf="card.cardTypeName === 'Table'">
      <app-table></app-table>
    </div>
   </div>
 </div>

现在的问题是,当我尝试使用 updateTable [当用户更改 tablePerodID] 为 table 加载数据时,它什么也没做。我试过 .next() 但它更新了整个 section$。反正我只能更新table?

这似乎是对管道运作方式的误解。

明喻

想想水管。

假设您想要一个水龙头来倒糖水。

如果你从水龙头里取一杯水,然后往里面加糖,你就不能再把那水放回水龙头里了。

解决办法是在接水之前加一根带加糖系统的管子。

rxjs 管道是一样的,一旦你订阅,就没有回头路了。 (除非您创建一个新管道并将水倒入其中;)

解决方案是通过管道传输所有内容。只有 subscribe 当你想获得价值时。这也有性能改进。

解决方案

this._cache$: BehaviorSubject<Section[]> = new BehaviorSubject<Section[]> ([]);
this.sections$: Observable<Section[]>;
loadPageData(id: number): void {
    // subcribe once and save the values in the cache
    this.fetchPageData(Id).subscribe(data => this._cache$.next(data));

    // section$ will emit values when caché changes
    this.sections$ = this._cache$.asObservable();
}

fetchPageData(Id): Observable<Section[]> {
    // call your data service
    return this.psService.getLoadPageData(Id).pipe(
       //...
    );
}

updateTable(sectionId, tablePerodID) {
    let tableData;
    this.psService.getTable(tablePerodID).subscribe((data) => {
      tableData = data;
    });

    this.sections$ = this.sections$.pipe(
        map(sections => sections.map(section =>
        {
            if (section.sectionID === sectionId) {
                section.table = tableData;
             }
        })
    );
}

不是订阅可观察对象,而是管道更新操作。

updateTable 将更新 sections$ 可观察对象,html 异步管道将更新结果。

使用 _cache$ 有另一个性能改进,因为每次更新管道时它都不会调用您的数据服务。 (没必要用)

澄清:这将更新您应用中的管道,而不是数据库或任何地方的数据。

感谢@adrisons 让我想到了缓存。这是对我有用的解决方案。它与@adrisons 的回答略有不同,因为它不使用 observable。

  _cached: any; 
  sections$: Observable<any>;

  loadPageData(Id): void {
    // Load data from server
    this.sections$ = this.psService.getLoadPageData(Id).pipe(
      switchMap(sections => forkJoin(
        sections.map(section => this.psService.getSectionData(section.sectionID).pipe(
          map(card => Object.assign({}, section, { card })),
          switchMap(c => c.card.map(card => iif(() => card.cardTypeId === 3,
            from(this.psService.getTable(this.tablePerodID)
            .pipe(map(table => Object.assign({}, c, { table })))),
            of(c))))
        ).concatAll()
        ))
      ), first(), // Tap and get the first emit and assign to cache
      tap((data: any) => {
        this._cached = data // Caching the data without subscribing.
      }));
  }

  updateTable(sectionId, tablePerodID) {
    // Get table Data from backend and update cache
    this.psService.getTable(tablePerodID).subscribe((data) => {
      this._cached.map(section => {
        if (section.sectionID === sectionId) {
          section.table = data;
        }
      });
    });
    // Refresh async observable from cache
    this.refreshFromCache();
  }

  refreshFromCache() {
    this.sections$ = of(this._cached)
  }