Asynchronous programming
Angular Asynchronous Patterns RxJS Top Typescript Visual Studio Code

Using RxJS BehaviorSubject to Synchronize Data Loading in Angular Forms

Welcome to today’s post.

In this blog I will show how to use the RxJS observable BehaviorSubject to synchronize loading of data subscriptions from Web APIs into an Angular form template.

The BehaviorSubject observable, when we subscribe to it will return the last value it has received. It requires an initial value and for this reason, is ideal for interfaces that change depending on receipt of asynchronous data. In addition, it is a Subject, which is a multicast observable which allows multiple subscribers to receive emitted data.

Without the adoption of synchronization, our data will be displayed before the incoming data from our Web API has had an opportunity to complete subscribing to the observable within our Angular form.

The most practical example is where we are loading data into a web form.

While waiting for the data we display an animated graphic (such as a spinner or progress bar) to the end user.

When the data is successfully loaded, the animated graphic disappears, and the data is displayed in a table or chart.

With the use of the BehaviorSubject observable in a UI, we can achieve the three goals:

  1. Assign a default value to our observable.
  2. To use an asynchronous observable as a flag to notify our form template that the data can be displayed.
  3. Automatically free the observable from the Angular form component once it is destroyed.

To declare and initialize our observable in our component as shown:

import { BehaviorSubject } from 'rxjs';
import { ProgressSpinnerMode } from '@angular/material/progress-spinner';
import { delay } from 'rxjs/operators';

We declare an observable variable initialized to indicate the data has not loaded:

hasLoaded$ = new BehaviorSubject<boolean>(false);

In the form initialisation, we subscribe to the data.

When the data subscription has completed, the observable is set to allow the form to display the data:

ngOnInit() {
  this.api.getBooks().pipe(delay(3000)).subscribe(res => {
    this.hasLoaded$.next(true);
    this.books = res
    console.log("read books from API");
  })
}

In the above I have delayed the return of results from the API request to a few seconds so that we can view how the loading would resemble if the request took longer to return a response.

The line of code below sets the hasLoaded$ observable to the next value of true, which is then subscribed to by an asynchronous observable.

this.hasLoaded$.next(true);

In the HTML template below, the asynchronous observable waits for changes to the next value of the hasLoaded$ observable and either displays the spinner (data still loading) or displays the book data (data has loaded):

<mat-card>
  <mat-card-content>
    <div><b>List of Books</b></div>
    <div *ngIf="hasLoaded$ | async; else loading">
      <mat-list *ngFor="let book of books">
        <div style="color: blueviolet; 
background-color:aquamarine;">
          <mat-list-item 
            class="clickLink" 
            (click)="selectBookById(book.id)"> 
            {{book.title}} 
          </mat-list-item>
        </div>
      </mat-list>
    </div>
    <br />
    <ng-template #loading>  
      <div>
        <br />
        <mat-progress-spinner
          [color]="primary"
          [mode]="mode"
          [value]="value">
        </mat-progress-spinner>
        loading books .. please wait
      </div>
    </ng-template>
  </mat-card-content>
  <mat-card-actions>
  </mat-card-actions>
</mat-card>

In this example we include a short delay of a few seconds before we start subscribing to the web API data so that we can simulate a longer delay in receiving data. With a small data set, the spinner would not be noticeable, but with larger data sets a delay would not be necessary and the progress spinner would be visible.

As we can see, the use of BehaviourSubject observable acts like a synchronization operator, allowing end users a better experience when receiving data from a backend or external service. Without the use of synchronization, the user experience would not be pleasant.

Using a basic Angular template within the view with a material progress component achieves this UI effect. We could also have used a progress bar or an animated graphic, whichever suits your requirement.

That’s all for this post.

I hope you found it useful and informative.

Social media & sharing icons powered by UltimatelySocial