AngularJS

Scope e Direttive


Speaker: Alessandro Giorgetti / @a_giorgetti




pick a theme: night or blood

Alessandro Giorgetti


Socio fondatore di SID Srl - società che opera nel campo della sanità


Partecipo a DevMarche


Un po' di contatti:

Mail: alessandro.giorgetti@live.com
Blog: PrimordialCode
Twitter: @a_giorgetti
Facebook: https://www.facebook.com/giorgetti.alessandro
LinkedIn: http://www.linkedin.com/in/giorgettialessandro
Google+: https://plus.google.com/+AlessandroGiorgetti74

Slides e Demo


Live version: http://agiorgetti.github.io/AngularDevConf

GitHub: https://github.com/AGiorgetti/AngularDevConf

Scegliamo AngularJS perché:


é fico!

Background nella scrittura di applicazioni Desktop (Windows Forms, WPF).

Necessità di migrare su piattaforme Web: iniziamo con ASP.NET MVC (razor + knockout + javascript)

Volevamo una applicazione web che fosse:

  • Altamente MODULARE
  • Fortemente INTERATTIVA
  • Dannatamente RESPONSIVA

2 mesi e mezzo di lavoro per la primissima versione: funzionante, ma non completamente soddisfatti!

Abbiamo creato prototipi con tutti i maggiori framework disponibili.

Abbiamo gettato nel cestino i 2 mesi di lavoro per rifare tutto con AngularJS!

Il risultato: Codename H8

Il risultato: Codename H8

Il risultato: Codename H8

Agenda


Scope

capire gli scope per comprendere come funzionano i binding

Direttive

estendere il dialetto html creando user controls

Scope

  • Collante tra il modello e la UI: il contesto di binding.
  • Costituiscono il contesto di esecuzione per la valutazione di espressioni.
  • Hanno struttura gerarchica, simile a quella del DOM.
  • Forniscono API per osservare lo stato di oggetti ed espressioni ($watch).
  • Forniscono API per propagare cambiamenti di stato del modello generati al di fuori del normale ciclo di esecuzione di Angular ($apply)
  • Forniscono API per generare, propagare e gestire eventi ($broadcast, $emit, $on).

Scope

  • Ogni applicazione Angular ha 1 solo $rootScope.
  • E' comunque possibile creare nuovi Scope programmaticamente.
  • Quando un nuovo scope viene creato a partire da un'altro scope si parla di Child Scope.
  • Alcune direttive possono richiedere la creazione di Child Scope (ex: ng-controller).
  • Gli scope costituiscono una struttura ad albero che ha la sua radice nello $rootScope.
  • Ad ogni nodo del DOM può essere associato 1 ed 1 solo scope.

Child Scope / Inherited Scope

  • I Child Scope ereditano il prototipo del loro scope parent (prototypical inheritance).
  • Quando AngularJS deve valutare una espressione inizia dallo scope a cui l'elemento è associato.
  • Se la proprietà non viene trovata si passa ad analizzare il parent, e così via.


Demo!

Scope

Informazioni dettagliate a questo link della guida di AngularJS

Directive (Direttiva)

  • Consentono di estendere le possibilità dell'html classico abilitando la creazione di veri e propri Web Controls.
  • Le direttive sono dei marker su un elemento del DOM (css class, attributo, elemento/tag) che indicano al 'compilatore HTML' di Angular come trasformare gli elementi del DOM e quali comportamenti aggiungere.
  • Angular fornisce un set predefinito (in espansione) di direttive che consentono di rendere interattiva la pagina HTML (ngModel, ngController, ngBind, ngRepeat, ...).

Una direttiva si fa così:


