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
main.ts
@Component({
selector: 'app-root',
standalone: true,
imports: [AsyncPipe, SpinnerComponent],
template: `
@if (loading$ | async) {
<ng-container>
<div class="overlay"></div>
<app-spinner></app-spinner>
</ng-container>
}
<button (click)="fetchMultipleData()">Fetch Multiple Data</button>
`,
})
export class App {
name = 'Angular';
loading$ = this.loadingService.loading$;
constructor(
private loadingService: LoadingService,
private http: HttpClient
) {}
fetchMultipleData() {
this.http
.get('https://api.github.com/users/thisiszoaib')
.subscribe((res) => {
console.log(res);
});
this.http
.get('https://api.github.com/users/thisiszoaib')
.pipe(concatMap(() => this.http.get('https://api.github.com/users')))
.subscribe((res) => {
console.log(res);
});
network.interceptor.ts
@Injectable()
export class NetworkInterceptor implements HttpInterceptor {
totalRequests = 0;
requestsCompleted = 0;
constructor(private loadingService: LoadingService) {}
intercept(
request: HttpRequest<unknown>,
next: HttpHandler
): Observable<HttpEvent<unknown>> {
this.loadingService.show();
this.totalRequests++;
return next.handle(request).pipe(
finalize(() => {
this.requestsCompleted++;
console.log(this.requestsCompleted, this.totalRequests);
if (this.requestsCompleted === this.totalRequests) {
this.loadingService.hide();
this.totalRequests = 0;
this.requestsCompleted = 0;
}
})
);
}
}
loading.service.ts
export class LoadingService {
private _loading = new BehaviorSubject<boolean>(false);
public readonly loading$ = this._loading.asObservable().pipe(delay(1));
constructor() {}
public show() {
this._loading.next(true);
}
public hide() {
this._loading.next(false);
}
}