Angular 13 Global Loader Spinner on HTTP calls Interceptors Tutorial

In this post, we will learn how to use Angular’s Interceptor class to show a common loader/ spinner indicating about an API HTTP call is in progress. We’ll create a UI component with a custom loader to keep track of every HTTP call going from the client application to a remote server. Angular Interceptors can…

By.

•

min read

In this post, we will learn how to use Angular’s Interceptor class to show a common loader/ spinner indicating about an API HTTP call is in progress. We’ll create a UI component with a custom loader to keep track of every HTTP call going from the client application to a remote server.

Angular Interceptors can be used in a number of ways as they work pretty well in manipulating and managing HTTP calls to communicate that we make from a client-side web application. Single Page Applications(SPA) build using Angular framework, use HttpClient module to make HTTP calls to the server for retrieving data to update pages with dynamic information. So it becomes very important to provide information about the status of API calls we make to a remote server to the users. Loaders/ Spinners and sometimes Progress bars are shown to inform the user that there are some server requests going on.

Using Angular interceptors we can also handle responses or errors returned by calls. We will maintain the stack or queue to calls and will show the loader if the requests queue is 1 or more than 1 as sometimes more than one call is also made to get data in dynamic applications.

 

Also Check: Angular Spinner Loader using ng-http-loader package

 

Our demo Angular project will have a custom MyLoaderComponent component, loader Interceptor and a loader service to show/ hide our loader using RxJs BehaviorSubject observable.

Let’s have a look at the steps we’ll perform during this implementation.

How to add Custom HTTP Loader using Angular Interceptors?

Step 1 – Update the Angular CLI tool to the latest version

Step 2 – Create a new Angular project

Step 3 – Create a loader service to broadcast isLoading boolean using the RxJs BehaviorSubject observable.

Step 4 – Create a Loader Interceptor to keep HTTP calls stack and handle errors as well.

Step 5 – Create a custom MyLoaderComponent

Step 6 – Add a custom loader component in the app component template.

Step 7 – Update App Module with HttpClientModule, Interceptor and Loader service.

Step 8 – Run Application

 

Let’s get started!

Step 1 – Update Angular CLI tool

Angular Interceptors are available from versions greater than 4.3 so you can go ahead with any version above that. But it is always preferred to upgrade the Angular CLI tool to the latest version.

Run the following npm command in the terminal window to upgrade to the current version 13.3.4

npm install -g @angular/cli

 

Step 2 – Create a new Angular project

Using the Angular CLI tool, create a new Angular 9 project by running below ng command

ng new angular-interceptor-loader
? Would you like to add Angular routing? Yes
? Which stylesheet format would you like to use? SCSS

Move to the project root directory

$ cd angular-interceptor-loader

Open the project by hitting the below shortcut command in the Visual Studio Code(if you have installed it)

$ code .

 

Step 3 – Create a Loader Service

Now we will create a loader service, which will have RxJs <strong>BehaviorSubject</strong>observable. We will subscribe to this special Observable type to keep a close watch on the HTTP request queue which will be emitted from an Interceptor which we will create in the next step.

The BehaviorSubject always emit the last value directly. You can get more details on this article by Luuk

Run the following ng command in CLI to create a loader service in the app folder in the project.

ng generate service services/loader --skipTests=true

The --skipTests=true option is used to skip spec files used for testing.

After creating the service replace ~services/loader.service.ts with the following code.

//loader.service.ts
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class LoaderService {

  public isLoading = new BehaviorSubject(false);
  constructor() { }
}

Step 4 – Create Loader Interceptor

In Angular Interceptor, we’ll create an array requests of type HttpRequests. Whenever a new Http call hits, it will be pushed in it. On successfully completing the Http call, that request will be popped out by calling the removeRequest().

Create the LoaderInterceptor in the interceptors folder by running below generate command

ng generate service interceptors/loader-interceptor --skipTests=true

The LoaderInterceptor class implements HttpInterceptorto override its intercept method.

Now replace following code in ~interceptors/loader-interceptor.service.ts

// loader-interceptor.service.ts
import { Injectable } from '@angular/core';
import {
  HttpResponse,
  HttpRequest,
  HttpHandler,
  HttpEvent,
  HttpInterceptor
} from '@angular/common/http';
import { Observable } from 'rxjs';
import { LoaderService } from '../services/loader.service';

@Injectable()
export class LoaderInterceptor implements HttpInterceptor {
  private requests: HttpRequest<any>[] = [];

  constructor(private loaderService: LoaderService) { }

