È uscito il Corso Java Completo — usa il coupon JAVA2026 (fino al 30 giugno)

Pipe

Cosa sono le Pipe

Le pipe sono funzioni di trasformazione utilizzate nei template Angular per formattare dati prima di visualizzarli. Sono indicate dal simbolo | (pipe) e possono essere concatenate.

<!-- Sintassi base -->
{{ valore | nomePipe }}

<!-- Con argomenti -->
{{ valore | nomePipe:argomento1:argomento2 }}

<!-- Pipe concatenate -->
{{ valore | pipe1 | pipe2 }}

Pipe Built-in

Angular fornisce numerose pipe pronte all’uso per le trasformazioni più comuni.

DatePipe

La pipe date formatta oggetti Date in stringhe leggibili.

import { Component } from '@angular/core';
import { DatePipe } from '@angular/common';

@Component({
  selector: 'app-esempio-date',
  standalone: true,
  imports: [DatePipe],
  template: `
    <h2>Formattazione Date</h2>

    <!-- Formati predefiniti -->
    <p>Short: {{ oggi | date:'short' }}</p>
    <p>Medium: {{ oggi | date:'medium' }}</p>
    <p>Long: {{ oggi | date:'long' }}</p>
    <p>Full: {{ oggi | date:'full' }}</p>

    <!-- Solo data -->
    <p>shortDate: {{ oggi | date:'shortDate' }}</p>
    <p>mediumDate: {{ oggi | date:'mediumDate' }}</p>
    <p>longDate: {{ oggi | date:'longDate' }}</p>

    <!-- Solo ora -->
    <p>shortTime: {{ oggi | date:'shortTime' }}</p>
    <p>mediumTime: {{ oggi | date:'mediumTime' }}</p>

    <!-- Formato personalizzato -->
    <p>Custom: {{ oggi | date:'dd/MM/yyyy' }}</p>
    <p>Custom: {{ oggi | date:'EEEE d MMMM yyyy, HH:mm' }}</p>
    <p>Custom: {{ oggi | date:'dd-MM-yy HH:mm:ss' }}</p>

    <!-- Con locale -->
    <p>Italiano: {{ oggi | date:'fullDate':'':'it' }}</p>
  `
})
export class EsempioDateComponent {
  oggi = new Date();
}

CurrencyPipe

La pipe currency formatta numeri come valute.

import { Component } from '@angular/core';
import { CurrencyPipe } from '@angular/common';

@Component({
  selector: 'app-esempio-currency',
  standalone: true,
  imports: [CurrencyPipe],
  template: `
    <!-- Default (USD) -->
    <p>{{ prezzo | currency }}</p>

    <!-- Euro -->
    <p>{{ prezzo | currency:'EUR' }}</p>

    <!-- Con simbolo -->
    <p>{{ prezzo | currency:'EUR':'symbol' }}</p>

    <!-- Con codice -->
    <p>{{ prezzo | currency:'EUR':'code' }}</p>

    <!-- Con formato decimali (minIntDigits.minFracDigits-maxFracDigits) -->
    <p>{{ prezzo | currency:'EUR':'symbol':'1.2-2' }}</p>

    <!-- Sterlina -->
    <p>{{ prezzo | currency:'GBP' }}</p>

    <!-- Yen senza decimali -->
    <p>{{ prezzoYen | currency:'JPY':'symbol':'1.0-0' }}</p>
  `
})
export class EsempioCurrencyComponent {
  prezzo = 1299.99;
  prezzoYen = 150000;
}

Pipe per Stringhe

import { Component } from '@angular/core';
import { UpperCasePipe, LowerCasePipe, TitleCasePipe, SlicePipe } from '@angular/common';

@Component({
  selector: 'app-esempio-stringhe',
  standalone: true,
  imports: [UpperCasePipe, LowerCasePipe, TitleCasePipe, SlicePipe],
  template: `
    <p>Originale: {{ testo }}</p>
    <p>Maiuscolo: {{ testo | uppercase }}</p>
    <p>Minuscolo: {{ testo | lowercase }}</p>
    <p>Title Case: {{ testo | titlecase }}</p>

    <!-- Slice (sottostringa) -->
    <p>Primi 5 caratteri: {{ testo | slice:0:5 }}</p>
    <p>Da posizione 6: {{ testo | slice:6 }}</p>
    <p>Ultimi 6 caratteri: {{ testo | slice:-6 }}</p>
  `
})
export class EsempioStringheComponent {
  testo = 'ciao mondo angular';
}

DecimalPipe e PercentPipe

import { Component } from '@angular/core';
import { DecimalPipe, PercentPipe } from '@angular/common';

