Chi ha utilizzato Angular 1.x conosce le direttive come dei marcatori che estendono l'HTML o modificano il comportamento di un elemento HTML standard. Da quanto abbiamo descritto finora in questa guida, il ruolo delle direttive sembra essere stato preso, almeno in parte, dai componenti.
In realtà, Angular 2 ha riorganizzato il concetto di direttiva prevedendone tre categorie:
- componenti
- direttive di attributo
- direttive strutturali
Componenti
I componenti non sono altro che direttive con un template, cioè elementi che hanno generalmente un ruolo primario nella costruzione dell'interfaccia utente. Abbiamo già visto come creare componenti utilizzabili nella costruzione di un'applicazione.
Direttive di attributo
Le direttive di attributo hanno il compito di modificare l'aspetto o il comportamento di un elemento. Esse si presentano come un normale attributo HTML a cui possono essere assegnate espressioni che rappresentano il comportamento desiderato.
Esempi di direttive di attributo sono ngStyle e ngClass. La prima direttiva, ngStyle
, modifica lo stile di un elemento in base al risultato della valutazione dell'espressione assegnata, come mostrato dal seguente esempio:
<p [ngStyle]="{'font-style': myStyle, 'font-size': mySize}">
Questo è il mio paragrafo!
</p>
La direttiva ngStyle applica al paragrafo corrente le impostazioni derivanti dalla valutazione dell'oggetto assegnato, in particolare dalla valutazione delle variabili myStyle
e mySize
.
Analogamente possiamo cambiare la classe CSS assegnata ad un elemento tramite la direttiva ngClass, come possiamo vedere dal seguente esempio:
<a class="button"
[ngClass]="{ active: datiModificati && !solaLettura,
disabled: !datiModificati || solaLettura }">
Salva
</a>
In questo caso la direttiva applica le classi corrispondenti alle proprietà dell'oggetto (active
e disabled
) le cui espressioni booleane associate sono vere.
Direttive strutturali
Le direttive strutturali modificano la struttura del DOM aggiungendo o rimuovendo elementi. Esempi di questa categoria di direttive sono ngIf, ngSwitch e ngFor.
La direttiva ngIf aggiunge un blocco di elementi in base alla valutazione di una espressione booleana:
<div *ngIf="!solaLettura">
<button>Salva</button>
</div>
In questo caso, il <div>
con il pulsante verrà inserito nel DOM soltanto se il valore della variabile solaLettura
è false
. È opportuno sottolineare che le direttive strutturali stabiliscono se un blocco di DOM deve essere generato o meno, non si limitano a nasconderlo. La differenza è sostanziale, in quanto un elemento nascosto richiede in ogni caso risorse di sistema.
La direttiva ngSwitch consente di generare un blocco di elementi da un insieme di possibili blocchi in base al valore di un'espressione. Ad esempio, il seguente markup mostra una serie di pulsanti in base al ruolo dell'utente:
<div [ngSwitch]="ruolo">
<div *ngSwitchCase="'admin'">
<button>Gestione utenti</button>
<button>Gestione documenti</button>
</div>
<div *ngSwitchCase="'manager'">
<button>Gestione documenti</button>
<button>Visualizza pratiche</button>
</div>
<div *ngSwitchCase="'utente'">
<button>Visualizza pratiche</button>
</div>
<div *ngSwitchDefault>
<button>Contatta l'assistenza</button>
</div>
</div>
Come possiamo vedere, la direttiva ngSwitch si avvale della collaborazione di altre due direttive: ngSwitchCase e ngSwitchDefault.
Direttiva | Descrizione |
---|---|
ngSwitch | Cattura un'espressione il cui valore rappresenta la discriminante per individuare il blocco di elementi da inserire nel DOM |
ngSwitchCase | Serve per individuare il blocco da generare |
ngSwitchDefault | Individua il blocco da generare nel caso in cui il valore dell'espressione catturata da ngSwitch non corrisponda a nessuno dei valori previsti |
La direttiva ngFor consente di generare un elenco di blocchi di elementi basati sul contenuto di una lista. Supponiamo, ad esempio, di voler mostrare l'elenco degli articoli contenuti in un array elencoArticoli
. Possiamo strutturare il nostro markup nel seguente modo:
<ul>
<li *ngFor="let articolo of elencoArticoli">
<b>{{articolo.titolo}}</b> di {{articolo.autore}}
</li>
</ul>
Il risultato di questo markup sarà la visualizzazione dell'elenco dei titoli degli articoli con i rispettivi autori.
Come abbiamo potuto notare, le direttive strutturali utilizzano il carattere asterisco (*) come prefisso nel loro nome. Questo carattere rappresenta un'abbreviazione sintattica per indicare che il markup sotto il loro controllo è un template da utilizzare per la generazione degli elementi del DOM. Per questo motivo la direttiva ngSwitch
non presenta il carattere *
, dal momento che il suo ruolo è quello di catturare il valore discriminante, non la generazione vera e propria, che è invece compito delle direttive ngSwitchCase
e ngSwitchDefault
.
Potremmo fare a meno del carattere *
specificando esplicitamente il tag <template>, come mostrato dal seguente codice:
<template [ngIf]="!solaLettura"
<div >
<button>Salva</button>
</div>
</template>
Infatti, questo markup è del tutto equivalente a quello visto prima:
<div *ngIf="!solaLettura">
<button>Salva</button>
</div>
Come abbiamo potuto vedere, quindi, Angular 2 mantiene in linea di massima le medesime funzionalità delle direttive predefinite di Angular 1.x anche se con una sintassi un po' diversa e con un impianto comune che può essere ricondotto ai due meccanismi di base: binding delle proprietà e gestione degli eventi.