
GUIDE PER ASPIRANTI PROGRAMMATORI
Le direttive in Angular
Ad un primo impatto, data la natura component based della maggior parte dei framework frontend moderni, potrebbe risultare strana la scelta di affrontare le direttive (e non i componenti) prima di ogni altra cosa. La ragione di questa scelta è che, come abbiamo detto confrontando Angular con altri framework a componenti come React e…


Vuoi avviare una nuova carriera o fare un upgrade?
Trova il corso Digital & Tech piĂą adatto a te nel nostro catalogo!
- Le direttive in Angular
- I componenti in Angular
- Il template in Angular
- Le direttive strutturali in Angular
- La content projection in Angular
- I servizi in Angular
- Le Pipes in Angular
- Routing in Angular
- Invio di form in Angular
- Built-in control flow in Angular
- Deferrable views in Angular
- Image optimization in Angular
Ad un primo impatto, data la natura component based della maggior parte dei framework frontend moderni, potrebbe risultare strana la scelta di affrontare le direttive (e non i componenti) prima di ogni altra cosa.Â
La ragione di questa scelta è che, come abbiamo detto confrontando Angular con altri framework a componenti come React e Vue, il principio di funzionamento di Angular non consiste nel creare un’alberatura virtuale di componenti (virtual DOM), bensì nell’agganciare funzionalità ai nodi del DOM nativo.
Vediamo, dunque, che cosa sono le direttive nel dettaglio, come funzionano e perchĂ© sono così importanti.Â
Che cos’è un direttiva in Angular?
Iniziamo dando una definizione formale di direttiva: una direttiva (in inglese: directive) Angular è una classe TypeScript che implementa un comportamento su un elemento del DOM. In pratica, le direttive sono il ponte tra gli elementi del DOM e il codice della nostra web app.
Esistono tre tipi di direttive in Angular:Â
- Componenti: il tipo di direttiva più ovvio; in Angular, il componente non è altro che un particolare tipo di direttiva dotata di un template, cioè di una porzione di codice HTML da proiettare nel DOM.
- Attributi: si tratta di direttive che vengono applicate attraverso degli attributi sui tag HTML; in genere, quando si parla semplicemente di direttiva, si fa riferimento a questo tipo. Come vedremo a breve, il bello di questo tipo di direttiva è il fatto di essere componibile, semplicemente applicando più attributi a uno stesso tag.
- Direttive strutturali: si tratta di direttive che modificano il DOM aggiungendo o rimuovendo porzioni di template; sono usate quasi esclusivamente per implementare sintassi di controllo di flusso (if/else, switch, for) nel template dei componenti, e con le ultime versioni di Angular stanno venendo soppiantate dalle sintassi built-in control flow (controllo di flusso integrato), che vedremo approfonditamente piĂą avanti.
A questo punto, non ci resta che implementare un paio di esempi di direttiva attributo, dato che approfondiremo gli altri due tipi piĂą avanti.
Type numeric: una direttiva attributo personalizzata.
Immaginiamo di avere nel nostro template app.component.html un form con dei campi di input che, pur essendo di tipo text, devono accettare solo valori numerici. Ci sono molti esempi di questa casistica: CAP, codici bancari, qualunque tipo di campo stringa che però non debba accettare altro che numeri.
Se volessimo ragionare in un’ottica di validazione di un form in HTML, useremmo l’attributo nativo pattern=”[0-9]”; ma noi vogliamo fare di più: vogliamo inibire l’inserimento di qualunque lettera o simbolo al momento in cui viene premuto il rispettivo tasto.
Se volessimo implementare questo comportamento in linguaggio HTML puro, finiremmo a scrivere una cosa di questo tipo:
<input type="text" onkeypress="return event.key.match('[0-9]') !== null">
L’attributo pattern e il metodo match accettano come argomento una regular expression (espressione regolare). Con questo speciale tipo di dato si possono fare operazioni piuttosto sofisticate sulle stringhe; per lo scopo di questa guida, possiamo limitarci a osservare che [0-9] significa semplicemente “un carattere compreso tra 0 e 9”.
Vediamo di capire bene che cosa succede qui: abbiamo un normalissimo input di tipo text con un evento onkeypress in cui ritorniamo il risultato di un’espressione che ci dice se il tasto premuto corrisponde o meno ad una cifra numerica.
Nel caso in cui non ci sia corrispondenza l’espressione risolve a false e in questo modo l’evento viene rigettato, e dunque il carattere digitato a tastiera non viene inserito nel nostro input.
Navighiamo, dunque, nella cartella del nostro progetto Angular appena creato. Nella root abbiamo i file di configurazione del nostro progetto e una cartella src che contiene il codice vero e proprio della nostra app.
Ora creiamo una direttiva con questo comando:
ng generate directive forms/type-numeric
La CLI creerĂ per noi una classe TypeNumericDirective opportunamente decorata dal decoratore Directive con un selettore appTypeNumeric che potremo andare a customizzare.
Abbiamo anche sfruttato la CLI per collocare direttamente la direttiva nella cartella src/forms, che è stata creata automaticamente.
A questo punto andiamo a implementare il comportamento desiderato:
import { Directive, ElementRef, HostListener } from '@angular/core'; @Directive({ selector: 'input[type="text"][appTypeNumeric]', standalone: true }) export class TypeNumericDirective { @HostListener('keypress', ['$event']) rejectNonNumeric(event: KeyboardEvent): void { if (event.key.match('[0-9]') === null) event.preventDefault(); }; }
Vediamo di capire bene che cosa sta succedendo qui:
- Abbiamo modificato il selettore della direttiva in modo che questa si applichi solo agli input con type=”text”; in questo modo non dovremo preoccuparci di gestire il caso in cui a qualcuno (ad esempio ad un altro developer, o più probabilmente a noi, qualche tempo dopo) venga in mente di applicare l’attributo appTypeNumeric sull’elemento sbagliato.
- Abbiamo scritto un metodo rejectNonNumeric che blocca l’effetto dell’evento keypress secondo lo stesso criterio di prima; in questo caso, il blocco non avviene restituendo false in caso di mismatch, ma invece bloccando proceduralmente gli effetti dell’evento. Il motivo per questa diversa implementazione è che, per gli eventi registrati tramite addEventListener, restituire false non ha alcun effetto.
- Per agganciare il metodo rejectNonNumeric, lo abbiamo decorato con HostListener, che si occupa appunto di chiamare addEventListener e removeEventListener per noi automaticamente.
A questo punto non ci resta che applicare la nostra direttiva; in app.component.ts scriviamo:
import { Component } from '@angular/core'; import { RouterLink, RouterOutlet } from '@angular/router'; import { TypeNumericDirective } from './forms/type-numeric.directive'; @Component({ selector: 'app-root', standalone: true, imports: [RouterOutlet, RouterLink, TypeNumericDirective], // <- import new directive templateUrl: './app.component.html', styleUrl: './app.component.css' }) export class AppComponent { title = 'examples'; }
Ora, in app.component.html scriviamo:
<input type="text" onkeypress="return event.key.match('[0-9]') !== null"> <input type="text" appTypeNumeric>
Ora possiamo confrontare il funzionamento dei due input e verificare che è lo stesso, come ci aspettavamo.
Prima di procedere con un altro esempio, una nota sui decoratori: i decoratori sono speciali funzioni che possono essere applicate (usando il prefisso @) su classi, proprietĂ o metodi per annotare dei metadati oppure per associarli a specifiche logiche di comportamento gestite dal framework.
Questo è generalmente il modo in cui Angular mette in collegamento la sua gestione delle API native di JavaScript (come appunto gli event handlers) e la sua architettura orientata agli oggetti.
Nel corso di questa guida vedremo diversi decoratori messi a disposizione dal framework, e il loro funzionamento.
Highlight error: integriamo un altro esempio di direttiva attributo.
Abbiamo detto che le direttive sono componibili; perché, allora, non creare una seconda direttiva che “collabori” con la nostra TypeNumericDirective per migliorarne l’esperienza?
Andiamo a implementare una nuova direttiva che alla pressione di un carattere non numerico modifichi lo stile dell’elemento per segnalare l’errore. Per fare questo useremo di nuovo il decoratore HostListener, e gli affiancheremo un altro decoratore molto utilizzato nelle direttive: HostBinding.
Creiamo una nuova direttiva con la CLI:
ng generate directive forms/highlight-invalid
Abbiamo una nuova HighlightInvalidDirective!
Andiamo a implementarla:
import { Directive, HostBinding, HostListener } from '@angular/core'; @Directive({ selector: 'input[type="text"][appTypeNumeric][appHighlightInvalid]', standalone: true }) export class HighlightInvalidDirective { @HostBinding('style.color') color: 'red' | 'unset' = 'unset'; @HostListener('keypress', ['$event']) onKeyPress(event: KeyboardEvent) { this.color = event.key.match('[0-9]') === null ? 'red' : 'unset'; } }
Proviamo ad analizzare questo codice:
- la nuova direttiva è applicabile solo a elementi che già applicano la direttiva TypeNumeric;
- abbiamo un listener sull’evento keypress che assegna una property color che può assumere due valori: red o unset;
- ogni volta che viene premuto un carattere non numerico, il colore viene cambiato a rosso se il carattere premuto non è valito, altrimenti viene semplicemente disimpostato;
- il nostro color è decorato con HostBinding, che accetta un parametro stringa dove possiamo indicare a quale attributo del nostro elemento vogliamo collegare il valore della nostra property; in questo caso, quando color è red, il nostro input avrà l’attributo style impostato come color: red.
A questo punto non ci resta che includere la nostra nuova direttiva nel nostro template:
<input type="text" appTypeNumeric appHighlightInvalid>
Per farla funzionare, dobbiamo ricordarci di importarla in app.component.ts:
import { Component } from '@angular/core'; import { RouterLink, RouterOutlet } from '@angular/router'; import { TypeNumericDirective } from './forms/type-numeric.directive'; import { HighlightInvalidDirective } from './forms/highlight-invalid.directive'; @Component({ selector: 'app-root', standalone: true, imports: [ RouterOutlet, RouterLink, TypeNumericDirective, HighlightInvalidDirective // <- new import ], templateUrl: './app.component.html', styleUrl: './app.component.css' }) export class AppComponent { title = 'examples'; }
Ora che ci siamo fatti un’idea di che cos’è e come si implementa una direttiva, andiamo finalmente a vedere il tipo di direttiva con cui avremo più spesso a che fare: i componenti.
CONTENUTI GRATUITI IN EVIDENZA
Guide per aspiranti programmatori 👨🏻‍🚀
Vuoi muovere i primi passi nel Digital e Tech? Abbiamo preparato alcune guide per aiutarti a orientarti negli ambiti piĂą richiesti oggi.