Display Loader/Spinner On Each HTTP Calls In Angular 9

In this article, we will learn how to display loader/spinner on each HTTP calls in Angular 9.

Prerequisites:

  • Basic knowledge of Angular
  • Code editor like Visual Studio Code

Create a new Angular project by typing the following command in the VSCode terminal.

ng new loader

Now create a new custom Loader Component by typing the following command in the VSCode terminal.

ng g c loader

Now create a new Loader Service by typing the following command in the VSCode terminal.

ng g s loader/services/loader

Now create a new Loader Interceptor by typing the following command in the VSCode terminal.

ng g s loader/interceptors/loader-interceptor

Angular Interceptors are used to do some work for every single HTTP request or response.

Open the loader.service.ts file and add the code in it.

import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';

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

  public isLoading = new BehaviorSubject(false);

  constructor() { }
}

Open the loader-interceptor.service.ts file and add the code in it.

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({
  providedIn: 'root'
})
export class LoaderInterceptorService 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);
    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();
          });
      return () => {
        this.removeRequest(req);
        subscription.unsubscribe();
      };
    });
  }
}

Open the loader.component.css file and add the code in it.

.spinner {
  width: 40px;
  height: 40px;
  background-color: #333;
  margin: auto;
  -webkit-animation: sk-rotateplane 1.2s infinite ease-in-out;
  animation: sk-rotateplane 1.2s infinite ease-in-out;
}

@-webkit-keyframes sk-rotateplane {
  0% {
    -webkit-transform: perspective(120px)
  }
  50% {
    -webkit-transform: perspective(120px) rotateY(180deg)
  }
  100% {
    -webkit-transform: perspective(120px) rotateY(180deg) rotateX(180deg)
  }
}

@keyframes sk-rotateplane {
  0% {
    transform: perspective(120px) rotateX(0deg) rotateY(0deg);
    -webkit-transform: perspective(120px) rotateX(0deg) rotateY(0deg)
  }
  50% {
    transform: perspective(120px) rotateX(-180.1deg) rotateY(0deg);
    -webkit-transform: perspective(120px) rotateX(-180.1deg) rotateY(0deg)
  }
  100% {
    transform: perspective(120px) rotateX(-180deg) rotateY(-179.9deg);
    -webkit-transform: perspective(120px) rotateX(-180deg) rotateY(-179.9deg);
  }
}

.LoaderWrap {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 9999;
  background: rgba(0, 0, 0, .3);
  right: 0;
  bottom: 0;
}

.LoaderWrap .spinnerWrap {
  height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
}

Open the loader.component.html file and add the code in it.

<div class="LoaderWrap" [hidden]="!loading">
    <div class="spinnerWrap">
        <div class="spinner"></div>
    </div>
</div>

Open the loader.component.ts file and add the code in it.

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

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

  loading: boolean;

  constructor(private loaderService: LoaderService) {
    this.loaderService.isLoading.subscribe((x) => {
      this.loading = x;
    });
  }

  ngOnInit(): void { }

}

Open the app.component.html file and add the code in it.

<app-loader></app-loader>
<router-outlet></router-outlet>

Open the app.component.ts file and add the code in it.

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 {

  constructor(public http: HttpClient) {
    this.http.get('https://reqres.in/api/users')
      .subscribe((result) => {
        console.log(result);
      });
  }

}

Open the app.module.ts file and add the code in it.

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { LoaderComponent } from './loader/loader.component';
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { LoaderService } from './loader/services/loader.service';
import { LoaderInterceptorService } from './loader/interceptors/loader-interceptor.service';

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

Output:

If you want to use any other loader design, then you just have to make changes in loader.component.css and loader.component.html.

Here are some reference URLs:

– – –

Please give your valuable feedback and if you have any questions or issues about this article, please let me know.

Also, check Form Validation In Angular 9

1 Comment

  1. Lukarioez

    this is a great implementation, but i am just wondering if i am looking to create a individual loader service for each component without the http interceptor, how should i go about doing it?

    0
    0
    Reply

Submit a Comment

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

Subscribe

Select Categories