Automated Modal Loader Service (Concept Discussion)

Good Day,

I have created some PHP code that whenever an AJAX Call is called anywhere on the site, it displays a modal popup with a loader.
The nice part of this is that I created and initialized it once, and never had to specify a loader again on any other code.

Is this possible to accomplish using Ionic 4 and Angular at all?
Or will I have to specify specifically every time I want to have a loader displayed?

Thank you in advance.

What you can do is create a service and call loaders on each request to the server and call dismiss() loaders accordingly.
Source:- stackoverflow .
Hope this helps

I would suggest looking at HTTP interceptors. I use them for this sort of task.

Thank you all! Much appreciated.
I have already implemented interceptors for Authorizations, so I guess this would be the most logic place to just activate and deactivate the loader modals. Fantastic! Thank you!

Thank you @rapropos,

I have implemented as per your suggestion, however, there are two issues with my code:

  1. I have to use a timeout for it to work properly (but obviously this is very bad)
  2. If multiple HTTP Calls are executed at once, there are multiple loading controllers over each other…

Here is my code:

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

        // YOU CAN ALSO DO THIS
        // const token = this.authenticationService.getToke()
        this.loadingCtrl.create({keyboardClose: true, spinner: 'bubbles', message: 'Loading...'})
        .then(loadingEl => {
            loadingEl.present();
        });
        return from(this.storage.get(environment.TOKEN_KEY))
            .pipe(
                switchMap(token => {
                    if (token) {
                        request = request.clone({ headers: request.headers.set('Authorization', 'Bearer ' + token) });
                    }

                    if (!request.headers.has('Content-Type')) {
                        request = request.clone({ headers: request.headers.set('Content-Type', 'application/json') });
                    }

                    if (this.debug) {
                        request = request.clone({ url: request.url + '?XDEBUG_SESSION_START=1'});
                    }

                    return next.handle(request).pipe(
                        map((event: HttpEvent<any>) => {
                            if (event instanceof HttpResponse) {
                                // do nothing for now
                                setTimeout( () => {
                                    this.loadingCtrl.dismiss();
                               }, 1000);
                            }
                            return event;
                        }),
                        catchError((error: HttpErrorResponse) => {
                            const status =  error.status;
                            const reason = error && error.error.reason ? error.error.reason : '';

                            this.presentAlert(status, reason);
                            this.loadingCtrl.dismiss();
                            return throwError(error);
                        })
                    );

                })
            );
    }

Would you be so kind as to guide me in how to fix these two issues?

Kind Regards

I’m not sure I understand what happens without the timeout, but map is strictly for transforming foos into bars; it’s a bad place to put actions that have side effects (like dismissing a loading controller). I use the share operator and multiple subscriptions for similar purposes:

intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
  let rv$ = next.handle(req).pipe(share());
  rv$.subscribe(() => {
  }, (err) => {
    // handle errors here
  });
  return rv$;
}

Reference counting to the rescue. Here is a service I use for this purpose:

@Injectable()
export class NetmonService implements HttpInterceptor {
  private reqcnt = 0;
  private active$ = new BehaviorSubject<boolean>(false);

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    this.reqcnt++;
    if (!this.active$.value) {
      this.active$.next(true);
    }
    return next.handle(req).pipe(
      finalize(() => {
        this.reqcnt--;
        if (this.reqcnt === 0) {
          this.active$.next(false);
        }
      }));
  }

  watchActive(): Observable<boolean> {
    return this.active$;
  }

  isActive(): boolean {
    return this.active$.value;
  }
}

Your loading controller overlord could inject this service, subscribe to watchActive(), and pop loading controllers when it goes true and close them when it goes false.

Thank you, this guided me into the right direction.

Much appreciated!