BACK TO THE SERIESGuida galattica ad Angular · 銀河のガイド

Angular in 10 steps - 9 of 10

angular in 10 steps 9 of 10

09 - Interceptor e pipe: gli attrezzi di finitura


Penultimo capitolo del modulo base, e lo dedichiamo a due strumenti che non costruiscono pagine ma rendono migliore tutto il resto:


  • gli interceptor, per mettere le mani su ogni chiamata HTTP da un punto solo: token, log, gestione errori
  • le pipe, per trasformare i dati direttamente nel template: date, valute, testi da accorciare

Sono i classici attrezzi che, una volta scoperti, ti chiedi come facevi prima.


🚦 Interceptor: la dogana delle richieste


Un interceptor si mette in mezzo tra la tua app e il server: ogni richiesta HTTP in uscita, e ogni risposta in entrata, passa da lì. Come una dogana, può lasciar passare, modificare, registrare o respingere.


I casi d'uso reali sono sempre gli stessi quattro, e prima o poi ti serviranno tutti:


  • aggiungere il token di autenticazione a ogni chiamata
  • loggare richieste e tempi di risposta
  • gestire gli errori in un punto centrale (401 → redirect al login)
  • mostrare un loader globale quando c'è traffico in corso

Il punto è sempre lo stesso: senza interceptor, questa logica andrebbe copiata in ogni service. Con l'interceptor la scrivi una volta.


🛠 Esempio: il token su ogni richiesta


Oggi un interceptor è una semplice funzione di tipo HttpInterceptorFn: riceve la richiesta e la funzione next per farla proseguire.

TYPESCRIPT · コード
1// auth.interceptor.ts
2import { HttpInterceptorFn } from '@angular/common/http';
3
4export const authInterceptor: HttpInterceptorFn = (req, next) => {
5  const token = localStorage.getItem('token');
6
7  // le richieste HTTP sono immutabili: si clona, non si modifica
8  const reqClone = token
9    ? req.clone({ setHeaders: { Authorization: `Bearer ${token}` } })
10    : req;
11
12  return next(reqClone);
13};

Due dettagli da notare. Il primo: le richieste sono immutabili, quindi non si modificano ma si clonano con le differenze (req.clone(...)). Il secondo: essendo una funzione eseguita nel contesto di Angular, dentro un interceptor puoi usare inject(): un AuthService vero, al posto del localStorage, si inietta esattamente come nei capitoli scorsi.


Si registra in app.config.ts, agganciandolo al provideHttpClient che conosciamo dal capitolo 7:

TYPESCRIPT · コード
1import { provideHttpClient, withInterceptors } from '@angular/common/http';
2import { authInterceptor } from './auth.interceptor';
3
4export const appConfig: ApplicationConfig = {
5  providers: [provideHttpClient(withInterceptors([authInterceptor]))],
6};

Nei progetti meno recenti troverai gli interceptor come classi che implementano HttpInterceptor, registrate con il provider HTTP_INTERCEPTORS (o con withInterceptorsFromDi()). Stessa idea, più cerimonia: per il codice nuovo usa le funzioni.

Un secondo esempio, giusto per vedere che si può agire anche sulla risposta: un log dei tempi di ogni chiamata.

TYPESCRIPT · コード
1import { HttpInterceptorFn } from '@angular/common/http';
2import { finalize } from 'rxjs';
3
4export const timingInterceptor: HttpInterceptorFn = (req, next) => {
5  const start = Date.now();
6  return next(req).pipe(
7    finalize(() => console.log(`${req.method} ${req.url}: ${Date.now() - start}ms`))
8  );
9};

Gli interceptor si mettono in fila: withInterceptors([authInterceptor, timingInterceptor]) li esegue nell'ordine in cui li elenchi.


🪄 Pipe: formattare senza sporcare


Cambiamo completamente zona: dalle richieste HTTP al template. Le pipe trasformano un valore al momento di mostrarlo, con la sintassi |:

HTML · コード
<p>{{ oggi | date:'longDate' }}</p>
<p>{{ prezzo | currency:'EUR' }}</p>
<p>{{ nome | uppercase }}</p>

Il dato resta intatto nel componente; a cambiare è solo la presentazione. È la differenza tra tenere prezzo = 59.99 e ritrovarsi nel componente una stringa "59,99 €" che poi non puoi più usare per fare i conti.


Le pipe pronte all'uso che uso più spesso:


  • date: formatta date, con locale e formato configurabili
  • currency: numeri come valuta
  • uppercase / lowercase / titlecase: maiuscole e minuscole
  • number: separatori delle migliaia e decimali
  • json: stampa un oggetto come JSON, preziosa in fase di debug
  • async: si sottoscrive a un Observable per te. Se nel capitolo 7 il subscribe() manuale ti era sembrato rumoroso, questa è l'alternativa

Anche le pipe vanno importate nel componente che le usa, come tutto nel mondo standalone: imports: [DatePipe, CurrencyPipe] e così via. Se il template ti dà errore su una pipe "che dovrebbe esserci", è quasi sempre questo.

✍️ Scrivere una pipe personalizzata


Quando le pipe di serie non bastano, scriverne una è questione di minuti. Esempio classico: accorciare un testo troppo lungo.

TYPESCRIPT · コード
1import { Pipe, PipeTransform } from '@angular/core';
2
3@Pipe({ name: 'shorten' })
4export class ShortenPipe implements PipeTransform {
5  transform(value: string, limit = 10): string {
6    return value.length > limit ? value.slice(0, limit) + '…' : value;
7  }
8}

Nel template, dopo averla importata:

HTML · コード
<p>{{ descrizione | shorten:20 }}</p>

Un dettaglio che spiega perché le pipe sono così efficienti: di default sono pure, cioè Angular le riesegue solo quando il valore in ingresso cambia. Puoi metterne venti in una lista senza pensarci: non è lì che l'app rallenta.


Dove siamo arrivati


Con interceptor e pipe il modulo base è completo: componenti, router, signals, form, HTTP, stato condiviso e ora anche gli attrezzi trasversali per richieste e presentazione. Tutto quello che segue è mestiere e pratica.


E la pratica arriva subito: nel prossimo capitolo, l'ultimo della serie, mettiamo insieme ogni singolo pezzo e costruiamo un'app completa: una libreria digitale di videogiochi. Porta i dadi.