La versione 17 di Angular è stata rilasciata pochi giorni fa insieme ad un restyle grafico del logo e al nuovo blog angular.dev.
In questo articolo analizzeremo insieme il nuovo control flow, che ci darà la possibilità di dimenticarci delle direttive ngIf, ngSwitch e ngFor grazie ad una nuova sintassi per scrivere condizioni if, if/else, switch e il ciclo for nei nostri template.
⚠️ ATTENZIONE: il nuovo control flow è attualmente in developer preview ⚠️
Senza dilungarmi oltre, iniziamo subito il nostro viaggio dalla condizione più basilare: @if.
@if condition
Per mostrare una determinata porzione del nostro template al verificarsi di una determinata condizione in Angular ci siamo sempre serviti della direttiva ngIf:
@Component({
standalone: true,
template: `<div *ngIf="condition"> ... </div>`,
imports: [NgIf]
})
export class MyComponent {}
Code language: TypeScript (typescript)
Questa semplice direttiva ha però alcuni punti negativi:
- La sintassi risulta poco intuitiva e troppo legata al framework;
- Non possiamo applicarla su gruppi di elementi, quindi siamo obbligati ad usare un elemento contenitore o ng-template/ng-container;
- Dobbiamo importare il CommonModule o la direttiva NgIf nei moduli.
La nuova condizione @if ci permette di ottenere lo stesso risultato:
@if (condition) {
* your content *
}
Code language: HTML, XML (xml)
@if offre una sintassi molto più pulita, i blocchi condizionali risaltano immediatamente durante la lettura del template, offrendoci la possibilità di raggruppare gruppi di elementi all’interno delle parentesi graffe e soprattutto: niente più moduli o direttive da importare.
@else e @else/if
Creare condizioni if/else è sempre stata una agonia in Angular:
<div *ngIf="condition; else otherTemplate">
* your content *
</div>
<ng-template #otherTemplate>
* your other content *
</ng-template>
Code language: HTML, XML (xml)
Dover utilizzare un ng-template in combinazione con una template variable per fornire alla direttiva ngIf un blocco di riserva ha sempre reso il codice poco intuitivo.
Ed è qui che @if viene maggiormente in nostro soccorso poiché può essere esteso utilizzando la condizione @else:
@if (condition) {
* your content *
} @else {
* your other content *
}
Code language: HTML, XML (xml)
Ma non è tutto, @if infatti accetta la possibilità di concatenare ulteriori condizioni @else/if:
@if (condition) {
* your content *
} @else if (secondCondition) {
* your second content *
} @else {
* your other content *
}
Code language: HTML, XML (xml)
Grazie a questo potremo quindi creare template con condizioni molto complesse producendo però codice molto pulito e facilmente leggibile.
Semplice da leggere, semplice da manutenete.
Passiamo ora alla condizione @switch.
@switch condition
Fino ad oggi abbiamo utilizzato la direttiva ngSwitch per mostrare un blocco di template diverso in base ad una determinata condizione:
<div [ngSwitch]="condition">
<div *ngSwitchCase="value1">* content block value1 *</div>
<div *ngSwitchCase="value2">* content block value2 *</div>
<div *ngSwitchDefault>* default content *</div>
</div>
Code language: HTML, XML (xml)
In modo analogo a @if abbiamo ora a disposizione una nuova condizione @switch:
@switch(condition) {
@case ('value1') {
* content block value1 *
} @case ('value2') {
* content block value2 *
} @default {
* default content *
}
}
Code language: HTML, XML (xml)
Anche in questo caso otteniamo una sintassi pulita e niente imports.
Siamo giunti ora all’ultimo argomento dell’articolo: il ciclo @for.
@for loop
Mostrare una lista di blocchi nel template per rappresentare una lista di elementi è uno dei concetti chiave di molti framework e in Angular questo è sempre stato possibile grazie alla direttiva ngFor:
<ul>
<li *ngFor="let item of itemList">
{{ item.name }}
</li>
</ul>
Code language: HTML, XML (xml)
Questa direttiva ci permette di creare un blocco di template per ogni singolo elemento di una lista, grazie alla sua capacità di fornirci le informazioni del singolo elemento e alcune variabili locali:
index: number
: index dell’elemento nella lista;count: number
: il valore della length della lista;first: boolean
: indica se l’elemento è il primo della lista;last: boolean
: indica se l’elemento è l’ultimo della lista;even: boolean
: indica se l’elemento ha index di valore pari nella lista;odd: boolean
: indica se l’elemento ha index di valore dispari nella lista.
Non solo, la direttiva ngFor accetta inoltre come parametro opzionale una funzione, chiamata trackBy, utilizzata per fornire ad Angular un identificativo unico per ogni elemento della lista (come un id):
<ul>
<li *ngFor="let item of itemList; trackBy: itemById">
{{ item.name }}
</li>
</ul>
Code language: HTML, XML (xml)
itemById(index, item) {
return item.id;
}
Code language: TypeScript (typescript)
Grazie a questa funzione Angular è in grado di ottimizzare le performance in caso di operazioni di aggiornamento o modifica liste.
Dunque per sostituire ngFor il nuovo control flow ci fornisce uno strumento apposito @for:
<ul>
@for (item of itemList; track item.id; let idx = $index, e = $even) {
<li>{{ item.name }}</li>
}
</ul>
Code language: HTML, XML (xml)
Questa nuova sintassi porta con sé alcuni grandi cambiamenti:
- Perfomance migliori fino al 90% rispetto a ngFor, grazie al nuovo algoritmo utilizzato per implementare @for;
- La funzione trackBy viene sostituita dalla proprietà track che richiede un espressione, molto più semplice da scrivere/leggere di una funzione;
- La proprietà track è obbligatoria cosi da spingerci ad usarla, dato che la funzione trackBy viene spesso dimenticata;
- Le variabili locali ora hanno come prefisso la lettera $ (es: $index).
Inoltre anche in questo caso otteniamo una sintassi pulita e niente imports.
@empty block
Infine la nuova sintassi @for porta con se la comodissima condizione @placeholder che ci permetterà di fornire un blocco di template nel caso in cui la lista risulti vuota:
@for (item of itemList; track item.id) {
<div>{{ item.name }}</div>
} @placeholder {
<div>No items available</div>
}
Code language: HTML, XML (xml)
Fantastico 🤩
Conclusioni:
Come abbiamo visto il nuovo Angular control flow ci offre un modo semplice e immediato per scrivere condizioni complesse nei nostri template, con l’aggiunta di performance migliori e maggior leggibilità dei nostri template.
⚠️ ATTENZIONE: il nuovo control flow è ancora in developer preview ⚠️
Se vuoi iniziare a provarlo ti consiglio di farlo su un applicativo già esistente.
Non dovrai far altro che lanciare un ng update e usare il comando di migrazione fornito dalla Angular CLI:
ng generate @angular/core:control-flow
Code language: Bash (bash)
Grazie per essere arrivato fin qui, mi farebbe molto piacere ricevere qualche feedback. 👋