00
:
00
:
00
:
00
Corso SEO AI - Usa SEOEMAIL al checkout per il 30% di sconto

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