Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

440
Views
Angular show spinner for every HTTP request with very less code changes

I am working on an existing Angular application. The version is Angular 4.

The application makes HTTP calls to a REST API from lot of various components.

I want to show a custom spinner for every HTTP request. Since this is an existing application, there are lot of places where calls to REST API are made. And changing code one at every places is not a feasible option.

I would like to implement an abstract solution which would solve this problem.

Please suggest if any options.

over 4 years ago · Santiago Trujillo
3 answers
Answer question

0

@jornare has a good idea in his solution. He's handling the case for multiple requests. However, the code could be written simpler, without creating new observable and storing requests in memory. The below code also uses RxJS 6 with pipeable operators:

import { Injectable } from '@angular/core';
import {
  HttpRequest,
  HttpHandler,
  HttpInterceptor,
  HttpResponse
} from '@angular/common/http';
import { finalize } from 'rxjs/operators';
import { LoadingService } from '@app/services/loading.service';
import { of } from 'rxjs';

@Injectable()
export class LoadingInterceptor implements HttpInterceptor {
  private totalRequests = 0;

  constructor(private loadingService: LoadingService) { }

  intercept(request: HttpRequest<any>, next: HttpHandler) {
    this.totalRequests++;
    this.loadingService.setLoading(true);

    return next.handle(request).pipe(
      finalize(res => {
        this.totalRequests--;
        if (this.totalRequests === 0) {
          this.loadingService.setLoading(false);
        }
      })
    );
  }
}

Add this interceptor service into your module providers:

@NgModule({
  // ...
  providers: [
    { provide: HTTP_INTERCEPTORS, useClass: LoadingInterceptor, multi: true }
  ]
})
export class AppModule { }

Here's an example of the LoadingService implementation:

@Injectable()
export class LoadingService {
  private isLoading$$ = new BehaviorSubject<boolean>(false);
  isLoading$ = this.isLoading$$.asObservable();
  
  setLoading(isLoading: boolean) {
    this.isLoading$$.next(isLoading);
  }
}

And here's how you'd use the LoadingService in a component:

@Component({
  selector: 'app-root',
  template: `
    <ng-container *ngIf="loadingService.isLoading$ | async">
      <i class="loading"></i>
    </ng-container>
    <router-outlet></router-outlet>
  `,
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class AppComponent {
  constructor(public loadingService: LoadingService) {}
}
over 4 years ago · Santiago Trujillo Report

0

Angular 4+ has a new HttpClient which supports HttpInterceptors. This allows you to insert code that will be run whenever you make a HTTP request.

It is important to notice that HttpRequest are not long-lived Observables, but they terminate after the response. Furthermore, if the observable is unsubscribed before the response has returned, the request is cancelled and neither of the handlers are being processed. You may therefore end up with a "hanging" loader bar, which never goes away. This typically happens if you navigate a bit fast in your application.

To get around this last issue, we need to create a new Observable to be able to attach teardown-logic.

We return this rather than the original Observable. We also need to keep track of all requests made, because we may run more than one request at a time.

We also need a service which can hold and share the state of whether we have pending requests.

@Injectable()
export class MyLoaderService {
    // A BehaviourSubject is an Observable with a default value
    public isLoading = new BehaviorSubject<boolean>(false);

    constructor() {}
}

The Interceptor uses the MyLoaderService

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

    constructor(private loaderService: MyLoaderService) { }

    removeRequest(req: HttpRequest<any>) {
        const i = this.requests.indexOf(req);
        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 => { this.removeRequest(req); observer.error(err); },
            () => { this.removeRequest(req); observer.complete(); });
          // teardown logic in case of cancelled requests
          return () => {
            this.removeRequest(req);
            subscription.unsubscribe();
          };
        });
    }
}

Finally, in our Component, we can use the same MyLoaderService and with the async operator we do not even need to subscribe. Since the source value we want to use is from a service, it should be shared as an Observable so that it gets a rendering scope/zone where it is used. If it is just a value, it may not update your GUI as wanted.

@Component({...})
export class MyComponent {
    constructor(public myLoaderService: MyLoaderService) {}
}

And an example template using async

<div class="myLoadBar" *ngIf="myLoaderService.isLoading | async">Loading!</div>

I assume you know how to provide services and set up modules properly. You can also see a working example at Stackblitz

over 4 years ago · Santiago Trujillo Report

0

In Angular 5 comes the HttpClient module. You can find more information there.

With this module, come something called interceptors.

They allow you to do something for every HTTP request.

If you migrate from Http to HttpClient (and you should, Http will be deprecated), you can create an interceptor that can handle a variable in a shared service :

intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    this.sharedService.loading = true;
    return next
      .handle(req)
      .finally(() => this.sharedService.loading = false);
}

Now you just have to use this variable into your templates.

<spinner *ngIf="sharedService.loading"></spinner>

(Be sure to have an injection of your service in the components that display this spinner)

over 4 years ago · Santiago Trujillo Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!