@Component({
  selector: 'app-esempio-numeri',
  standalone: true,
  imports: [DecimalPipe, PercentPipe],
  template: `
    <!-- DecimalPipe: formato 'minIntDigits.minFracDigits-maxFracDigits' -->
    <p>{{ numero | number }}</p>
    <p>{{ numero | number:'3.2-5' }}</p>
    <p>{{ numeroPiccolo | number:'1.0-3' }}</p>

    <!-- Con separatore locale italiano -->
    <p>{{ numeroGrande | number:'1.2-2':'it' }}</p>

    <!-- PercentPipe -->
    <p>{{ percentuale | percent }}</p>
    <p>{{ percentuale | percent:'2.2-2' }}</p>

    <!-- Percentuali pratiche -->
    <p>Progresso: {{ 0.756 | percent:'1.0-0' }}</p>
    <p>Sconto: {{ 0.15 | percent }}</p>
  `
})
export class EsempioNumeriComponent {
  numero = 3.14159;
  numeroPiccolo = 0.123456;
  numeroGrande = 1234567.89;
  percentuale = 0.259;
}

JsonPipe e KeyValuePipe

import { Component } from '@angular/core';
import { JsonPipe, KeyValuePipe } from '@angular/common';

@Component({
  selector: 'app-esempio-json',
  standalone: true,
  imports: [JsonPipe, KeyValuePipe],
  template: `
    <!-- JsonPipe: utile per il debug -->
    <pre>{{ utente | json }}</pre>

    <!-- KeyValuePipe: iterare su oggetti -->
    <dl>
      @for (voce of configurazione | keyvalue; track voce.key) {
        <dt>{{ voce.key }}</dt>
        <dd>{{ voce.value }}</dd>
      }
    </dl>
  `
})
export class EsempioJsonComponent {
  utente = {
    nome: 'Marco',
    cognome: 'Bianchi',
    eta: 30,
    ruolo: 'Sviluppatore'
  };

  configurazione: Record<string, string | number> = {
    tema: 'scuro',
    lingua: 'italiano',
    timeout: 3000,
    versione: '2.1.0'
  };
}

Pipe Personalizzate

Creare una Pipe Custom

import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'tronca',
  standalone: true
})
export class TroncaPipe implements PipeTransform {
  transform(valore: string, lunghezzaMax: number = 50, suffisso: string = '...'): string {
    if (!valore) return '';
    if (valore.length <= lunghezzaMax) return valore;
    return valore.substring(0, lunghezzaMax).trim() + suffisso;
  }
}

Utilizzo:

<p>{{ descrizione | tronca }}</p>
<p>{{ descrizione | tronca:100 }}</p>
<p>{{ descrizione | tronca:30:' [...]' }}</p>

Pipe per il Tempo Relativo

import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'tempoFa',
  standalone: true
})
export class TempoFaPipe implements PipeTransform {
  transform(valore: Date | string): string {
    const data = new Date(valore);
    const ora = new Date();
    const differenzaMs = ora.getTime() - data.getTime();
    const secondi = Math.floor(differenzaMs / 1000);
    const minuti = Math.floor(secondi / 60);
    const ore = Math.floor(minuti / 60);
    const giorni = Math.floor(ore / 24);
    const mesi = Math.floor(giorni / 30);
    const anni = Math.floor(giorni / 365);

    if (secondi < 60) return 'pochi secondi fa';
    if (minuti < 60) return `${minuti} minut${minuti === 1 ? 'o' : 'i'} fa`;
    if (ore < 24) return `${ore} or${ore === 1 ? 'a' : 'e'} fa`;
    if (giorni < 30) return `${giorni} giorn${giorni === 1 ? 'o' : 'i'} fa`;
    if (mesi < 12) return `${mesi} mes${mesi === 1 ? 'e' : 'i'} fa`;
    return `${anni} ann${anni === 1 ? 'o' : 'i'} fa`;
  }
}
<p>Pubblicato {{ dataArticolo | tempoFa }}</p>
<!-- Output: "Pubblicato 3 ore fa" -->

Pipe di Filtro

import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'filtra',
  standalone: true
})
export class FiltraPipe implements PipeTransform {
  transform<T>(items: T[], campo: keyof T, valore: string): T[] {
    if (!items || !valore) return items;

    const termineMinuscolo = valore.toLowerCase();
    return items.filter(item => {
      const valCampo = String(item[campo]).toLowerCase();
      return valCampo.includes(termineMinuscolo);
    });
  }
}
<input [(ngModel)]="termineRicerca" placeholder="Cerca...">

@for (prodotto of prodotti | filtra:'nome':termineRicerca; track prodotto.id) {
  <div>{{ prodotto.nome }} - {{ prodotto.prezzo | currency:'EUR' }}</div>
}

Pipe Pure vs Impure

Pipe Pure (default)

Le pipe pure vengono eseguite solo quando Angular rileva un cambiamento nel valore di input o nei suoi argomenti. Per gli oggetti e gli array, viene controllata solo la referenza (non il contenuto).

@Pipe({
  name: 'filtraPure',
  standalone: true,
  pure: true // default
})
export class FiltraPurePipe implements PipeTransform {
  transform(items: any[], filtro: string): any[] {
    console.log('Pipe pure eseguita'); // Chiamata raramente
    return items.filter(i => i.nome.includes(filtro));
  }
}

