Reputation: 1456
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:
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;
}
Upvotes: 9
Views: 15982
Reputation: 802
For anyone who has the problem with the counter never reaching zero again, even though no requests are pending: I had to additionaly check for the type of event when increasing the counter:
if (event instanceof HttpResponse) {
this.counter.dec();
} else {
this.counter.inc();
}
Otherwise, i had the case of a HttpResponse also increasing my counter. With the above check my counter is going back to zero on all my components.
Also, make sure a returning http error (e.g. 401) is also decreasing your counter, otherwise the counter will never reach zero again. To do so:
return next.handle(req).pipe(tap(
(event: HttpEvent<any>) => {
if (event instanceof HttpResponse) {
this.counter.dec();
}
},
err => {
if (err instanceof HttpErrorResponse) {
this.counter.dec();
}
}
));
Upvotes: 0
Reputation: 58039
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;
}
Upvotes: 2
Reputation: 1
For everyone following up to this issue, the OP's code now is working fine, except for the remaining issue that the loader doesn't seem to hide. The fix to that is to subscribe to the Observable, after the .catch .do chain, like so:
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();
}
})
.subscribe(); /* <---------- ADD THIS */
return handleObs;
After this, the code should be working fine, and the loader will hide when the counter reaches 0. Thanks to all the above answers for their contribution as well!
Upvotes: 0
Reputation: 1456
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.
Upvotes: 5