Nel corso di questa guida abbiamo avuto modo di utilizzare diverse direttive di Angular che ci hanno consentito di semplificare notevolmente il codice JavaScript che avremmo dovuto scrivere, e soprattutto ci hanno consentito di iniettare della logica nell'HTML con un approccio prettamente dichiarativo.
Il ruolo delle direttive è centrale nella visione di Angular: esse rappresentano un modo di estendere il linguaggio HTML con elementi e attributi personalizzati. Questo si riallaccia, inoltre, all'ottica per cui ogni componente Angular ha un suo ruolo specifico: in particolare, il ruolo delle direttive consiste nell'estendere le potenzialità dell'approccio dichiarativo dell'HTML nella costruzione di interfacce grafiche.
Ma AngularJS non si limita a fornire un insieme di direttive predefinite. Il framework fornisce anche il supporto per la creazione di direttive personalizzate.
Una semplice direttiva
Iniziamo a prendere confidenza con la creazione di direttive personalizzate analizzando il seguente codice:
angular.module("myApp", [])
.directive("titolo", function() {
return {
template: "<h1>Questo è un titolo</h1>"
};
});
Nell'esempio utilizziamo il metodo directive() per creare una direttiva all'interno della nostra applicazione. Come si vede questo metodo prende come argomenti:
- una stringa che rappresenta il nome della direttiva;
- una funzione che definisce la direttiva e che restituisce un oggetto che contiene le impostazioni della direttiva.
Nel nostro caso si tratta di una direttiva semplicissima che si limita a definire un template
, cioè un blocco di codice HTML che sarà iniettato nel punto in cui sarà utilizzata.
Infatti, se nella nostra pagina HTML inseriamo il seguente tag:
<titolo></titolo>
vedremo apparire al suo posto la scritta:
Restrict, usare le direttive come tag, attributi o altro
È importante notare che otteniamo lo stesso effetto sia che utilizziamo il nome della direttiva come attributo sia che lo utilizzaimo come tag, cioè nel seguente modo:
<div titolo></div>
Angular prevede infatti di definire direttive utilizzabili come nomi di elementi HTML, come attributi, come classi e addirittura come commenti.
Quando creiamo una direttiva come nell'esempio appena visto, Angular consente automaticamente di utilizzare il suo nome sia come elemento che come attributo, ma non come classe e commento. Abbiamo tuttavia la possibilità di specificare come vogliamo che venga utilizzata la nostra direttiva tramite la proprietà restrict.
Ad esempio, il seguente codice limita l'utilizzo della nostra direttiva consentendo solo il suo uso come nome di elemento HTML:
angular.module("myApp", [])
.directive("titolo", function() {
return {
restrict: "E",
template: "<h1>Questo è un titolo</h1>"
};
});
Assegnando il valore E
alla proprietà restrict non consentiamo il suo utilizzo in maniera diversa dal nome di un elemento HTML. La seguente tabella riassume i possibili valori che possiamo assegnare alla proprietà restrict:
Valore | Significato | Esempio d'uso |
---|---|---|
E | Elemento | <titolo></titolo> |
A | Attributo | <div titolo></div> |
C | Classe | <div class="titolo"></div> |
M | Commento | <!-- directive: titolo --> |
È possibile consentire l'utilizzo di una direttiva secondo più modalità specificando una stringa composta dai possibili valori della proprietà restrict. Ad esempio, il seguente codice prevede che la nostra direttiva possa essere utilizzata come elemento, attributo e classe ma non come commento:
angular.module("myApp", [])
.directive("titolo", function() {
return {
restrict: "EAC",
template: "<h1>Questo è un titolo</h1>"
};
});
La scelta dalla modalità di utilizzo di una direttiva è a carico dello sviluppatore. In generale, la scelta di consentire l'utilizzo di una direttiva come commento è molto rara; anche l'uso della classe non è molto comune, ed ha senso quando, oltre ad una funzionalità, si vuole associare alla direttiva un rendering grafico personalizzabile. L'utilizzo più comune ricade prevalentemente tra elemento e attributo.
Ma quando conviene scegliere di definire una direttiva come elemento e quando come attributo? In linea di massima possiamo dire che è opportuno definire una direttiva come elemento quando stiamo costruendo un componente autonomo, come ad esempio un datepicker, una griglia, un menu, etc. Definiremo una direttiva come attributo quando intendiamo arricchire un elemento esistente di una nuova funzionalità.
Nomi delle direttive
Prima di chiudere questa prima introduzione dobbiamo fare qualche osservazione sui nomi da assegnare alle direttive. In linea di massima possiamo utilizzare i nomi che preferiamo, anche addirittura ridefinire elementi o attributi HTML esistenti, come ad esempio fa Angular con l'elemento input o l'attributo required. Tuttavia, se non abbiamo intenzione di ridefinire esplicitamente il comportamento di elementi e attributi HTML, è opportuno fare attenzione ai nomi che assegniamo alle nostre direttive.
In ogni caso, bisogna considerare il fatto che il nome che diamo alla nostra direttiva potrebbe collidere con il nome di direttive di Angular o con direttive create da altri sviluppatori. Per questo motivo è opportuno premettere un prefisso al nome delle nostre direttive, come una sorta di namespace che consenta di distinguere eventuali direttive che hanno accidentalmente lo stesso nome.
È questo il motivo per cui le direttive predefinite di Angular presentano il prefisso ng-
, proprio per distinguerle da quelle che ciascun sviluppatore può creare da sè.
Il seguente codice definisce la nostra direttiva d'esempio facendo precedere il nome dal prefisso my-
:
angular.module("myApp", [])
.directive("myTitolo", function() {
return {
restrict: "EAC",
template: "<h1>Questo è un titolo</h1>"
};
});
Come possiamo vedere, in realtà non abbiamo inserito nel nome della direttiva proprio il prefisso my-
, ma abbiamo riscritto il suo nome come la composizione in camel case di my
e titolo
. L'uso del camel case nei nomi delle direttive si traduce nell'HTML in nomi separati da trattini:
<my-title></my-title>
Occorre tenere presente questa convenzione perché viene mantenuta anche per altri elementi della definizione di una direttiva.
Template e templateUrl
Nell'esempio di definizione della direttiva my-titolo
, abbiamo utilizzato la proprietà template che consente di definire il markup da iniettare nel punto in cui sarà piazzata la direttiva. Se il nostro markup è complesso, può essere scomodo definirlo in una stringa.
Consideriamo ad esempio il caso in cui vogliamo definire una direttiva che visualizza una select
con un elenco di città. Il suo markup potrebbe essere analogo al seguente:
<select ng-model="selectedItem">
<option ng-repeat="citta in elencoCitta" value="citta.codice">{{citta.nome}}</option>
</select>
Risulta scomodo inserire questo testo come semplice stringa nel codice della direttiva. Possiamo allora salvare il markup del template in un file ricorrere alla proprietà templareUrl per indicare ad Angular da dove recuperarlo:
angular.module("myApp", [])
.directive("mySelectCity", function() {
return {
restrict: "E",
templateUrl: "/mySelectCityTemplate.html"
};
});
In alternativa al salvataggio del template in un file separato, possiamo includere il markup della direttiva in un inline template:
<script type="text/ng-template" id="/mySelectCityTemplate.html">
<select ng-model="selectedItem">
<option ng-repeat="citta in elencoCitta"
value="citta.codice">{{citta.nome}}</option>
</select>
</script>
Dal punto di vista della definizione della direttiva non cambia nulla dal momento che, come sappiamo, i template esterni e i template inline vengono trattati in maniera trasparente da Angular.