Padroneggiare le direttive AngularJS

Le direttive sono uno dei componenti più potenti di AngularJS, che consente di estendere elementi / attributi HTML di base e di creare riutilizzabile e testabile codice. In questo tutorial, ti mostrerò come utilizzare le direttive AngularJS con le best practice della vita reale. 

Cosa intendo qui direttiveè per lo più direttive personalizzate durante il tutorial. Non cercherò di insegnarti come usare le direttive built-in come ng-repeat, ng-spettacolo, ecc. Ti mostrerò come utilizzare le direttive personalizzate per creare i tuoi componenti. 

Schema

  1. Direttive semplici
  2. Limitazioni della direttiva
  3. Ambito isolato
  4. Ambiti della direttiva
  5. Eredità della direttiva
  6. Debug della direttiva
  7. Test unitario della direttiva
  8. Test di portata della direttiva
  9. Conclusione

1. Direttive semplici

Supponiamo che tu abbia un'applicazione di eCommerce sui libri e che tu stia visualizzando dettagli di libri specifici in diverse aree, come i commenti, le pagine del profilo utente, gli articoli, ecc. Il tuo widget dei dettagli del libro potrebbe essere il seguente:

In questo widget sono presenti l'immagine di un libro, il titolo, la descrizione, i commenti e la valutazione. Raccogliere tali informazioni e inserire un elemento dom specifico può essere difficile da fare in ogni luogo in cui si desidera utilizzarlo. Vediamo di widgetizzare questa vista usando una direttiva AngularJS. 

angular.module ('masteringAngularJsDirectives', []) .directive ('book', function () return limitt: 'E', scope: data: '=', templateUrl: 'templates / book-widget.html ')

Nell'esempio precedente è stata utilizzata una funzione direttiva per creare prima una direttiva. Il nome della direttiva è libroQuesta direttiva restituisce un oggetto e parliamo un po 'di questo oggetto. limitareè per definire il tipo di direttiva, e può essere UN(UNttribute),C(Class), E (Element), eM(coMment). Puoi vedere l'utilizzo di ciascuno rispettivamente sotto.

genere uso
UN
libro>
C
E <libro data = "book_data">libro>
M

scopoè per la gestione dell'ambito della direttiva. Nel caso precedente, i dati del libro vengono trasferiti al modello direttivo usando il "="tipo di ambito. Parlerò in dettaglio di scope nelle seguenti sezioni. TemplateURLviene utilizzato per chiamare una vista al fine di rendere il contenuto specifico utilizzando i dati trasferiti all'ambito della direttiva. Puoi anche usare modelloe fornire direttamente il codice HTML, come questo:

… modello: '
Info libro
'...

Nel nostro caso, abbiamo una struttura HTML complicata, ed è per questo che ho scelto il TemplateURLopzione.

2. Limitazioni della direttiva

Le direttive sono definite nel file JavaScript del tuo progetto AngularJS e utilizzate nella pagina HTML. È possibile utilizzare le direttive AngularJS nelle pagine HTML come segue:

A (Attributo)

In questo utilizzo, il nome della direttiva viene utilizzato all'interno di elementi HTML standard. Diciamo che hai un menu basato sui ruoli nella tua applicazione eCommerce. Questo menu è formato in base al tuo ruolo attuale. È possibile definire una direttiva per decidere se il menu corrente debba essere visualizzato o meno. Il tuo menu HTML potrebbe essere come sotto:

  • Casa
  • Ultime notizie
  • Amministrazione degli utenti
  • Gestione della campagna

e la direttiva come segue:

