In questa lezione continuiamo ad illustrare altri strumenti che Angular fornisce per lavorare con i template e in particolare ci soffermiamo ad analizzare alcune delle direttive predefinite più utili.
Tipi di direttive
In Angular esistono tre tipi di direttive:
- i Componenti rappresentano una particolare direttiva caratterizzata dalla presenza di un template;
- le direttive dette Attribute directives, il cui nome deriva dal fatto che vengono applicate a un elemento come se fossero degli attributi, permettono di modificare l’aspetto o il comportamento di un elemento o del componente ad esso associato;
- le direttive strutturali consentono di mutare la struttura di un’applicazione andando a modificare direttamente il DOM e permettendo di aggiungere o rimuovere degli elementi.
Attribute directives
Le Attribute directives vengono applicate ad un qualsiasi elemento come un normale attributo HTML, ma il valore ad esse assegnato può essere il risultato di un’espressione valutata da Angular.
La direttiva NgClass
La prima direttiva di cui parliamo è NgClass che consente di aggiungere o rimuovere delle classi CSS da un elemento in maniera dinamica.
Abbiamo già visto come usare la tecnica del binding di una classe per aggiungere o rimuovere una singola classe CSS. Quando abbiamo necessità di lavorare con più classi CSS contemporaneamente, la direttiva NgClass è tuttavia il metodo da preferire. In questi casi potremo utilizzare un oggetto le cui chiavi sono i nomi delle classi CSS e i rispettivi valori sono pari a true se la classe deve essere aggiunta o a false se deve essere rimossa.
Vediamo subito un esempio in cui il componente AppComponent presenta un oggetto componentClasses avente due proprietà show e warning i cui valori sono legati a quelli di altre proprietà del componente stesso.
import { Component } from '@angular/core';
@Component({
selector: 'simple-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
loaded = true;
important = true;
dangerous = false;
componentClasses = {
show: this.loaded,
warning: this.important && this.dangerous
};
}
All’interno del template del componente, associamo a ngClass l’oggetto componentClasses in cui solo la proprietà componentClasses.show ha un valore pari a true.
<div [ngClass]="componentClasses" >
Messaggio importante
</div>
All’elemento <div> sarà quindi applicata la sola classe CSS show.
In alternativa possiamo sempre associare alla proprietà ngClass un array i cui elementi sono i nomi delle classi CSS da aggiungere o semplicemente una stringa contenente, anche in questo caso, la lista dei nomi delle classi CSS, ma separati da uno spazio.
<!-- Nota: la stringa passata alla proprietà ngClass -->
<!-- è racchiusa fra singoli apici (') -->
<div [ngClass]="'warning show'" >
Messaggio importante
</div>
<div [ngClass]="['warning', 'show']" >
Messaggio importante
</div>
In entrambi i casi otteniamo un risultato simile a quello mostrato nell’immagine sottostante.
La direttiva NgStyle
La direttiva NgStyle permette di settare lo stile di un certo elemento in base al valore ottenuto dalla valutazione di una certa espressione come mostrato nel frammento di codice sottostante.
componentStyle = {
'width': '200px',
'height.px': 100,
'background-color': this.dangerous ? 'firebrick' : 'forestgreen'
};
Supponiamo che il nostro componente ha una proprietà componentStyle come l’oggetto riportato sopra. Notiamo che le chiavi dell’oggetto sono i nomi di proprietà CSS a cui appendiamo opzionalmente .<unità>. A ciascuna di queste associamo dei valori che possono anche essere il risultato di un’espressione valutata da Angular.
<div [ngStyle]="componentStyle" >
Messaggio importante
</div>
Supponendo che la proprietà dangerous, usata nel frammento di codice visto in precedenza, abbia un valore pari a true, l’elemento <div> risultante sarà quello riportato sotto.
Direttive strutturali
Vediamo ora tre delle direttive strutturali più utili che sono messe a disposizione da Angular.
La direttiva NgIf
La direttiva NgIf aggiunge o rimuove un elemento e i suoi discendenti dal DOM in base al valore dell’espressione assegnata. Se quest’ultimo è equivalente al valore booleano true, l’elemento viene aggiunto, in caso contrario viene rimosso.
<div>
<div *ngIf="loaded">
<h1>AppComponent</h1>
<p>
AppComponent.loaded === true
</p>
</div>
<p>Paragrafo sempre inserito nel DOM</p>
</div>
Quando la proprietà loaded è pari a false, il <div>
a cui è applicata NgIf e i suoi discendenti non sono inseriti nel DOM oppure, se erano precedentemente presenti, vengono rimossi. È bene evidenziare che tali elementi non vengono nascosti, ma vengono effettivamente eliminati.
È inoltre importante ricordare di precedere sempre *ngIf dal carattere asterisco senza utilizzare le parentesi quadre. In questo modo stiamo infatti usando una sintassi semplificata messa a disposizione da Angular per agevolare la scrittura dei template. Internamente Angular trasforma il codice da noi scritto racchiudendo l’elemento con *ngIf in un elemento <ng-template> a cui viene applicata una proprietà [ngIf] associata tramite binding delle proprietà all’espressione booleana da noi specificata.
<ng-template [ngIf]="loaded">
<p>AppComponent.loaded === true</p>
</ng-template>
Il frammento di codice riportato sopra è quindi equivalente a quello sottostante.
<p *ngIf="loaded">
AppComponent.loaded === true
</p>
La direttiva NgForOf
La direttiva NgForOf permette di inserire nel DOM una serie di elementi simili a partire da una lista di valori. Attraverso un blocco di codice HTML si definisce come deve essere visualizzato un singolo elemento della lista. Angular userà quel blocco come modello e inserirà gli elementi nel DOM utilizzando per ciascuno le informazioni passate a NgForOf.
Supponiamo per esempio che il nostro componente ha una proprietà ingredients come la seguente.
ingredients = [
'avocado',
'pomodoro',
'lattuga',
'maionese',
'pancetta',
'pane'
];
Possiamo visualizzare una semplice lista degli ingredienti inserendo nel template del componente il seguente frammento di codice.
<ul>
<li *ngFor="let ingredient of ingredients">{{ ingredient }}</li>
</ul>
Ma possiamo estendere il nostro esempio accedendo ad altre informazioni messe a disposizione dal l’uso della direttiva NgFor. Per esempio possiamo ottenere l’indice di ciascun elemento o accedere a due particolari valori even e odd che permettono di distinguere gli elementi di posto pari da quelli di posto dispari.
Modifichiamo quindi il nostro esempio come riportato sotto.
import { Component } from '@angular/core';
@Component({
selector: 'simple-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
ingredients = [
'avocado',
'pomodoro',
'lattuga',
'maionese',
'pancetta',
'pane'
];
delete(item: string) {
this.ingredients = this.ingredients
.filter((ingredient) => ingredient !== item);
}
}
A ciascun elemento della lista è applicata la classe even o odd a seconda che sia un elemento di posto pari o dispari. Inoltre è presente un pulsante che, se cliccato, invoca il metodo delete(item) a cui viene passato il nome dell’elemento da rimuovere. Angular procederà ad aggiornare l’applicazione con le nuove informazioni aggiornate. (Abbiamo fatto anche uso della pipe predefinita titlecase che fa in modo che la prima lettera di una stringa sia maiuscola)
<ul>
<li
*ngFor="let ingredient of ingredients;
let i = index;
let even = even;
let odd = odd"
[ngClass]="{ even: even, odd: odd }">
{{ i }}. {{ ingredient | titlecase }}
<button class="delete" (click)="delete(ingredient)">Rimuovi</button>
</li>
</ul>
La direttiva NgSwitch
Vediamo infine la direttiva NgSwitch che, come è possibile intuire dal nome, funziona in maniera simile all’istruzione switch disponibile in molti linguaggi di programmazione. La direttiva NgSwitch, che in realtà comprende tre diverse direttive che cooperano fra loro (ngSwitch, ngSwitchCase e ngSwitchDefault), permette di selezionare un solo elemento, fra un gruppo di diverse possibili scelte, da inserire nel DOM in base a una certa condizione.
<div [ngSwitch]="newVehicle.type">
<simple-car *ngSwitchCase="'car'" [vehicle]="newVehicle"></simple-car>
<simple-bus *ngSwitchCase="'bus'" [vehicle]="newVehicle"></simple-bus>
<simple-unknown-vehicle *ngSwitchDefault></simple-unknown-vehicle>
</div>
A ngSwitch assegniamo un’espressione che viene valutata e il suo valore viene confrontato con quello di ciascuna direttiva ngSwitchCase presente su diversi elementi. Quindi ciascun ngSwitchCase aggiunge l’elemento, per cui è responsabile, al DOM solo se il suo valore coincide con quello di ngSwitch. Se nessuno degli elementi a cui è applicata ngSwitchCase viene selezionato, viene aggiunto al DOM l’elemento associato a ngSwitchDefault.
Facendo riferimento all’esempio, se newVehicle.type è pari a ‘car’, viene aggiunto al DOM il primo elemento, se è uguale a ‘bus’ sarà inserito nel DOM il secondo. In tutti gli altri casi verrà aggiunto al DOM l’elemento <simple-unknown-vehicle>
Conclusioni
In questa lezione abbiamo illustrato alcune direttive predefinite che Angular mette a disposizione per realizzare delle applicazioni più complesse. Abbiamo visto che esistono tre tipi di direttive:
- i componenti, particolari direttive con template
- le direttive strutturali, che modificano il DOM, il cui nome è preceduto da un asterisco in modo tale da permettere ad Angular di racchiudere l’elemento a cui vengono applicate fra i tag <ng-template>;
- le direttive definite come attribute directives che permettono di cambiare l’aspetto e il comportamento degli elementi HTML standard o associati a un componente.
Nella prossima lezione, continueremo il nostro viaggio di esplorazione dei diversi costrutti sintattici disponibili per lavorare con i template in Angular e parleremo delle Pipe.