Angular HTTP Interceptor - Display spinner in multi-module app
Solution 1
The issue was the ApiService
was using the Http
from @angular/http
instead of HttpClient
from @angular/common/http
.
So the ApiInterceptor
has nothing to intercept.
Solution 2
forget reportProgress:true. The problem is that we have to discriminate the event of "do". Moreover, we must take a count of the calls, so the interceptor must be like
contador: number = 0;
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
this.contador++;
if (this.contador === 1) {
this.spinner.show();
}
let handleObs: Observable<HttpEvent<any>> = next.handle(req);
handleObs
.catch((err: any) => { //If an error happens, this.contador-- too
this.contador--;
return Observable.throw(err);
})
.do(event => {
if (event instanceof HttpResponse) { //<--only when event is a HttpRespose
this.contador--;
if (this.contador==0)
this.spinner.hide();
}
});
return handleObs;
}
TheMagnificent11
I've worked in .Net roles since 2007 (mainly ASP.Net but also some WinForms back in the day). I do mainly ASP.Net MVC and Web API development in my current role. However, what I enjoy most is working with Angular (2+) and ASP.Net Core for my API, which is the tech stack I'm using in my first Github repo: https://github.com/TheMagnificent11/LunchVoting
Updated on July 26, 2022Comments
-
TheMagnificent11 almost 2 years
I'm trying to display the
ng4-loading-spinner
spinner for HTTP calls made to my API.I based my code on the examples in the following links:
- https://angular.io/guide/http#intercepting-all-requests-or-responses
- Angular4: Using HttpClient's interceptor to setup a spinner
My Angular 5 app has multiple multiple modules. The HTTP interceptor is in the "services" module.
I think I'm having a dependency injection problem because the code HTTP interceptor code doesn't get executed when I debug my code with Chrome Dev Tools.
api-interceptor.ts
import 'rxjs/add/operator/do'; import 'rxjs/add/operator/catch' import { Observable } from 'rxjs/Observable'; import { Injectable } from '@angular/core'; import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest, HttpResponse } from '@angular/common/http'; import { Ng4LoadingSpinnerService } from 'ng4-loading-spinner'; @Injectable() export class ApiInterceptor implements HttpInterceptor { private count: number = 0; constructor(private spinner: Ng4LoadingSpinnerService) { } intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { this.count++; if (this.count == 1) this.spinner.show(); let handleObs: Observable<HttpEvent<any>> = next.handle(req); handleObs .catch((err: any) => { this.count--; return Observable.throw(err); }) .do(event => { if (event instanceof HttpResponse) { this.count--; if (this.count == 0) this.spinner.hide(); } }); return handleObs; } }
api.service.ts
import { Injectable, Inject } from '@angular/core'; import { Http, Response, Headers, RequestOptions } from '@angular/http'; import { Observable } from 'rxjs/Observable'; import { TokenService } from './token.service'; @Injectable() export class ApiService { constructor( private http: Http, private session: TokenService, @Inject('BASE_URL') private baseUrl) { } get(entityRoute: string): Observable<Response> { let apiRoute = this.getApiRoute(entityRoute); let options = this.generateRequestOptions(); return this.http.get(apiRoute, options); } post<T>(entityRoute: string, entity: T): Observable<Response> { let apiRoute = this.getApiRoute(entityRoute); let options = this.generateRequestOptions(); return this.http.post(apiRoute, entity, options); } put<T>(entityRoute: string, entity: T): Observable<Response> { let apiRoute = this.getApiRoute(entityRoute); let options = this.generateRequestOptions(); return this.http.post(apiRoute, entity, options); } private getApiRoute(entityRoute: string): string { return `${this.baseUrl}api/${entityRoute}`; } private generateRequestOptions(): RequestOptions { let headersObj = null; let accessToken = this.session.getAccessToken(); if (accessToken) { headersObj = { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + accessToken }; } else { headersObj = { 'Content-Type': 'application/json' }; } let headers = new Headers(headersObj); return new RequestOptions({ headers: headers }); } }
services.module.ts
import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { HttpModule } from '@angular/http'; import { Ng4LoadingSpinnerModule } from 'ng4-loading-spinner'; import { ApiInterceptor, ApiService, TokenService } from './index'; @NgModule({ imports: [ CommonModule, HttpModule, Ng4LoadingSpinnerModule ], providers: [ ApiInterceptor, ApiService, TokenService ] }) export class ServicesModule { } export * from './index';
app.module.ts
import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { HTTP_INTERCEPTORS } from '@angular/common/http'; import { Ng4LoadingSpinnerModule } from 'ng4-loading-spinner'; import { BootstrapModule } from './bootstrap/bootstrap.module'; import { ServicesModule, ApiInterceptor } from './services/services.module'; import { AppComponent } from './app-component'; @NgModule({ bootstrap: [ AppComponent ], imports: [ BrowserModule, Ng4LoadingSpinnerModule.forRoot(), BootstrapModule, ServicesModule ], providers: [ { provide: 'BASE_URL', useFactory: getBaseUrl }, { provide: HTTP_INTERCEPTORS, useClass: ApiInterceptor, multi: true, } ] }) export class AppModule { } export function getBaseUrl(): string { return document.getElementsByTagName('base')[0].href; }
-
TheMagnificent11 over 6 yearsI added this code but it doesn't solve my dependency injection problem. The
ApiInterceptor
never gets hits (when I debug) and the spinner never shows. I have updated my question to include your code. -
Eliseo over 6 yearssorry, I feel stupid for not having noticed before: interceptor only work using HttpClient (NOT http). Change your ApiService constructor
-
TheMagnificent11 over 6 yearsSwitching the
HttpClient
works...thanks. However, the spinner never gets hidden. Isdo
the appropriate place to hide the spinner? -
Eliseo over 6 yearsI don't be sure, perhaps using finally... I just "ajust" the example of angular.io/guide/http#logging
-
TheMagnificent11 over 6 yearsThanks, that worked. Do you want to post another answer saying that the issue is that I need to use
HttpClient
instead ofHttp
in myApiService
and I'll mark it as the solution? -
nick gowdy over 5 yearsWhen I add subscribe to the chain, I see in the network tab of my browser that it's doing all requests twice. Does anybody know why this would be?
-
Geiv Tomço over 5 years@nickgowdy Can you please provide us a screenshot of the network tab? Note that the whole code above, including the .subscribe() method, only "listens" to the main Http calls, it doesn't mutate them as far as this code is concerned. We can only assume that you are referring to the fact that XHR requests generate two requests, one being OPTION (not the actual request, just headers prior to the real request) and the other being the actual request (either GET/POST etc. with the actual data). Anyway I assume we need a bit more information please. Best regards!
-
nick gowdy over 5 yearsDo you want me to create a seperate SO post?
-
nick gowdy over 5 yearsI've created a new post with my code: stackoverflow.com/questions/52876207/…
-
Admin almost 4 yearsIt is blinking if so lot fast requests/responses
-
Eliseo almost 4 years@MeganClerc, there're another aproach, for me the best, that it's create a new operator -no interceptor-, see stackoverflow.com/questions/60207721/…