Search code examples
angulartypescriptangular6observableasync-pipe

Updating part of an observable used in async pipe


We have a page where there are multiple sections. Each section can have a card or a table. An observable binds data in html using async pipe. Now I need to update one of the table without refreshing whole 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;
      }
    });
    });
  }

and the HTML is like:

<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>

Now the issue is when i try to load data for the table using updateTable [when user changes tablePerodID], it does nothing. I have tried .next() but it updates whole section$. Is there anyway i can only update table?


Solution

  • Thanks @adrisons for giving me the idea of caching. Here is the solution that worked for me. It is slightly different from @adrisons answer in a way that it doesn't use 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)
      }