angularrxjszone

How to load data from REST api every x seconds and update UI without flickering in Angular?


I have an angular application where I periodically want to fetch data from my backend and show the updates in the UI. This is my backend service :

  // _backendService
  apiDashboardJobs(): Observable<any> {
    return this._httpClient.get<any>(`${environment.apiUrl}/api/dashboard/jobs`);
  }

My component code:

      this.jobsOverview$ = interval(3000)
      .pipe(
        takeUntil(this.destroy$),
        switchMap(response => this._backendService.apiDashboardJobs()),
        map(response => 
          [
            {'status' :'Completed', 'number': response.completed, 'color': 'rgb(94,210,86)'},
            //...
          ]
        ));

    public trackByFn(index, item) {
      return index;
    }

    ngOnDestroy(): void {
      // Unsubscribe and complete the destroy$ subject
      this.destroy$.next();
      this.destroy$.complete();
    }

My html code:

                  <tr *ngFor="let job of jobsOverview$ | async; trackBy: trackByFn" style="height: 30px;">
                    <td>
                      <div style.background-color="{{job.color}}"> </div> 
                    </td>
                    <td>
                      {{job.status}}
                    </td>
                    <td>
                      {{job.number}}
                    </td>
                  </tr>

Now, this code seemed to work fine initially, but after some time the table does not render at all anymore and I see a bunch of these errors in the network tab: chrome network tab

Can someone guide me on how to fix this?


Solution

  • I disagree with Gilles's comment

    Well if it's working fine at begining then I think the issue is more related to your server or directly your backend. There are probably some rate limiting which prevent the next requests to be treated

    The screenshot shows that the requests are cancelled. Cancellation is done client side.

    Looking at your code, you're using an interval (every 3s) and a switchMap. Meaning that if your server for any reason takes longer than 3s to reply, the next tick of your interval will happen and switchMap will cancel the ongoing request.

    If you replace your switchMap with an exhaustMap, it should work as expected.

    EDIT:

    Also note that in order to avoid that back pressure in the first place, you could improve the code to:

    Here's how I'd do it:

    defer(() => this._backendService.apiDashboardJobs())
      .pipe(retry({ delay: 3000 }), repeat({ delay: 3000 }))
      .subscribe(() => console.log('Received answer'));
    

    Here's a live demo where I also show that errors are handled as expected: https://stackblitz.com/edit/rxjs-uah45e?devtoolsheight=60&file=index.ts