Pipe Impure

Le pipe impure vengono eseguite ad ogni ciclo di change detection, anche se l’input non è cambiato. Usale con cautela per ragioni di performance.

@Pipe({
  name: 'filtraImpure',
  standalone: true,
  pure: false // Eseguita ad ogni change detection
})
export class FiltraImpurePipe implements PipeTransform {
  transform(items: any[], filtro: string): any[] {
    console.log('Pipe impure eseguita'); // Chiamata frequentemente
    return items.filter(i => i.nome.includes(filtro));
  }
}

Quando usare pipe impure:

  • Quando la pipe dipende da stato esterno (es. un servizio)
  • Quando l’input e un array/oggetto mutato internamente senza cambiare referenza

Soluzione alternativa alle pipe impure

Invece di usare pipe impure, crea un nuovo array quando i dati cambiano:

// Nel componente, crea un nuovo riferimento quando muti l'array
aggiungiProdotto(prodotto: Prodotto): void {
  this.prodotti = [...this.prodotti, prodotto]; // Nuovo riferimento
  // NON: this.prodotti.push(prodotto); // Stesso riferimento, pipe pure non si attiva
}

Async Pipe

La async pipe si sottoscrive automaticamente a un Observable o Promise e restituisce l’ultimo valore emesso. Gestisce automaticamente la cancellazione della sottoscrizione quando il componente viene distrutto.

import { Component, OnInit } from '@angular/core';
import { AsyncPipe, DatePipe, CurrencyPipe } from '@angular/common';
import { Observable, interval, map } from 'rxjs';
import { HttpClient } from '@angular/common/http';

interface Utente {
  id: number;
  nome: string;
  email: string;
}

@Component({
  selector: 'app-esempio-async',
  standalone: true,
  imports: [AsyncPipe, DatePipe, CurrencyPipe],
  template: `
    <!-- Con Observable -->
    @if (utenti$ | async; as utenti) {
      @for (utente of utenti; track utente.id) {
        <div>{{ utente.nome }} - {{ utente.email }}</div>
      }
    } @else {
      <p>Caricamento utenti...</p>
    }

    <!-- Timer con async pipe -->
    <p>Secondi trascorsi: {{ timer$ | async }}</p>

    <!-- Async pipe con altre pipe -->
    <p>Orario: {{ oraCorrente$ | async | date:'HH:mm:ss' }}</p>

    <!-- Con Promise -->
    <p>Messaggio: {{ messaggioPromise | async }}</p>
  `
})
export class EsempioAsyncComponent implements OnInit {
  utenti$!: Observable<Utente[]>;
  timer$!: Observable<number>;
  oraCorrente$!: Observable<Date>;
  messaggioPromise!: Promise<string>;

  constructor(private http: HttpClient) {}

  ngOnInit(): void {
    // Observable da HTTP
    this.utenti$ = this.http.get<Utente[]>('/api/utenti');

    // Timer che emette ogni secondo
    this.timer$ = interval(1000);

    // Ora corrente aggiornata ogni secondo
    this.oraCorrente$ = interval(1000).pipe(
      map(() => new Date())
    );

    // Promise
    this.messaggioPromise = new Promise(resolve => {
      setTimeout(() => resolve('Dati caricati!'), 2000);
    });
  }
}

Vantaggi dell’async pipe

L’async pipe è la soluzione raccomandata perche:

  1. Gestisce automaticamente la sottoscrizione e la cancellazione
  2. Previene memory leak senza bisogno di ngOnDestroy
  3. Funziona con OnPush change detection strategy
  4. Riduce il codice boilerplate nel componente
// SENZA async pipe (più codice, rischio memory leak)
export class SenzaAsyncComponent implements OnInit, OnDestroy {
  utenti: Utente[] = [];
  private sub!: Subscription;

  ngOnInit(): void {
    this.sub = this.http.get<Utente[]>('/api/utenti')
      .subscribe(utenti => this.utenti = utenti);
  }

  ngOnDestroy(): void {
    this.sub.unsubscribe(); // Necessario!
  }
}

// CON async pipe (più pulito, nessun memory leak)
export class ConAsyncComponent {
  utenti$ = this.http.get<Utente[]>('/api/utenti');
  constructor(private http: HttpClient) {}
}

Best Practices

  • Preferisci le pipe pure per le prestazioni; usa pipe impure solo quando strettamente necessario
  • Usa la async pipe per gestire Observable nei template e evitare memory leak
  • Le pipe devono essere funzioni pure senza effetti collaterali
  • Crea pipe personalizzate per formattazioni riutilizzabili nell’applicazione
  • Non usare pipe di filtro o ordinamento su liste grandi: sposta la logica nel componente
  • Concatena le pipe quando serve: {{ data | date:'short' | uppercase }}
  • Testa le pipe personalizzate con test unitari: sono semplici da testare poiche sono funzioni pure