【问题标题】:Updating part of an observable used in async pipe更新异步管道中使用的 observable 的一部分
【发布时间】:2025-11-21 16:30:03
【问题描述】:

我们有一个包含多个部分的页面。每个部分可以有一张卡片或一张桌子。一个 observable 使用异步管道在 html 中绑定数据。现在我需要更新其中一个表而不刷新整个 observable。

  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 时] 为表加载数据时,它什么也不做。我试过.next(),但它会更新整个section$。反正我只能更新表格吗?

【问题讨论】:

  • 我是否认为每个部分中的表格都是相同的,因为它们来自相同的组件级别tablePeriodID?那么每当tablePeriodID 更新时,所有表都以相同的方式更新?
  • 每个部分的表格可以不同。每个部分表标题都有一个带有句点的下拉列表。想法是仅更新该下拉值更改的特定部分中的表。
  • 你能把它创建为最小的堆栈闪电战吗?目前可以有多种解读方式。
  • 至少,在updateTable 方法中,forEach 块this.sections$.forEach 应该在this.psService.getTablesubscribe 块中,因为这个调用是异步的。虽然这可能无法解决您眼前的问题,但它解决了您最有可能遇到的问题。如果我的评论不够清楚,我可以发布一个答案来说明这一点
  • 你为什么不自己处理每个部分?你得到这个部分,用 ngFor 做一个 ng-container 并创建一个 app-section。在本节中,您加载本节的每个数据。在本节中,您对卡片和桌子做同样的事情。所以应用表和应用卡。在每个组件中,您将数据加载到 ngOnInit。如果要通过带有句点的下拉列表更改表格,您可以轻松地在此表格中再次触发该方法。这基本上是角度的核心。分而治之!如果你有一个从父母到孩子的价值,你使用 ViewChild,如果不是很好。 #databinding

标签: angular typescript angular6 observable async-pipe


【解决方案1】:

这似乎是对管道如何运作的误解。

类似

想想水管。

想象一下你想要一个水龙头倒糖水。

如果你从水龙头里拿出一杯水,然后加糖,你就不能再把水放进水龙头了。

解决方案是在喝一杯水之前添加一根带有加糖系统的管道。

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

不要订阅 observable,而是通过管道传输更新操作。

updateTable 将更新 sections$ observable,html 异步管道将更新结果。

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

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

【讨论】:

  • 这个解决方案对我不起作用,因为我试图避免刷新整个 section$ observable。
  • 如果你使用caché,只有调用loadPageData时才会获取数据,那么你应该使用observable。订阅sections$ observable 不会触发http调用
【解决方案2】:

感谢@adrisons 给了我缓存的想法。这是对我有用的解决方案。它与@adrisons 的回答略有不同,它不使用可观察的。

  _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)
  }

【讨论】: