AngularJS – MVC in azione
Nello scorso articolo abbiamo visto cos’è AngularJS, come funziona e quali sono le sue grandi potenzialità.
Sicuramente ho usato termini non familiari a tutti, ma che spero siano entrati a far parte delle vostre basilari competenze in fatto di AngularJS.
Oggi vediamo come implementare un’applicazione AngularJS tramite pattern MVC.
Per questo articolo utilizzerò la versione 1.2.8 di Angular.
La lista della spesa
Come ogni guida che si rispetti, propongo in questo articolo la realizzazione di una classica applicazione per l’apprendimento, come la lista della spesa, o una lista di cose da fare.
Insomma la nostra applicazione dovrà permetterci di inserire una nuova voce e di segnare se tale voce è stata fatta o meno.
Con questa semplice ed efficace applicazione avrò modo di mostrarvi in azione il tanto blasonato pattern MVC e il Two-way data-binding di cui abbiamo accennato qualcosa nel precedente articolo (in realtà queste due caratteristiche sono presenti in qualsiasi applicazione Angular).
Si potrebbe pensare che, arrivati a questo punto, sapendo cosa dobbiamo fare e quali strumenti utilizzare, ci si possa buttare direttamente a scrivere codice: niente di più sbagliato!
Quando si lavora con oggetti e pattern come può essere MVC, la prima cosa da fare è fermarsi a riflettere, se necessario munirsi di carta e penna, e sviscerare le idee individuando Model, Controller e View.
Per le applicazioni ad oggetti più complesse la progettazione avviene tramite l’utilizzo di diagrammi UML o altri modelli concettuali, che aiutano anche lo sviluppo della documentazione.
Organizziamo le idee
Per la nostra applicazione possiamo individuare molto facilmente i tre elementi su cui sarà necessario lavorare:
- il Model, che rappresenta i dati dell’applicazione: sarà chiaramente la lista della spesa, ovvero una variabile javascript (in questo caso un array) che sarà modificabile e visibile sia dal controller che dalla view; trattandosi di Angular, la sincronizzazione di tale variabile sarà istantanea, tutto questo grazie al Two-way data-binding.
- il Controller, che per definizione è la parte dell’applicazione che effettua le operazioni sui dati (o logica di business), sarà una porzione di codice che si occuperà di inizializzare i vari elementi della lista e di gestire l’aggiornamento di tale lista. Il controller in Angular è collegato a un elemento del DOM (Document Object Model) tramite una direttiva. L’elemento del DOM segna il dominio in cui tale controller può operare, questo grazie agli scope di Angular. Ma cosa vuol dire tutto questo? Un momento, ve lo spiegherò a breve
- la View, sarà rappresentata dall’HTML, in questo caso una lista puntata, un input testuale e un bottone.
Una volta organizzate le idee, abbiamo bene chiaro come procedere e scrivere un codice ben organizzato.
Gli “scope”: come ci aiutano e come cambiano il modo di lavorare?
A questo punto è fondamentale conoscere il meccanismo che permette ad Angular di offrire tutti questi vantaggi e scoprire cosa si nasconde dietro i termini strani utilizzati fino ad ora per introdurre l’argomento: partiamo dallo scoprire cosa sono gli “scope”.
Il concetto alla base degli scope non è semplice: la pagina ufficiale che Angular ha dedicato agli scope sostiene che “lo scope è la colla tra il controller e la view”.
In pratica lo scope permette la comunicazione tra la view ed il controller, rendendo così possibile l’accesso al model, mantenendolo sincronizzato.
Gli scope inoltre permettono il controllo delle variazioni del model, oltre al lancio di un determinato evento (ad esempio, se viene modificata tale variabile allora sarà richiama tale funzione).
Possiamo interagire con questi eventi tramite le API fornite dallo scope di Angular. Le API sono specifiche funzioni o classi che sono messe liberamente a disposizione da chi realizza un’applicazione e possono essere utilizzabili da chi programma.
Vedremo nei prossimi articoli come utilizzare tali API per ‘tenere d’occhio’ il Model.
Gli scope in sostanza sono quelli che, sotto la scocca, fanno funzionare il meccanismo del two-way data-binding.
Gli scope sono disposti secondo una struttura gerarchica che è l’impronta della struttura DOM (il DOM è una rappresentazione ad oggetti dell’HTML): esiste infatti uno scope padre chiamato $rootScope, collegato all’elemento che contiene l’attributo ng-app, da cui nascono tutti gli altri scope e che è visibile in tutta l’applicazione.
Questo collegamento tra HTML e scope crea un contesto dove Angular può valutare le espressioni sullo scope: in pratica se, per esempio, uno scope è associato ad un determinato div, le modifiche che verranno apportate sullo scope saranno percettibili solo all’interno di quel div.
Per capire meglio come funzionano gli scope consiglio di fare esercizio con la programmazione ad oggetti in javascript e, per chi non ha dimestichezza con il DOM, cominciare ad utilizzare linguaggi come jQuery.
Probabilmente non vi sarà ancora del tutto chiaro come utilizzare gli scope, perciò passiamo subito alla pratica e andiamo a guardare il codice per vedere come mettere in pratica questo pieno di nozioni!
Finalmente codice
Partiamo creando l’applicazione, potete visionare la demo completa qui:
<!doctype html> <html ng-app="listApp"> <head> <title>Lista della spesa</title> <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.8/angular.min.js"></script> </head> <body> <div ng-controller="ListCtrl"> </div> <script type="text/javascript" src='assets/js/main.js'></script> </body> </html>
Utilizziamo l’attributo ng-app per definire la nostra applicazione dandogli anche un nome ng-app=”listApp”.
Includiamo Angular da Google CD e predisponiamo un file js che sarà il nostro applicativo vero e proprio (main.js).
Fatto questo creaimo un div (o un qualsiasi altro tag HTML) ed inseriamo l’attributo ng-controller, dandogli un nome, che per convenzione finisce sempre con Ctrl.
Questo sarà associato ad uno scope tramite la direttiva ng-controller, il div sarà il contesto (o dominio) del controller.
Da ora in poi tutto il codice html andrà inserito dentro questo div.
Passiamo al file javascript:
var listaDellaSpesa = angular.module('listApp', []); listaDellaSpesa.controller('ListCtrl', ['$scope',function ListCtrl($scope) { }]);
Definiamo l’applicazione Angular tramite angular.module(‘listApp’, []): quello che definiamo è un modulo Angular. Ci sono già diversi moduli che vedremo più avanti e ci permetteranno di fare ancora più cose con Angular; per importarle è sufficiente importare il relativo file js e inserire il nome nell’array passato come secondo parametro.
Alla seconda riga andiamo a definire il controller agendo sulla variabile dell’applicazione.
Come primo parametro passiamo il nome del controller (questo nome e il nome dell’applicazione devono corrispondere a quelli delle direttive utilizzate nell’ HTML, relativamente in ng-app ed ng-controller).
Come secondo passiamo la funzione che sarà il vero controller.
Gli esperti avranno notato che non viene passata la funzione nel modo classico, ma viene passato un array:
[‘$scope’,function ListCtrl($scope) {
}]
Questa dicitura dice ad Angular che deve passare come parametro la variabile $scope; avvalendosi della Dependency Injection, Angular è in grado di fornirci gli oggetti che ci servono quando ci servono; in questo modo esplicitando i nomi permettiamo ad Angular di capire di che variabile si tratta anche se il js è minificato.
All’interno della funzione inseriremo la logica applicativa, dobbiamo perciò inizializzare la lista e fornire una funzione per aggiungere un elemento alla lista:
listaDellaSpesa.controller('ListCtrl', ['$scope',function ListCtrl($scope) { $scope.lista = [ { 'nome' : 'Uova', 'comprato' : true }, { 'nome' : 'Latte', 'comprato' : false } ]; $scope.aggiungi = function(){ $scope.lista.push({ 'nome':document.getElementById("input_nome").value, 'comprato':false }); }; }]);
Inseriamo qui il model nello scope: $scope.lista.
La nostra lista è un array di array associativi.
Ogni elemento della lista avrà un nome e uno stato (comprato o no).
Aggiungiamo allo scope anche la funzione aggiungi che inserirà un nuovo elemento nella lista prendendo il valore da un input con id = input_nome.
A questo punto ci manca solo la view:
<ul> <li ng-repeat="elemento in lista"> <input type="checkbox" ng-model="elemento.comprato" /> <span ng-if="!elemento.comprato">{{ elemento.nome }}</span> <span ng-if="elemento.comprato" style="text-decoration:line-through;">{{ elemento.nome }}</span> </li> </ul> <input type='text' id='input_nome'/><button ng-click="aggiungi()">Aggiungi</button>
Trovandoci all’interno del div creato prima, siamo all’interno del contesto di $scope.
Questo ci permette di accedere a tutti i dati dello scope (è qui che si vede come lo scope lega la view con il controller, e come Angular osserva tutte le variazioni tramite lo scope).
Possiamo quindi iterare la lista tramite una direttiva ng-repeat nel tag li, e sincronizzare il valore “comprato” di ogni elemento con una checkbox tramite la direttiva ng-model.
Di queste due direttive abbiamo già parlato nella guida precedente, mentre non abbiamo visto ng-if: è una direttiva che valuta la condizione passata e solo se questa è vera stampa il codice html all’interno del tag, altrimenti verrà rimosso dal DOM.
In questo caso ho usato molto basilarmente uno span che barra il testo se l’elemento è stato comprato mentre lo fa vedere in chiaro se non è ancora stato comprato.
Creiamo infine un campo testo con id = “input_nome” ed un bottone che quando viene cliccato richiama la funzione aggiungi grazie all’utilizzo di ng-click.
Per visualizzare il risultato finale, qui:
trovate la demo dell’app che abbiamo sviluppato in questo articolo.
Conclusioni
Abbiamo quindi appreso come utilizzare il modello MVC ed avere dimestichezza con direttive e scope.
Come avrete notato abbiamo fatto tanto con pochissimo codice.
Vedremo nel prossimo articolo come creare applicazioni single-page.
4 commenti
Trackback e pingback
[…] Nello scorso articolo abbiamo visto cos’è AngularJS, come funziona e quali sono le sue grandi potenzialità.Sicuramente ho usato termini…
[…] con la nostra guida su Angular. Come promesso nel precedente articolo, dopo aver visto come funziona e come si…