Una delle prime domande che ci si pone avvicinandosi per la prima volta ad AngularJS riguarda il meccanismo con cui riesca ad implementare il two-way binding. Come fa Angular a rendersi conto che il valore di una variabile dello scope è cambiato e ad aggiornare tutti gli elementi della pagina collegati ad essa?
Al di là della semplice curiosità, comprendere almeno a grandi linee come Angular funziona internamente può aiutare ad evitare di scrivere codice che presenta malfunzionamenti spesso difficili da diagnosticare. La conoscenza dei meccanismi interni diventa poi necessaria quando vogliamo fare un utilizzo avanzato del framework.
In questa sezione descriveremo il meccanismo interno che consente ad Angular di tenere scope e view sincronizzati e vedremo come anche noi possiamo sfruttare in determinate situazioni alcune delle funzionalità interne del framework.
Come è fatto il two-way-binding
Qualsiasi programmatore JavaScript conosce il modello a eventi del DOM e sa come poterlo sfruttare per reagire all'interazione dell'utente con la pagina Web. È sufficiente individuare l'evento giusto e definire un apposito gestore che esegua il nostro codice JavaScript.
>> Leggi tutto sulla gestione degli eventi in JavaScript
In questo modo possiamo, ad esempio, catturare quello che un utente ha inserito in una casella di testo e visualizzarlo in qualche punto della pagina, come nel seguente esempio:
<div>
Nome: <input type="text" id="txtNome"><br/>
<span id="spnMessaggio"></span>
</div>
<script type="text/javascript">
var txtNome = document.getElementById("txtNome");
txtNome.addEventListener("input", function(e) {
var spnMessaggio = document.getElementById("spnMessaggio");
spnMessaggio.innerHTML = "Buongiorno " + e.target.value;
});
</script>
Lo stesso effetto è ottenuto con AngularJS senza bisogno di scrivere codice JavaScript:
<div>
Nome: <input type="text" id="txtNome" ng-model="nome"><br/>
<span id="spnMessaggio" ng-show="nome != null && nome != ''">Buongiorno {{nome}}</span>
</div>
Watch
Come fa Angular ad ottenere questo comportamento? Il principio di base è abbastanza semplice: ogni volta che leghiamo un elemento dell'interfaccia utente ad una variabile del modello dei dati, Angular tiene sotto controllo il suo valore creando un cosiddetto watch.
Facendo riferimento al nostro esempio, avremo un watch associato alla variabile nome
dal momento che è stata definita come variabile del modello dei dati tramite la direttiva ng-model
.
Quando si verifica un evento che modifica il contenuto della variabile nome
, come ad esempio, l'inserimento di un valore nella casella di testo associata, viene automaticmanete avviato un processo di valutazione chiamato digest loop.
Digest loop
Il digest loop consiste nello scorrere la lista dei watch allo scopo di individuare le variabili il cui valore è cambiato dall'ultima esecuzione del loop. Se il valore di una variabile è cambiato, vengono eseguite le opportune attività che consentono, ad esempio, di aggiornare il DOM nei punti in cui la variabile è utilizzata.
Il digest loop termina quando non ci sono più aggiornamenti da eseguire. Può però succedere che una variabile a cui è associato un watch venga modificata proprio nel corso del digest loop, ad esempio perché il suo valore dipendeva da un'altra variabile tenuta sotto controllo da un altro watch. In questo caso il digest loop viene rieseguito fino a quando non ci sono più variazioni a catena.
Questo meccanismo consente non solo di tenere aggiornata automaticamente l'interfaccia utente, ma anche di mantenere sincronizzato il contenuto dei controlli HTML con le variabili del modello associate. In altre parole, il digest loop
è il meccanismo che implementa il two-way binding
di AngularJS.