user notifications
Angular Asynchronous Material RxJS SPA Typescript Visual Studio Code

How to use RxJS Subscriptions in Angular Applications

Welcome to today’s post.

Today I will be showing how to use RxJS subscriptions in an Angular web application.

More specifically, I will use the RxJS subscriptions to create visual notifications in an Angular application. In previous posts I showed how to use another type of visual notification, the Material Snack Bar.

Visual Badge Notifications

One quite useful notification is to include these in the menu of your application in the form of a visual badge notification, which is a numeric number on the top right corner of a Material Icon Button as shown:

Polling a Subscription

We can set a polling interval by which our menu will periodically refresh the notification value.

It is advised to no make this interval too frequent as it could cause your application to slow.

An interval of at least a minute is recommended. During development you can increase the polling interval. We will subscribe to the RxJs interval to do the polling for us.

this.pollingSubscription = interval(NotificationConfiguration.NOTIFICATION_INTERVAL)
    .subscribe(val => 
    {
        console.log('Polling notification..');
        this.menuNotification$.next(true);
    }
);

We will also require subscribing to one or more data sources to obtain our notification data from. These can be external APIs that return data that we can use to display within our notification:

this.recentLoansCount$ = this.notificationService.getRecentLoansCount();

this.recentReturnsCount$ = this.notificationService.getRecentReturnsCount();

Our polling subscription will then fire a menu notification observable as an RxJs BehaviorSubject

We then combine these observables together to populate our notification list.

With the Subscribe() function we then stream the two observables and the menu asynchronous observable for use in our component. The notification list arrays are then populated from the observable values as shown:

this.subscription = combineLatest(
[ 
    this.recentLoansCount$,
    this.recentReturnsCount$,
    this.menuNotification$
])
.subscribe((
    [recentLoansCount, 
     recentReturnsCount, 
     menuNotification]) => 
    {
        if (!menuNotification)
            return;

        this.numberOfLoans = recentLoansCount;
        this.numberOfReturns =  recentReturnsCount;
        console.log('number of recent loans = ' + this.numberOfLoans);
        console.log('number of recent returns = ' + this.numberOfReturns);

        this.notificationList.items = [];

        if (this.numberOfLoans > 0)
            this.notificationList.items.push(
            { 
                notificationDescription: "There are " + 
                    this.numberOfLoans +  " recent loans",
                count: this.numberOfLoans
            })

        if (this.numberOfReturns > 0)
            this.notificationList.items.push(
            { 
                notificationDescription: "There are " + 
                    this.numberOfReturns +  " recent returns",
                count: this.numberOfReturns
            })
    }
);

Once our structure is populated, it can be read by our HTML menu, which is just a material themed menu with a notification icon.

A Menu with a Notification Icon

Below is the HTML markup for a button that will be an icon warning button that will display the number of notifications:

<button mat-icon-button [matMenuTriggerFor]="notificationMenu">
    <div *ngIf="notificationList && notificationList.items && 
        notificationList.items.length > 0">
    <mat-icon matBadge="{{notificationList.items.length}}" matBadgeColor="warn">
        notifications_active
    </mat-icon>
  </div>
</button>

<mat-menu #notificationMenu="matMenu">
    <button mat-menu-item *ngFor="let item of notificationList.items">
        <mat-icon>list_alt</mat-icon>
        <span>{{item.notificationDescription}}</span>
    </button>
</mat-menu>

We can test this notification by updating our backend data. Once we have updated the data for our book loan by creating a new loan, or recalling an existing loan, the recent loans or returns will be reflected in the call to our API methods that determine these quantities.

By expanding our notification menu icon, we see 2 existing recent book loans:

 Our API service is as shown:

import { Injectable } from "@angular/core";
import { HttpClient } from '@angular/common/http'
import { environment } from 'src/environments/environment';
import { Observable } from "rxjs";

@Injectable()
export class NotificationService 
{
    private baseLoanAPI: string = environment.baseLoanApiUrl;

    constructor(private http: HttpClient) {}

    getRecentLoansCount(): Observable<number> {
        return this.http.get<number>(this.baseLoanAPI + 
            '/Loan/GetRecentLoansCount');
    }

    getRecentReturnsCount(): Observable<number> {
        return this.http.get<number>(this.baseLoanAPI + 
            '/Loan/GetRecentReturnsCount');
    }    
}

Our API methods each return a number which indicates the quantities of the loans and returns from our backend SQL table data.

In our application we update one existing loan records by returning the loan.

Following the update, we wait for the duration of our interval, observing the menu bar.

The number of notifications will then increase and show above the notification icon.

It will show a notification indicating one recent loan return.

This shows how useful polled subscriptions, when run efficiently, can be for your Angular applications. As a background thread running periodically within your UI it can provide some quite useful indicators and notifications such as:

  1. Significant changes to records.
  2. Notifications of backend processes running such as incoming emails or new customers / memberships.
  3. Notifications of backend processes being run successfully, such as a backup or data clean up.

The above notification example has shown how to combine the RxJs observables BehaviorSubject and functions CombineLatest() and Subscribe(). My previous posts applying the use of BehaviorSubject to synchronize data loading and CombineLatest to Synchronizing Dependent UI Data explain this is more detail.

That’s all for today’s post.

I hope you found this post useful and informative.

Social media & sharing icons powered by UltimatelySocial