  removeRequest(req: HttpRequest<any>) {
    const i = this.requests.indexOf(req);
    if (i >= 0) {
      this.requests.splice(i, 1);
    }
    this.loaderService.isLoading.next(this.requests.length > 0);
  }

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {

    this.requests.push(req);

    console.log("No of requests--->" + this.requests.length);

    this.loaderService.isLoading.next(true);
    return Observable.create(observer => {
      const subscription = next.handle(req)
        .subscribe(
          event => {
            if (event instanceof HttpResponse) {
              this.removeRequest(req);
              observer.next(event);
            }
          },
          err => {
            alert('error' + err);
            this.removeRequest(req);
            observer.error(err);
          },
          () => {
            this.removeRequest(req);
            observer.complete();
          });
      // remove request from queue when cancelled
      return () => {
        this.removeRequest(req);
        subscription.unsubscribe();
      };
    });
  }
}

The intercept method is required in HttpInterceptor implemented class, this will help in modifying responses and requests made through HTTP Client in Angular application.

The removeRequest method will keep track of the number of HTTP calls in progress by maintaining the queue in an array.

We are emitting true/false boolean value to LoaderService using the next method checking the length of pending requests.

Step 5 – Create a custom Loader Component

Now, we’ll create a custom styled loader spinner component with its own style to keep loader separate and more customizable. Run below generate command to create MyLoaderComponent in the components folder.

ng generate component components/my-loader --skipTests=true

Place below HTML template in ~components/my-loader/my-loader.component.html on new loader component.

<!-- my-loader.component.html -->
<div class="progress-loader" [hidden]="!loading">
    <div class="loading-spinner">
        <img src="https://loading.io/mod/spinner/gear-set/index.svg">
        <span class="loading-message">Please wait...</span>
    </div>
</div>

Check more loader images here.

Add loader style in the ~components/my-loader/my-loader.component.css

/* my-loader.component.css */
.loading-spinner{    
    background-color: #0000001f;
    position: absolute;
    width: 100%;
    top: 0px;
    left: 0px;
    height: 100vh;
    align-items: center;
    justify-content: center;
    display: grid;
}

.loading-spinner img{
    align-self: end;
}

.loading-message{
    text-align: center;
    align-self: start;
}

Replace the ~components/my-loader/my-loader.component.ts file with the following code

// my-loader.component.ts
import { Component, OnInit } from '@angular/core';
import { LoaderService } from '../../services/loader.service';

@Component({
  selector: 'app-my-loader',
  templateUrl: './my-loader.component.html',
  styleUrls: ['./my-loader.component.css']
})
export class MyLoaderComponent implements OnInit {

  loading: boolean;

  constructor(private loaderService: LoaderService) {

    this.loaderService.isLoading.subscribe((v) => {
      console.log(v);
      this.loading = v;
    });

  }
  ngOnInit() {
  }

}

In the component class above, we are subscribing to the BehaviorSubject which we defined in the service. Based on that the loader boolean will show/hide loader based on a flag returned.

 

Step 6 – Add Loader Component

Just place the app-my-loader loader component in the main parent application component, which is App component in our case. Add the loader component in the app.component.html file, which is the root component template as shown below.

...
<button (click)="makeHttpCall()">Make Http Call</button>
<app-my-loader></app-my-loader>

Add a method makeHttpCall() to test an Http get() call in the app.component.ts file as shown below:

// app.component.ts
import { Component } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent {
  title = 'angular-interceptor-loader';

  constructor(public http: HttpClient) {}

  makeHttpCall() {
    this.http
      .get('https://jsonplaceholder.typicode.com/comments')
      .subscribe((r) => {
        console.log(r);
      });
  }
}

Step 7 – Update App Module.

Finally, we need to import the LoaderService, LoaderInterceptor and HttpClientModule to make Http calls in the app.module.ts file.

Also, we need to import HTTP_INTERCEPTORS from @angular/common/http to enable Interceptors in the application.

// app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';

import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';

import { LoaderService } from './services/loader.service';
import { LoaderInterceptor } from './interceptors/loader-interceptor.service';
import { MyLoaderComponent } from './components/my-loader/my-loader.component';

@NgModule({
  declarations: [AppComponent, MyLoaderComponent],
  imports: [BrowserModule, HttpClientModule],
  providers: [
    LoaderService,
    { provide: HTTP_INTERCEPTORS, useClass: LoaderInterceptor, multi: true },
  ],
  bootstrap: [AppComponent],
})
export class AppModule {}

Pheww.. we are finally done 🙂 a bit long way by only one-time implementation adds a lot for a good user experience.

Step 8 – Run Application

Now you can check your application by running $ ng serve --open in the terminal. In this way, we can show a common loader in Angular based applications using the power of Interceptors.

Check next post to see how to add Spinners and Progress bar using Angular Material.

Leave a Reply

Your email address will not be published. Required fields are marked *