Simple Loading Stack with Ionic 3

This is a simple Angular Service which handles multiple ionic 3 loaders at the same time. The most recent loader is displayed on top. If it is finished, and other loaders are still pending, the next recent will be shown.

File: loading.service.ts
typescript

/** @format */

import { Injectable } from '@angular/core';
import { Loading, LoadingController } from 'ionic-angular';
import { IntervalObservable } from 'rxjs/observable/IntervalObservable';

interface LoadingIndicator {
   id: string | number;
   message: string;
}

@Injectable()
export class LoadingService {
   private loadingIndicators: Array<LoadingIndicator>;
   private loader: Loading;
   private isLoaderActive: boolean;

   constructor(private loadingCtrl: LoadingController) {
      console.log('LoadingProvider loaded.');
      this.loadingIndicators = [];
      this.createEmptyLoader();
      this.isLoaderActive = false;
   }

   /**
    * Add loading indicator on stack
    * @param {LoadingIndicator} indicator
    */
   public add(indicator: LoadingIndicator): void {
      this.loadingIndicators.unshift(indicator);
      this.handleLoaderVisibility();
   }

   /**
    * Finish loading indicator by id
    * @param {string | number} indicatorId
    */
   public finish(indicatorId: string | number): void {
      this.loadingIndicators = this.loadingIndicators.filter(indicator => {
         // Hide loader before deleting
         return indicator.id !== indicatorId;
      });
      this.handleLoaderVisibility();
   }

   /**
    * Initialize empty loader
    */
   private createEmptyLoader(): void {
      this.loader = this.loadingCtrl.create({
         content: ''
      });
   }

   /**
    * Determine which loader is currently visible
    */
   private handleLoaderVisibility(): void {
      // If has loaders, show message of most recent
      if (this.loadingIndicators.length > 0) {
         this.loader.setContent(this.loadingIndicators[0].message);
         if (!this.isLoaderActive) {
            console.warn('Creating instance');
            this.loader.present();
            this.isLoaderActive = true;
         }
      } else {
         if (this.isLoaderActive) {
            console.warn('Destroying instance');
            this.setTimedSelfDestruction();
            this.isLoaderActive = false;
         }
      }
   }

   /**
    * Destroy loader after min 500ms to avoid flickering
    * Do not destroy when another loader is pushed on stack in this time period
    */
   private setTimedSelfDestruction(): void {
      // This step is needed to avoid flickering loaders, which are invoked one by one
      const sub = IntervalObservable.create(500).subscribe(() => {
         // Check if loader has not been reactivated, then destroy,
         // else self-instruction will be invoked again
         if (!this.isLoaderActive) {
            this.loader.dismiss();
            this.createEmptyLoader();
            sub.unsubscribe();
         }
      });
   }
}

Leave a Reply

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