Adding loading screen using HttpInterceptor

Issue

Here is my LoadingScreenService:

    import { Inject, Injectable } from '@angular/core';
import { DOCUMENT } from '@angular/common';
import { animate, AnimationBuilder, AnimationPlayer, style } from '@angular/animations';
import { NavigationStart, NavigationEnd, Router, NavigationCancel } from '@angular/router';

@Injectable()
export class LoadingScreenService {

  private loadingScreenEl: HTMLElement = null;
  private player: AnimationPlayer;

  private isEnabled = false;
  private isShowing = false;
  private isHiding = false;

  constructor(
    private animationBuilder: AnimationBuilder,
    @Inject(DOCUMENT) private document: any = null,
    private router: Router = null
  ) {
    if (document !== null) {
      this.loadingScreenEl = this.document.body.querySelector('#fuse-loading-screen');
    }

    if (router !== null) {
      this.router.events.subscribe((event) => {
          if (event instanceof NavigationStart) {
            setTimeout(() => {
              this.show();
            }, 0);
          }
          if (event instanceof NavigationEnd || event instanceof NavigationCancel) {
            setTimeout(() => {
              this.hide();
            }, 0);
          }
        }
      );
    }
  }

  /**
   * @param {number} wait Duration (ms) of a timeout before showing the spinner
   */
  show(wait: number = 1000) {
    if (this.isEnabled || this.isShowing) {
      return;
    }

    this.isShowing = true;
    this.player =
      this.animationBuilder
        .build([
          style({
            opacity: '0',
            zIndex : '9999'
          }),
          animate('200ms ease', style({opacity: '1'}))
        ]).create(this.loadingScreenEl);

    this.isEnabled = true;

    setTimeout(() => {
      this.player.play();

      setTimeout(() => {
        this.isShowing = false;
      }, 200);
    }, wait);

  }

  hide() {
    if (!this.isShowing
      && (!this.isEnabled || this.isHiding)) {
      return;
    }

    this.isHiding = true;
    this.player =
      this.animationBuilder
        .build([
          style({opacity: '1'}),
          animate('200ms ease', style({
            opacity: '0',
            zIndex : '-20'
          }))
        ]).create(this.loadingScreenEl);

    setTimeout(() => {
      this.isEnabled = false;
      this.player.play();

      setTimeout(() => {
        this.isHiding = false;
      }, 200);
    }, 0);
  }
}

And I’m trying to use show/hide methods whenever I’m making a request to backend.

Here is what I got so far but I cannot find a way to properly put my subscriveLoading() method into intercept().

   import { Injectable } from '@angular/core';
import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
import { LoadingScreenService } from '../../core/services/loading-screen.service';

@Injectable()
export class LoadingInterceptor implements HttpInterceptor {

  constructor(
    private loadingScreenService: LoadingScreenService
  ) {
  }

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

  }

  private subscribeLoading(): Observable<any[]> {
    this.loadingScreenService.show();

    const observable;
    observable.subscribe(() => {
      this.loadingScreenService.hide();
    }, err => {
      this.loadingScreenService.hide();
      return err;
    }); // Pass the error to other observers

    return observable;
  }
}

Is there some easy way to use my LoadingScreenService to intercept every call and use show method form mentioned service while I’m waiting for response from backend?

Solution

Here is a way to use your service in an interceptor

On intercept you start to show your loader, then your intecept return the request with a finally operator that will hide the on success or error

@Injectable()
export class LoadingInterceptor implements HttpInterceptor {
    constructor(
        private loadingScreenService: LoadingScreenService
    ) {
    }

    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        this.loadingScreenService.show();

        return next.handle(req).pipe(finalize(() => {
            this.loadingScreenService.hide();
        }));
    }
}

BUT : if you have two concurrent requests, when the first request will end, it will hide the loader even if the second request is not finished.

You should have a counter of pending request in your service :

@Injectable()
export class LoadingScreenService {
....
    private counter: number: 0;
...

show(wait: number = 1000) {
        this.counter++;
....
}

hide() {
        this.counter--;
        if (this.counter > 0) return;
...
}

Answered By – Pierre Mallet

Answer Checked By – Clifford M. (AngularFixing Volunteer)

Leave a Reply

Your email address will not be published.