5. AngularJS
Advanced
17.07.2014
AngularJS - Generale
E’ un framework JavaScript:
● per lo sviluppo di applicazioni Web in modalità Single
Page Application
● “forza” l’uso del pattern MVC
● implementa il two-way data-binding
● pensato per facilitare il test:
o separazione delle competenze
o dependency injection
o mock del back end
9. AngularJS
Advanced
17.07.2014
Direttive - Definizione
● restrict - definisce le modalità di utilizzo
ammesse (e.g. restrict: ‘EA’)
● template - inline HTML template
● templateUrl - url per scaricare il template
della direttiva tramite richiesta ajax
● replace - definisce se sostituire o
appendere il template all’elemento originale
10. AngularJS
Advanced
17.07.2014
Direttive - Definizione
● priority - definisce l’ordine di
“esecuzione” delle direttive
● terminal - indica se il processo di
compilazione deve terminare alla direttiva
corrente
● require - specifica se è necessario
utilizzare altre direttive: [?][^]directive
11. AngularJS
Advanced
17.07.2014
Direttive - ciclo di vita
1. costruzione
2. compilazione del template
3. *creazione dello scope e del controller
4. *pre-linking
5. ricorsione sui figli
6. post-linking
12. AngularJS
Advanced
17.07.2014
Direttive - scope
scope: false | true | { … }
● false - è il default, la direttiva condivide lo
scope del controllore che la contiene
● true - viene creato uno scope per la
direttiva che “deriva” dallo scope contenitore
● { … } - viene creato uno scope isolato
13. AngularJS
Advanced
17.07.2014
● fondamentali per: separazione e riusabilità
● permettono di definire dei collegamenti con il
mondo esterno:
o @[attr] - collegamento monodirezionale con il
valore di un attributo del DOM
o =[?][attr] - collegamento bidirezionale
o &[attr] - esecuzione di un espressione nello
scope contenitore
Direttive - scope isolati
14. AngularJS
Advanced
17.07.2014
Direttive - controller
● si dichiarano come i controller “normali”
● iniettabile con:
o qualunque servizio disponibile nell’applicazione
o servizi specifici: $scope, $element, $attrs,
$transclude
● logica e varibili “locali”
● no manipolazione del DOM
● opzionale
16. AngularJS
Advanced
17.07.2014
Direttive - link
● è la funzione principale
● nel 80% dei casi non serve altro
● manipolazione del DOM
● registrazione agli eventi del DOM:
o è fondamentale utilizzare $scope.$apply per
propagare le modifiche
● registrazione dei watch
19. AngularJS
Advanced
17.07.2014
Direttive - compilazione
compile: function(tElement, tAttrs) { ... }
● modifica il DOM del template
● non ha accesso allo scope
● è invocata per ogni direttiva
● *ritorna una funzione/oggetto di link
● migliora le performance
20. AngularJS
Advanced
17.07.2014
Direttive - comunicazione
● Direttiva → Controller:
o scope isolato con binding &
● Controller → Direttiva:
o scope isolato con binding mono o bidirezionale
● Direttiva ←→ Direttiva:
o attraverso i controller
25. AngularJS
Advanced
17.07.2014
Testing - Jasmine - spies
● Mock (FakeItEasy, mockito, etc.)
● Comportamento:
o invocazione metodo originale
o invocazione metodo fake
o lancio eccezioni
● Asserzioni sull’invocazione dei metodi:
o argomenti
o ordine
26. AngularJS
Advanced
17.07.2014
Testing - angular.mock
● module(…) - registrare moduli per l’injector
● inject(…) - inietta le variabili richieste
● $httpBackend - simula la comunicazione
HTTP con il back end e di definire delle
attese:
o when???(…).response(…)
o expect???(…)[.response(…)]
o flush([count])
27. AngularJS
Advanced
17.07.2014
Testing - controller
● Caricare il modulo
● Farsi iniettare le dipendenze
● Creare uno scope
● Creare i mock necessari
● Creare il controller
● Utilizzare il metodo $apply dello scope
28. AngularJS
Advanced
17.07.2014
Testing - direttive
● Caricare il modulo
● Preparare l’HTML di test (stringa)
● Farsi iniettare $compile e $rootScope
● Compilare l’HTML
● Modificare scope → verificare il DOM
● Modificare il DOM e lanciare gli eventi →
verificare lo scope
30. AngularJS
Advanced
17.07.2014
Dipendenze - AngularJS’s way
Modulo - raggruppamento di “oggetti Angular”
angular.module('???', […])
.config(function(…){…})
.run(function(…){…})
var m = angular.module('???')
Angular non si occupa di caricare gli script!
31. AngularJS
Advanced
17.07.2014
Dipendenze - RequireJS
● Definisce le dipendenze fra file/moduli
● Carica file/moduli
● Garantisce l’ordine di caricamento
● Asincrono
● Usabile con librerie non-RequireJS
● Elimina variabili globali
● Include un tool per minimizzare e offuscare il
codice (r.js)
33. AngularJS
Advanced
17.07.2014
Dipendenze - RequireJS
● Definire nelle sezioni path e shim, la
posizione e le dipendeze delle librerie
angular
● Bootstrap manuale:
o No ng-app
o utilizzare plugin domReady
o utilizzare angular.bootstrap(…)
● Definire due main.js per test ed “esecuzione”
priority è un numero, le direttive con priority maggiore vengono eseguite prima
a parità di priority l’ordine è indeterminato
priority di default è zero, alcune direttive che la cambiano sono: ng-disabled (100), ng-controller (500), ng-include (400), etc.
terminal deve essere usato in congiunzione con priority
require è ideale per la comunicazione tra direttive, che vedremo dopo, e possibile specificare una o più direttive (utilizzando un array). ? vuol dire che è opzionale, ^ vuol dire che la direttiva può essere su un elemento contenitore. Esempio menuDirective.js
la costruzione è invocata una volta sola durante il ciclo di vita dell’applicazione. Volendo far vedere il codice della slide #5 per far capite cos’è la costruzione
scope e controller vengono creati solo se necessario;
pre-linking → link allo scope prima della compilazione/linking dei figli
post-linking → link allo scope dopo la compilazione/linking dei figli
Direttive con priority maggiore vengono compilate per prime. Le funzioni di pre-link sono invocate nello stesso ordine mentre le funzioni di post-link vengono eseguite in ordine inverso;
Le direttive di default condividono lo scope del controller che le contiene
true → se ci sono più direttive sullo stesso elemento che richiedono un nuovo scope con true allora AngularJS ne crea uno solo condiviso fra tutte
deriva prototipicamente
gli scope isolati sono scope che non partecipano alla normale eriditarietà prototipale degli scope di AngularJS
uno scope isolato “vuoto” non contiene nulla se non ciò che ci viene messo dalla direttiva
gli scope isolati sono fondamentali per evitare che una direttiva possa interferire in alcun modo con il comportamento del controller o di altre direttive
il valore di un attributo del DOM è sicuramente una stringa quindi il binding @darà sempre una stringa
con il binding = il ? serve per dire che non è obbligatorio che l’attributo ci sia, di default è obbligatorio
il binding & è perfetto stabilire una comunicazione fra direttiva e controller (nel verso opposto basta usare un binding :-) ). Vedi ui-bootstrap.js riga 2876
VEDERE ESEMPIO LIVE SUL BROWSER
ideali per concentrare tutta la logica della direttiva e per contenere variabili che non devono essere esposte sullo scope per il binding
$transclude è la stessa funzione che viene passata ai metodi di link, dato che non è consigliabile manipolare il DOM dal controller meglio non usarla
scope: è lo scope della direttiva (lo stesso del controller)
iElement: è [il clone] del template
iAttrs: è la lista “normalizzata” (cioè con i nomi in camel case) degli attributi presenti sull’elemento. E’ condiviso da tutte le direttive presenti su un elemento
controller: è il controller della direttiva o una lista di controller se è stato utilizzata l’opzione require
transcludeFn: è una funzione che consente di clonare e linkare il template per poi inserirlo nel DOM
Direttive con priority maggiore vengono compilate per prime. Le funzioni di pre-link sono invocate nello stesso ordine mentre le funzioni di post-link vengono eseguite in ordine inverso;
pre: dato che viene eseguita prima che i figli siano stati linkati è meglio evitare di manipolare il DOM. Deve essere usata solo per lavori preparatori o attività che non riguardano il DOM ma che comunque sono relative all’istanza specifico del DOM e non al template. E.g. direttiva form in angular.js alla riga 16345
eseguita dopo che il template è stato clonato
esempio completo vedere cmswSpinnerDirective.js
in questo esempio è stato utilizzato anche l’attributo replace per sostituire il template della direttiva al posto dell’HTML originale
il contenuto di una direttiva che ha transclude a true non fa riferimento allo scope della direttiva (anche se vi è contenuto nell’albero HTML) ma fa riferimento allo scope subito fuori dalla direttiva
Direttive con priority maggiore vengono compilate per prime. Le funzioni di pre-link sono invocate nello stesso ordine mentre le funzioni di post-link vengono eseguite in ordine inverso;
vedere come esempio ngRepeat in angular.js alla riga 20393 - 20404
della funzione/oggetto di link se ne parla dopo
migliora le performance perché se la nostra direttiva è utilizzata insieme, ad esempio, ad ng-repeat le operazione fatte nella funzione compile verranno effettuate una volta sola
ngCloak si autoelimina dal proprio template nella funzione compile (angular.js riga 18624)
se viene dichiarata la funzione compile non può essere dichiarata la funzione/oggetto link, quest’ultimo deve essere ritornato eventualmente dalla funzione compile
Direttive con priority maggiore vengono compilate per prime. Le funzioni di pre-link sono invocate nello stesso ordine mentre le funzioni di post-link vengono eseguite in ordine inverso