app.directive ("restricted", function () return restrict: 'A', link: function (scope, element, attrs) // Qualche funzione di controllo auth var isAuthorized = checkAuthorization (); if (! isAuthorized)  element.css ('display', 'none');)

Se usi il limitatodirettiva nell'elemento menu come attributo, è possibile eseguire un controllo del livello di accesso per ciascun menu. Se l'utente corrente non è autorizzato, quel menu specifico non verrà mostrato. 

Quindi, qual è il collegamentofunzione lì? Semplicemente, la funzione di collegamento è la funzione che è possibile utilizzare per eseguire operazioni specifiche della direttiva. La direttiva non sta solo rendendo il codice HTML fornendo alcuni input. È anche possibile associare le funzioni all'elemento direttiva, chiamare un servizio e aggiornare il valore della direttiva, ottenere gli attributi direttiva se è un Edirettiva tipo, ecc.

C (Classe)

È possibile utilizzare il nome della direttiva all'interno delle classi di elementi HTML. Supponendo che userete la direttiva sopra come Cpuoi aggiornare la direttiva limitare come Ce usarlo come segue:

  • Casa
  • Ultime notizie
  • Amministrazione degli utenti
  • Gestione della campagna

Ogni elemento ha già una classe per lo styling e come limitato la classe viene aggiunta in realtà è una direttiva.

E (elemento)

Non è necessario utilizzare una direttiva all'interno di un elemento HTML. È possibile creare il proprio elemento utilizzando una direttiva AngularJS con un E restrizione. Supponiamo che tu abbia un widget utente nella tua applicazione da mostrare nome utente, avatar, e reputazionein diversi punti della tua applicazione. Potresti voler usare una direttiva come questa:

app.directive ("utente", funzione () return limitt: 'E', link: function (scope, element, attrs) scope.username = attrs.username; scope.avatar = attrs.avatar; scope.reputation = attrs.reputation;, template: '
Nome utente: username, Avatar: avatar, reputazione: reputazione
')

Il codice HTML sarà:

Nell'esempio sopra, viene creato un elemento personalizzato e vengono forniti alcuni attributi come nome utente, avatar, e reputazione. Voglio attirare l'attenzione sul corpo della funzione di collegamento. Gli attributi degli elementi sono assegnati all'ambito della direttiva. Il primo parametro della funzione di collegamento è lo scopo della direttiva corrente. Il terzo parametro della direttiva è l'oggetto attributo della direttiva, il che significa che è possibile leggere qualsiasi attributo dalla direttiva personalizzata usando attrs.attr_name. I valori degli attributi sono assegnati all'ambito in modo che vengano utilizzati all'interno del modello. 

In realtà, puoi fare questa operazione in un modo più breve, e ne parlerò più tardi. Questo esempio è per capire l'idea principale dietro l'utilizzo.

M (coMment)

Questo uso non è molto comune, ma mostrerò come usarlo. Supponiamo che tu abbia bisogno di un modulo di commento per la tua applicazione da utilizzare in molti posti. Puoi farlo usando la seguente direttiva:

app.directive ("comment", function () return restrict: 'M', template: '')

E nell'elemento HTML:

3. Ambito isolato

Ogni direttiva ha il proprio ambito di applicazione, ma è necessario fare attenzione sull'associazione dati con la dichiarazione direttiva. Diciamo che stai implementando il cestinoparte della tua applicazione eCommerce. Nella pagina del carrello ci sono articoli già aggiunti qui prima. Ogni articolo ha il suo campo quantità per selezionare quanti oggetti vuoi comprare, come di seguito:

Ecco la dichiarazione direttiva:

app.directive ("item", function () return restrict: 'E', link: function (scope, element, attrs) scope.name = attrs.name;, template: '
Nome: nome Seleziona importo: Importo selezionato: contare
')

E per visualizzare tre elementi in HTML:

  

Il problema qui è che ogni volta che si sceglie la quantità dell'elemento desiderato, tutte le sezioni di quantità degli articoli verranno aggiornate. Perché? Perché c'è un collegamento dati bidirezionale con un nome contare, ma la portata non è isolata. Per isolare l'ambito, basta aggiungere scopo: all'attributo direttiva nella sezione ritorno:

app.directive ("item", function () return restrict: 'E', scope: , link: function (scope, element, attrs) scope.name = attrs.name;, template: '
Nome: nome Seleziona importo: Importo selezionato: contare
')

Ciò porta la direttiva ad avere il proprio ambito isolato in modo che l'associazione bidirezionale dei dati si verifichi separatamente all'interno di questa direttiva. Inoltre parlerò del scopoattributo più tardi.

4. Ambiti di applicazione della direttiva

Il vantaggio principale della direttiva è che si tratta di un componente riutilizzabile che può essere facilmente utilizzato: puoi persino farlo fornire alcuni attributi aggiuntivi a tale direttiva. Ma come è possibile passare un valore aggiunto, un legame o un'espressione a una direttiva in modo che i dati possano essere utilizzati all'interno della direttiva?

"@" Scopo: Questo tipo di ambito viene utilizzato per passare il valore all'ambito della direttiva. Supponiamo che tu voglia creare un widget per un messaggio di notifica:

app.controller ("MessageCtrl", function () $ scope.message = "Prodotto creato!";) app.directive ("notification", function () return restrict: 'E', scope: messaggio: '@' , modello: '
Messaggio
');

e puoi usare:

In questo esempio, il valore del messaggio viene semplicemente assegnato all'ambito della direttiva. Il contenuto HTML visualizzato sarà:

Prodotto creato!

"=" Ambito: In questo tipo di scope, le variabili scope vengono passate al posto dei valori, il che significa che non passeremo Messaggio, passeremo Messaggio anziché. Il motivo alla base di questa funzione è la costruzione di un bind di dati bidirezionale tra la direttiva e gli elementi della pagina o i controller. Vediamolo in azione.

.direttiva ("bookComment", function () return restrict: 'E', scope: text: '=', template: '')

In questa direttiva, stiamo cercando di creare un widget per visualizzare il testo di input di commenti per fare un commento per un libro specifico. Come puoi vedere, questa direttiva richiede un attributo testo per costruire un collegamento dati bidirezionale tra altri elementi sulle pagine. Puoi usare questo nella pagina:

Questa è la casella di testo sulla direttiva 

Questo mostrerà semplicemente una casella di testo sulla pagina, quindi aggiungiamo qualcosa in più per interagire con questa direttiva:

Questa è la casella di testo sulla pagina  
Questa è la casella di testo sulla direttiva

Ogni volta che si digita qualcosa nella prima casella di testo, verrà digitata anche nella seconda casella di testo. Puoi farlo al contrario. Nella direttiva, abbiamo passato la variabile scope commentText invece del valore, e questa variabile è il riferimento di associazione dati alla prima casella di testo. 

"&" Scopo: Siamo in grado di passare il valore e il riferimento alle direttive. In questo tipo di ambito esamineremo come passare le espressioni alla direttiva. Nei casi reali, potrebbe essere necessario passare una specifica funzione (espressione) alle direttive per evitare l'accoppiamento. A volte, le direttive non hanno bisogno di sapere molto sull'idea dietro le espressioni. Ad esempio, una direttiva apprezzerà il libro per te, ma non sa come farlo. Per fare ciò, puoi seguire una struttura come questa:

.direttiva ("likeBook", function () return restrict: 'E', scope: like: '&', template: '')

In questa direttiva, un'espressione verrà passata al pulsante della direttiva tramite piace attributo. Definiamo una funzione nel controller e la passiamo alla direttiva all'interno dell'HTML.

$ scope.likeFunction = function () alert ("Mi piace il libro!")

Questo sarà all'interno del controller e il modello sarà:

likeFunction () proviene dal controller e viene passato alla direttiva. Cosa succede se si desidera passare un parametro a likeFunction ()? Ad esempio, potrebbe essere necessario passare un valore di valutazione a likeFunction (). È molto semplice: basta aggiungere un argomento alla funzione all'interno del controller e aggiungere un elemento di input alla direttiva per richiedere il conteggio dell'avvio da parte dell'utente. Puoi farlo come mostrato di seguito:

.direttiva ("likeBook", function () return restrict: 'E', scope: like: '&', template: '
'+'')
$ scope.likeFunction = function (star) alert ("Mi piace il libro !, e ho dato" + star + "star.")

Come puoi vedere, la casella di testo proviene dalla direttiva. Il valore della casella di testo è associato all'argomento della funzione Mi piace (star: starCount). stella è per la funzione controller e starCount per il binding del valore della casella di testo. 

5. Eredità della direttiva

A volte, potresti avere una funzionalità che esiste in diverse direttive. Possono essere inseriti in una direttiva padre in modo che siano ereditati dalle direttive secondarie. 

Lascia che ti dia un esempio di vita reale. Si desidera inviare dati statistici ogni volta che i clienti spostano il cursore del mouse sulla parte superiore di un libro specifico. È possibile implementare un evento clic del mouse per la direttiva del libro, ma cosa succederebbe se fosse utilizzato da un'altra direttiva? In questo caso, puoi utilizzare l'ereditarietà delle direttive come di seguito:

app.directive ('mouseClicked', function () return restrict: 'E', scope: , controller: "MouseClickedCtrl as mouseClicked")

Questa è una direttiva genitore che deve essere ereditata dalle direttive secondarie. Come puoi vedere c'è un attributo controller della direttiva che usa la direttiva "as". Definiamo anche questo controller:

app.controller ('MouseClickedCtrl', function ($ element) var mouseClicked = this; mouseClicked.bookType = null; mouseClicked.setBookType = function (type) mouseClicked.bookType = type; $ element.bind ("click", function () alert ("Typeof book:" + mouseClicked.bookType + "inviato per analisi statistiche!");))

In questo controller, stiamo semplicemente impostando un'istanza controller della variabile booktype usando le direttive bambino. Ogni volta che fai clic su un libro o una rivista, il tipo di elemento verrà inviato al servizio back-end (ho usato una funzione di avviso solo per mostrare i dati). In che modo le direttive sui minori saranno in grado di utilizzare questa direttiva?

app.directive ('ebook', function () return require: "mouseClicked", link: function (scope, element, attrs, mouseClickedCtrl) mouseClickedCtrl.setBookType ("EBOOK");) .directive (' magazine ', function () return require: "mouseClicked", link: function (scope, element, attrs, mouseClickedCtrl) mouseClickedCtrl.setBookType ("MAGAZINE");))

Come puoi vedere, le direttive secondarie usano il richiedere parola chiave per utilizzare la direttiva genitore. E un altro punto importante è il quarto argomento della funzione di collegamento nelle direttive figlio. Questo argomento si riferisce all'attributo controller della direttiva padre che indica che la direttiva figlio può utilizzare la funzione controller setBookType all'interno del controller. Se l'elemento corrente è un eBook, è possibile utilizzare la prima direttiva e, se si tratta di una rivista, è possibile utilizzare la seconda:

Game of thrones (clicca me)
PC World (clicca me)

Le direttive secondarie sono come una proprietà della direttiva madre. Abbiamo eliminato l'utilizzo dell'evento click-mouse per ogni direttiva figlio inserendo quella sezione all'interno della direttiva genitore.

6. Debug della direttiva

Quando usi le direttive all'interno del modello, ciò che vedi nella pagina è la versione compilata della direttiva. A volte, si desidera vedere l'effettivo utilizzo della direttiva a scopo di debug. Per vedere la versione non compilata della sezione corrente, puoi usare ng-non-associabile. Ad esempio, supponiamo tu abbia un widget che stampa i libri più popolari, e qui c'è il codice per questo:

La variabile scope del libro proviene dal controller e l'output di questo è il seguente:

Se vuoi conoscere l'uso della direttiva dietro questo output compilato, puoi usare questa versione del codice:

  • libro

Questa volta l'output sarà come sotto:

È bello fino ad ora, ma cosa succede se vogliamo vedere entrambe le versioni non compilate e compilate del widget? È ora di scrivere una direttiva personalizzata che eseguirà un'operazione di debug avanzata. 

app.directive ('customDebug', function ($ compile) return terminal: true, link: function (scope, element) var currentElement = element.clone (); currentElement.removeAttr ("custom-debug"); var newElement = $ compile (currentElement) (scope); element.attr ("style", "border: 1px solid red"); element.after (newElement);)

In questa direttiva, stiamo clonando l'elemento in modalità debug in modo che non venga modificato dopo alcune serie di operazioni. Dopo la clonazione, rimuovere il personalizzato-debugdirettiva per non agire come modalità di debug e quindi compilarlo con $ complile, che è già stato iniettato nella direttiva. Abbiamo dato uno stile all'elemento di modalità di debug per enfatizzare il debug. Il risultato finale sarà il seguente:

È possibile salvare i tempi di sviluppo utilizzando questo tipo di direttiva di debug per rilevare la causa principale di qualsiasi errore nel progetto.

7. Test dell'unità direttiva

Come già saprai, il testing delle unità è una parte molto importante dello sviluppo per controllare totalmente il codice che hai scritto e prevenire potenziali bug. Non approfondirò i test unitari, ma ti fornirò un indizio su come testare le direttive in un paio di modi.

Userò Jasmine per i test unitari e Karma per il corridore di prova dell'unità. Per utilizzare Karma, è sufficiente installarlo globalmente eseguendo npm install -g karma karma-cli (devi avere Node.js e npm installati sul tuo computer). Dopo l'installazione, apri la riga di comando, vai alla cartella principale del progetto e digita karma init. Ti verrà chiesto un paio di domande come sotto per impostare i requisiti del test.

Sto usando Webstorm per lo sviluppo, e se si utilizza anche Webstorm, basta fare clic destro su karma.conf.js e selezionare Correre karma.conf.js. Questo eseguirà tutti i test configurati nel conf karma. Puoi anche eseguire test con inizio karma riga di comando nella cartella principale del progetto. Tutto ciò riguarda la configurazione dell'ambiente, quindi passiamo alla parte di test.

Diciamo che vogliamo testare la direttiva del libro. Quando passiamo un titolo alla direttiva, dovrebbe essere compilato in una vista dettagliata del libro. Quindi iniziamo.

define ("Book Test", function () var element; var scope; beforeEach (module ("masteringAngularJsDirectives")) beforeEach (inject (funzione ($ compile, $ rootScope) scope = $ rootScope; element = angular.element ( ""); $ compile (elemento) ($ rootScope) scope. $ digest ())); it (" direttiva deve essere compilata con successo ", function () expect (element.html ()). toBe (" test " )));

Nel test precedente, stiamo testando una nuova direttiva chiamata booktestQuesta direttiva prende l'argomento titoloe crea un div usando questo titolo. Nel test, prima di ogni sezione di test, stiamo chiamando il nostro modulo masteringAngularJsDirectivesprimo. Quindi, stiamo generando una direttiva chiamata booktest. In ogni fase del test, l'output della direttiva verrà testato. Questo test è solo per un controllo del valore.

8. Test di portata della direttiva

In questa sezione testeremo l'ambito della direttiva booktestQuesta direttiva genera una vista dei dettagli del libro sulla pagina e quando si fa clic su questa sezione dei dettagli, viene chiamata una variabile dell'ambito consultatisarà impostato come vero. Nel nostro test, controlleremo se consultatiè impostato su true quando viene attivato l'evento click. La direttiva è:

.direttiva ('booktest', function () return restrict: 'E', scope: title: '@', sostituire: true, template: '
titolo
', link: function (scope, element, attrs) element.bind ("click", function () console.log ("book viewed!"); scope.viewed = true;); )

Per impostare un evento su un elemento in AngularJS all'interno della direttiva, è possibile utilizzare collegamento attributo. All'interno di questo attributo, hai l'elemento corrente, direttamente associato a un evento click. Per testare questa direttiva, è possibile utilizzare quanto segue:

define ("Book Test", function () var element; var scope; beforeEach (module ("masteringAngularJsDirectives")) beforeEach (inject (funzione ($ compile, $ rootScope) scope = $ rootScope; element = angular.element ( ""); $ compile (elemento) ($ rootScope) scope. $ digest ())); it (" scope piaciuto dovrebbe essere true quando book piace ", function () element.triggerHandler (" click "); expect ( element.isolateScope (). viewed) .toBe (true);););

Nella sezione test, viene attivato un evento click utilizzando element.triggerHandler ( "click"). Quando viene attivato un evento click, la variabile visualizzata deve essere impostata come vero. Questo valore è affermato usando si aspettano (element.isolateScope (). consultati) .toBe (vero).

9. Conclusione

Per sviluppare progetti web modulari e testabili, AngularJS è la migliore in comune. Le direttive sono uno dei migliori componenti di AngularJS, e questo significa che più conosci le direttive AngularJS, più i progetti modulari e testabili che puoi sviluppare. 

In questo tutorial, ho cercato di mostrarti le best practice della vita reale sulle direttive e tenere presente che è necessario fare molta pratica per comprendere la logica dietro le direttive. Spero che questo articolo ti aiuti a capire bene le direttive AngularJS.