I am working on an Angular 17 project and need to implement a loading spinner that displays during HTTP calls. I want the spinner to show when a request or several is made and hide once the response is received. I am looking for a clean and efficient way to achieve this.
Here is what I have so far: Demo@Stackblitz
app.component.html
@if (loading$ | async) {
<ng-container>
<div class="overlay"></div>
<app-spinner></app-spinner>
</ng-container>
}
<router-outlet></router-outlet>
app.component.ts
export class AppComponent {
loading$ = this.loadingService.loading$;
constructor(private loadingService: LoadingService) {}
}
main.ts
bootstrapApplication(AppComponent, appConfig)
.catch((err) => console.error(err));
app.config.ts
export const appConfig: ApplicationConfig = {
providers: [provideHttpClient(withInterceptors([networkInterceptorFn])),provideRouter(routes), provideAnimations(), provideAnimationsAsync(), ]
};
network.interceptor.ts
export const networkInterceptorFn: HttpInterceptorFn = (req, next) => {
let totalRequests = 0;
let requestsCompleted = 0;
const loadingService = inject(LoadingService);
loadingService.show();
totalRequests++;
return next(req).pipe(
finalize(() => {
requestsCompleted++;
if (requestsCompleted === totalRequests) {
loadingService.hide();
totalRequests = 0;
requestsCompleted = 0;
}
})
);
};
Function for testing the loading spinner:
constructor(private taskHttpService: TaskHttpService, private http: HttpClient) {
this.fetchDataTmp();
}
fetchDataTmp(): Observable<any>{
return this.http.get('https://jsonplaceholder.typicode.com/todos/1').pipe(
delay(5000)
);
}
You are not registering your NetworkInterceptor
into the application
Approach 1: Working with traditional HttpInterceptor
class.
import {
HTTP_INTERCEPTORS,
HttpClient,
provideHttpClient,
withInterceptorsFromDi,
} from '@angular/common/http';
import {
networkInterceptorFn
} from './app/network/network.interceptor';
bootstrapApplication(App, {
providers: [
provideHttpClient(withInterceptorsFromDi()),
{
provide: HTTP_INTERCEPTORS,
useClass: NetworkInterceptor,
multi: true,
},
],
});
Approach 2: Working with HttpInterceptorFn
.
Migrate the legacy HttpInterceptor
class to HttpInterceptorFn
.
export const networkInterceptorFn: HttpInterceptorFn = (req, next) => {
let totalRequests = 0;
let requestsCompleted = 0;
const loadingService = inject(LoadingService);
loadingService.show();
totalRequests++;
return next(req).pipe(
finalize(() => {
requestsCompleted++;
console.log(requestsCompleted, totalRequests);
if (requestsCompleted === totalRequests) {
loadingService.hide();
totalRequests = 0;
requestsCompleted = 0;
}
})
);
};
import {
HttpClient,
provideHttpClient,
withInterceptors
} from '@angular/common/http';
import {
networkInterceptorFn,
} from './app/network/network.interceptor';
bootstrapApplication(App, {
providers: [
provideHttpClient(
withInterceptors([networkInterceptorFn])
),
],
});
References: