Come Utilizzare un Interceptor in Angular con Signals e Standalone Components per Mostrare un Loader Dinamico

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à.

Blog

Altri articoli che potrebbero interessarti