Gerarchia di scope

21 aprile 2015

Abbiamo visto come ciascun controller ha uno scope associato che consente il dialogo tra il modello dei dati e la view. Una importante caratteristica degli scope è la loro organizzazione gerarchica e il meccanismo di ereditarietà prototipale tipica degli oggetti JavaScript.

Se definiamo controller annidati, lo scope del controller più interno eredita le proprietà e i metodi dello scope più esterno. Anzi, per essere più precisi, eredita le proprietà e i metodi di tutti gli scope più esterni, a partire dallo scope associato all’applicazione chiamato $rootScope.

Consideriamo ad esempio il seguente codice HTML:

<div ng-controller="userController"> 
   <p>Nome: <input type="text" ng-model="utente.nome"></p> 
   <p>Cognome: <input type="text" ng-model="utente.cognome"></p> 
   <p ng-controller="greetingController">{{saluta()}}</p> 
</div> 

Si tratta della riorganizzazione del codice visto negli esempi precedenti in cui abbiamo definito una view interna gestita dal controller greetingController. Ridefiniamo il codice dei rispettivi controller annidati come nel seguente esempio:

<script type="text/javascript"> 
angular.module("myApp", []) 
	.controller("userController",   
		function($scope) { 
			$scope.utente = {nome: "Mario", cognome: "Rossi"}; 
		}) 
	.controller("greetingController",   
		function($scope) { 
			$scope.saluta = function() { 
				return "Buongiorno " + $scope.utente.nome + " " + $scope.utente.cognome + "!" 
			};   
		}); 
</script> 

Notiamo che il controller userController si limita a definire il modello dei dati, mentre il controller greetingController definisce il metodo saluta(). Quest’ultimo metodo però accede al nome e cognome dell’utente come se il modello dei dati fosse stato definito nello scope corrente. Questa possibilità è derivata proprio dal fatto che lo scope del controller greetingController eredita il modello dei dati definito dal controller padre userController.

Questa caratteristica risulta molto comoda in diversi contesti, ma introduce una dipendenza tra gli scope dei vari controller che occorre tener presente in caso di refactoring dell’interfaccia utente.

Controller senza scope

È possibile definire un controller senza ricorrere agli scope. In questo caso è lo stesso controller a veicolare il modello dei dati e le funzionalità necessarie alla view. Infatti possiamo definire il nostro controller come costruttore del modello dei dati come mostrato di seguito:

<script type="text/javascript"> 
angular.module("myApp", []) 
	.controller("userController",   
		function() {
			this.utente = {nome: "Mario", cognome: "Rossi"}; 
			this.saluta = function() {
				return "Buongiorno " +
				       this.utente.nome + " " +
					   this.utente.cognome + "!" 
			};   
		}); 
</script> 

Possiamo notare l’assenza del parametro $scope e l’utilizzo di this come supporto al modello dei dati e delle funzionalità utili alla view.

Utilizzando questo approccio dovremo modificare l’aggancio del controller nella view nel seguente modo:

<div ng-controller="userController as userCtrl"> 
   <p>Nome: <input type="text" ng-model="userCtrl.utente.nome"></p> 
   <p>Cognome: <input type="text" ng-model="userCtrl.utente.cognome"></p> 
   <p>{{userCtrl.saluta()}}</p> 
</div>

In pratica associamo una sorta di alias al controller tramite il costrutto controller as ed utilizziamo questo alias per accedere al modello dei dati ed ai metodi.

Questo approccio consente di ottenere una maggiore chiarezza nel codice HTML quando abbiamo controller annidati, ma ha l’inconveniente di non poter sfruttare l’ereditarietà degli scope. Riprendendo l’esempio dei controller annidati visto nella sezione precedente, con questo approccio possiamo riscriverlo nel seguente modo:

<div ng-controller="userController as userCtrl">
	<p>Nome: <input type="text" ng-model="userCtrl.utente.nome"></p>
	<p>Cognome: <input type="text" ng-model="userCtrl.utente.cognome"></p>
	<p ng-controller="greetingController as greetingCtrl">{{greetingCtrl.saluta(userCtrl.utente)}}</p>
</div>

<script type="text/javascript">
angular.module("myApp", [])
	.controller("userController",
		function() {
			this.utente = {nome: "Mario", cognome: "Rossi"};
		})
	.controller("greetingController",
		function() {
			this.saluta = function(utente) {
				return "Buongiorno " + utente.nome + " " + utente.cognome + "!"
			};
		});
</script>

A parte l’esplicitazione dell’appartenenza dei vari elementi ai rispettivi controller, notiamo che abbiamo dovuto modificare la definizione del metodo saluta() prevedendo il passaggio dell’oggetto utente per sopperire alla mancanza dell’ereditarietà degli scope.

Osservazioni sui controller

Uno degli errori più frequenti soprattutto da parte di chi si avvicina per la prima volta ad Angular è il sovraccaricare i controller di funzionalità che non gli appartengono. Per il principio della separazione delle competenze tra i componenti Angular, è opportuno sottolineare che il compito principale di un controller consiste nell’impostare lo stato iniziale del modello dei dati e la definizione di funzionalità per la view.

In particolare un controller non deve:

  1. Manipolare il DOM,
    questo compito è riservato alle direttive
  2. Formattare l’input,
    per questo compito è opportuno utilizzare i form Angular
  3. Formattare l’output,
    questo è compito dei filtri
  4. Condividere dati con altri controller,
    per questo compito è opportuno creare dei servizi
  5. Implementare funzionalità generali,
    l’implementazione di funzionalità che non riguardano direttamente l’interazione tra dati e view deve essere fatta nei servizi
Tutte le lezioni

1 ... 4 5 6 ... 38

Se vuoi aggiornamenti su Gerarchia di scope inserisci la tua e-mail nel box qui sotto:
Tags:
 
X
Se vuoi aggiornamenti su Gerarchia di scope

inserisci la tua e-mail nel box qui sotto:

Ho letto e acconsento l'informativa sulla privacy

Acconsento al trattamento di cui al punto 3 dell'informativa sulla privacy