Con l’introduzione di Angular 17 e le novità come Signals e Standalone Components, la gestione dello stato
e la struttura delle applicazioni sono diventate più semplici e reattive. In questa guida, ti mostrerò come
implementare un interceptor HTTP per mostrare un loader dinamico durante le chiamate HTTP, sfruttando
Signals per la gestione dello stato e Standalone Components per una struttura più snella.
1. Creare un Servizio per il Loader con Signals
Iniziamo creando un servizio che utilizza Signals per gestire lo stato del loader.
Passo 1: Generare il Servizio
Esegui il seguente comando:
ng generate service loader
Passo 2: Implementare il Servizio con Signals
Nel file loader.service.ts, utilizza signal per gestire lo stato del loader:
import { Injectable, signal } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class LoaderService {
isLoading = signal<boolean>(false);
show(): void {
this.isLoading.set(true);
}
hide(): void {
this.isLoading.set(false);
}
}
2. Creare un Standalone Component per il Loader
Ora, creiamo un Standalone Component che visualizzi il loader quando isLoading è true.
Passo 1: Generare il Component
Esegui il seguente comando:
ng generate component loader --standalone
Passo 2: Implementare il Component
Nel file loader.component.ts, importa il servizio e utilizza isLoading per controllare la visibilità del loader:
import { Component, inject } from '@angular/core';
import { LoaderService } from './loader.service';
import { ProgressSpinnerModule } from 'primeng/progressspinner';
@Component({
selector: 'app-loader',
standalone: true,
imports: [ProgressSpinnerModule],
template: `
@if (loaderService.isLoading()) {
<div class="loader-overlay">
<div class="loader-spinner">
<p-progressSpinner></p-progressSpinner>
</div>
</div>
}
`,
styles: [`
.loader-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
z-index: 9999;
}
.loader-spinner {
color: white;
}
`]
})
export class LoaderComponent {
loaderService = inject(LoaderService);
}
3. Creare l’Interceptor HTTP
Ora, creiamo l’interceptor che intercetterà tutte le chiamate HTTP e aggiornerà lo stato del loader.
Passo 1: Generare l’Interceptor
Esegui il seguente comando:
ng generate interceptor loader
Passo 2: Implementare l’Interceptor
Nel file loader.interceptor.ts, implementa la logica per mostrare e nascondere il loader:
import { Injectable, inject } from '@angular/core';
import {
HttpRequest,
HttpHandler,
HttpEvent,
HttpInterceptor
} from '@angular/common/http';
import { Observable } from 'rxjs';
import { finalize } from 'rxjs/operators';
import { LoaderService } from './loader.service';
@Injectable()
export class LoaderInterceptor implements HttpInterceptor {
private loaderService = inject(LoaderService);
intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
// Mostra il loader
this.loaderService.show();
return next.handle(request).pipe(
finalize(() => {
// Nasconde il loader quando la richiesta è completata
this.loaderService.hide();
})
);
}
}
4. Configurare l’Interceptor in un’Applicazione Standalone
Con Angular 17 e i Standalone Components, la configurazione dell’interceptor avviene direttamente nel file
main.ts o app.config.ts.
Passo 1: Configurare l’Interceptor in app.config.ts
Crea o modifica il file app.config.ts per includere l’interceptor:
import { ApplicationConfig, provideHttpClient, withInterceptorsFromDi } from '@angular/common/http';
import { provideRouter } from '@angular/router';
import { routes } from './app.routes';
import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { LoaderInterceptor } from './loader.interceptor';
export const appConfig: ApplicationConfig = {
providers: [
provideRouter(routes),
provideHttpClient(withInterceptorsFromDi()),
{ provide: HTTP_INTERCEPTORS, useClass: LoaderInterceptor, multi: true }
]
};
5. Includere il Loader Component nell’App
Ora, includi il componente LoaderComponent nel template principale dell’applicazione.
Passo 1: Aggiungere il Component in app.component.ts
Assicurati che app.component.ts sia un Standalone Component e importi LoaderComponent:
import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { LoaderComponent } from './loader/loader.component';
@Component({
selector: 'app-root',
standalone: true,
imports: [RouterOutlet, LoaderComponent],
template: `
<app-loader></app-loader>
<router-outlet></router-outlet>
`,
})
export class AppComponent {}
6. Testare l’Implementazione
Ora, ogni volta che viene effettuata una chiamata HTTP, il loader verrà visualizzato automaticamente fino al
completamento della richiesta.
Esempio di Chiamata HTTP
Ecco un esempio di servizio che effettua una chiamata HTTP:
import { Injectable, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class DataService {
private http = inject(HttpClient);
private apiUrl = 'https://jsonplaceholder.typicode.com/posts';
getPosts(): Observable<any> {
return this.http.get(this.apiUrl);
}
}
Quando chiami getPosts() da un componente, il loader apparirà automaticamente e scomparirà al termine
della richiesta.
7. Ottimizzazioni e Considerazioni
Escludere Alcune Richieste
Se non vuoi che il loader appaia per alcune richieste (es. chiamate di polling o richieste in background), puoi
modificare l’interceptor per ignorarle:
intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
// Ignora le richieste con un header specifico
if (request.headers.get('skip-loader')) {
return next.handle(request);
}
this.loaderService.show();
return next.handle(request).pipe(
finalize(() => {
this.loaderService.hide();
})
);
}
Gestione degli Errori
Se vuoi assicurarti che il loader venga nascosto anche in caso di errore, puoi utilizzare l’operatore
catchError:
import { catchError } from 'rxjs/operators';
import { throwError } from 'rxjs';
// ...
return next.handle(request).pipe(
catchError(error => {
this.loaderService.hide();
return throwError(() => error);
}),
finalize(() => {
this.loaderService.hide();
})
);
Conclusione
Con Angular 17, Signals e Standalone Components, la gestione di un loader dinamico durante le chiamate
HTTP diventa più semplice e reattiva. Utilizzando un interceptor HTTP e un servizio basato su Signals,
puoi:
- Centralizzare la logica di visualizzazione del loader.
- Evitare la duplicazione di codice.
- Migliorare l’esperienza utente con un feedback visivo immediato.
Questa soluzione è moderna, efficiente e scalabile, perfetta per applicazioni Angular che puntano alla
massima reattività e usabilità.