var myModule = angular.module(...);
myModule.directive('directiveName', function factory(injectables) {
  var directiveDefinitionObject = {
    priority: 0,
    terminal: false,
    template: '
', // or // function(tElement, tAttrs) { ... }, // or // templateUrl: 'directive.html', // or // function(tElement, tAttrs) { ... }, replace: false, transclude: false, restrict: 'A', scope: false, controller: function($scope, $element, $attrs, $transclude, otherInjectables) { ... }, require: 'siblingDirectiveName', // or // ['^parentDirectiveName', '?optionalDirectiveName', '?^optionalParent'], compile: function compile(tElement, tAttrs, transclude) { return { pre: function preLink(scope, iElement, iAttrs, controller) { ... }, post: function postLink(scope, iElement, iAttrs, controller) { ... } } // or // return function postLink( ... ) { ... } }, // or // link: { // pre: function preLink(scope, iElement, iAttrs, controller) { ... }, // post: function postLink(scope, iElement, iAttrs, controller) { ... } // } // or // link: function postLink( ... ) { ... } }; return directiveDefinitionObject; });

La nostra 1° direttiva

Aggiungiamo comportamento ad un tag <input>


.directive('selectOnFocus', function () {
        return {
            restrict: 'A',
            link: function(scope, iElement) {
                iElement.mouseup(function(evt) {
                    evt.preventDefault();
                });
                iElement.focus(function() {
                    iElement.select();
                });
            }
        }
    })

Introducing:

  • directive - dichiarare una direttiva.
  • restrict (E, A, C, M) - impone restrizioni sulla modalità di dichiarazione.
  • link(scope, iElement, iAttrs, controller, transcludeFn){...} - aggiungere comportamento al DOM: registra gli event handler ed è responsabile dell'aggiornamento del DOM.

The link() function

argomenti:

  • scope - lo scope usato dalla direttiva.
  • iElement - elemento del DOM a cui la direttiva è collegata.
  • iAttrs - lista degli attributi dichiarati sull'elemento (condivisa tra tutte le direttive applicate sullo stesso elemento).
  • controller - lista dei controller associati alle direttive applicate allo stesso elemento.
  • transcludeFn - A transclude linking function pre-bound to the correct transclusion scope. ?!? WHAT THE H... ?!?


Demo!

La nostra 2° diretiva (hardcoded widget)

Iniziamo da del markup per una form di immissione dati usando Bootstrap


angular.module('myDirectives', [])
    .directive('myLabelInput', function () {
        return {
            restrict: 'EA',
            replace: true,
            template: '
' + '' + '' + '
' } });

introducing: replace & template

Demo!

Make it reusable: isolated scope

Consentire alle direttive di creare i propri scope è fondamentale per realizzare componenti che siano riutilizzabili sfruttanto il meccanismo dei binding senza dover 'impazzire' nel gestire la comunicazione con l'esterno tramite attributi.


.directive('myLabelInput2', function () {
            return {
                restrict: 'EA',
                replace: true,
                scope: { // isolated scope!!! (it does not inherits the scope from its enclosing scope).
                    id: '@', // one-way binding
                    type: '@',
                    placeholder: '@',
                    ngModel: '=' // two-way binding
                },
                template: '
' + '' + '' + '
' }; });

introducing: isolated scope - gli scope isolati non ereditano il prototipo del parent scope, serve a prevenire leak di dati e ad evitare modifiche accidentali dei dati sui parent scope.

Passare parametri allo Scope

il passaggio di parametri avviene dichiarativamente dal template HTML verso le proprietà definite sullo scope:

scope: {
    oneWayExpression: '@',
    twoWayBinding: '=',
    oneWayBehavior: '&'
} 

<div my-directive
    one-way-expression="questa espressione viene valutata: {{ i++ }}"
    two-way-binding="variabile"
    one-way-behavior="func(variabile)" >
</div>
  • @ - binding tra la variabile sullo isolated scope ed il valore di un attributo sul DOM (eventualmente una espressione che viene valutata, il risultato è una stringa).
  • = - binding bidirezionale tra la proprietà definita sullo scope isolato della direttiva e la proprietà associata allo scope esterno (definita mediante attributo sul template).
  • & - metodo per eseguire funzioni e valutare espressioni nel contesto del chiamante: è possibile passare parametri alla funzione definita sullo scope esterno utilizzando una mappatura dei parametri, ex: func(variabile) può essere invocata come: $scope.oneWayBehavior({ variabile: 'xxx'}).

Demo!

The compile() function

Se si devono eseguire operazioni sul DOM o alterare il template della direttiva è buona norma usare la funzione 'compile'.

Argomenti:

  • iElement
  • iAttrs

Se si usa la funzione compile e si vuole renedere la direttiva dinamica (utilizzando uno scope) occorre far ritornare una link function ed eseguire la compilazione ed il linking allo scope manualmente usando il servizio $compile messo a disposizione da AngularJS

Introducing: compile



Demo!

Transclusion

'Parolona' per indicare una cornice (frame).


Consente di iniettare contenuto (tag html, templates, etc) all'interno del template di una direttiva già esistente senza dover operare ricorrendo alla funzione 'compile' vista in precedenza.


.directive('myCenteredFrame', function () {
            return {
                restrict: 'EA',
                transclude: true,
                //replace: true,
                template: '<div class="row">' +
                          '<div ng-transclude class="col-xs-8 col-xs-offset-2"></div>' + 
                          '</div>'
            };
        })

Introducing: transclude & ng-transclude

Demo!

Controller

Le direttive possono avere necessità di comunicare tra loro, possono farlo tramite:

  • Attributi sul DOM (denormalizzati e accessibili l'oggetto 'attrs' sulle funzioni compile e link (roba già vista).
  • Associando un 'controller' alla direttiva ed eseguendo il 'require' di altre direttive sullo stesso nodo se si vuole accedere ai loro controller.


La funzione che definisce il controller puó accettare dei parametri risolti con dependency injection.



Introducing: controller & require

Nell'ordine di esecuzione delle funzioni il controller viene istanziato PRIMA della chiamata alle pre-linking functions. Consente alle direttive di comunicare e modificare il comportamento l'una dell'altra.

Demo: Take a look at the 'myTabs & myPane' sample directives!

Ancora due cose:

  • priority - imposta ordine di priorità nell'esecuzione delle direttive: maggiore il numero più alta la priorità. All'interno della stessa priorità l'ordine di esecuzione non è garantito.
  • terminal - arresta l'esecuzione delle direttive sull'ordine di priorità della direttiva corrente: direttive con priorità inferiore non vengono eseguite.


  • No Demo this time!

That's it!   Q & A time!



Contacts:

Mail: alessandro.giorgetti@live.com
Blog: PrimordialCode
Twitter: @a_giorgetti
Facebook: myself
LinkedIn: myself again
Google+: and again