In questa lezione vediamo come definire il routing per le nostre applicazioni, cioè come mappare uno specifico URL su una determinata vista. L'esempio che vogliamo implementare prevede una vista per mostrare l'elenco degli utenti ed una vista che visualizza i dettagli di un singolo utente. Identifichiamo la prima vista mediante l'URL relativo /utenti
e la seconda con un URL del tipo /utenti/1234
dove 1234
è l'identificatore dell'utente.
In corrispondenza di questi URL relativi verranno generati degli URL assoluti sfruttando il simbolo standard # (hashtag) per indicare la navigazione all'interno della pagina HTML. Cioè, se la pagina che contiene la nostra applicazione ha come URL http://www.myapp.com/index.html
, l'URL assoluto per individuare la vista degli utenti sarà http://www.myapp.com/index.html#/utenti
.
Una volta individuati gli URL da mappare, configuriamo il servizio di routing utilizzando il metodo config()
del modulo principale della nostra applicazione:
angular.module("myApp", ["ngRoute"])
.config(function($routeProvider) {
$routeProvider.when("/utenti", {...})
.when("/utenti/:userId", {...})
.otherwise({redirectTo: "/utenti"});
});
Come possiamo vedere dal codice, utilizziamo $routeProvider
per configurare il servizio di routing e non il servizio direttamente, dal momento che in fase di configurazione esso non è disponibile.
L'oggetto $routeProvider consente di specificare tramite il metodo when() la mappatura tra un URL relativo e un oggetto di configurazione. Questa mappatura è chiamata route.
Il metodo otherwise() consente di gestire gli eventuali URL per i quali non è stata definita una mappatura. Questo consente di catturare situazioni in cui l'utente inserisce un URL errato.
Nel nostro caso abbiamo passato al metodo otherwise()
l'oggetto che specifica di reindirizzare l'utente all'URL /utenti
.
L'oggetto di configurazione di ciascun URL prevede la possibilità di specificare i dettagli della vista da caricare. Ad esempio, il seguente oggetto definisce il template HTML ed il relativo controller da caricare in corrispondenza dell'URL /utenti
:
{
templateUrl: "templates/listaUtenti.html",
controller: "listaUtentiCtrl"
}
La proprietà templateUrl
definisce il file HTML da caricare mentre la proprietà controller
definisce il nome del controller da associare alla vista. Il file HTML conterrà esclusivamente il markup necessario da iniettare all'interno della vista e può contenere direttive ed espressioni Angular. Il seguente è un esempio di come potrebbe essere il template della vista che visualizza l'elenco degli utenti:
<ul>
<li ng-repeat="utente in utenti">
<a href="#/utenti/{{utente.id}}">{{utente.nome}} {{utente.cognome}}</a>
</li>
</ul>
Mentre quello che segue potrebbe essere il controller associato:
angular.module("myApp")
.controller("listaUtentiCtrl", function($scope, utentiService) {
$scope.utenti = utentiService.utenti;
});
Nell'esempio abbiamo supposto che il controller dipenda da un servizio utentiService che mette a disposizione l'elenco degli utenti. Oltre a poter condividere l'elenco degli utenti in tutta l'applicazione, questo approccio ci consente di avere una certa indipendenza tra le modalità con cui vengono caricati i dati ed le modalità con cui essi vengono visualizzati.
I dati relativi agli utenti potrebbero essere caricati dal server tramite una chiamata Ajax o, nel caso più semplice, essere rappresentati da un array di oggetti, come nel seguente esempio:
angular.module("myApp")
.factory("utentiService", function() {
var self = {};
self.utenti = [{ id:1, nome: "Andrea", cognome: "Rossi", citta: "Roma" },
{ id:2, nome: "Marco", cognome: "Verdi", citta: "Milano" },
{ id:3, nome: "Giovanni", cognome: "Neri", citta: "Napoli" },
{ id:4, nome: "Roberto", cognome: "Gialli", citta: "Palermo" }];
return self;
});
Riprendiamo quindi il codice per la configurazione del routing per completarlo con i relativi oggetti:
angular.module("myApp", ["ngRoute"])
.config(function($routeProvider) {
$routeProvider
.when("/utenti", {
templateUrl: "/templates/listaUtenti.html",
controller: "listaUtentiCtrl"
})
.when("/utenti/:userId", {
templateUrl: "/templates/utente.html",
controller: "utenteCtrl"
})
.otherwise({redirectTo: "/utenti"});
});
Una volta che abbiamo definito come vanno mappati gli URL del nostro routing occorre dire ad Angular dove dovranno essere visualizzate le viste che verranno caricate dinamicamente. A questo scopo utilizziamo la direttiva ng-view
:
<body ng-app="myApp">
<div ng-view></div>
</body>
Il codice HTML dei template verrà interpretato e visualizzato all'interno dell'elemento identificato dalla direttiva, gestendo automaticamente il suo contenuto in base all'interazione con l'utente.
Parametri e routing
La seconda "route" della nostra configurazione riguarda la vista del dettaglio di ciascun utente. Dal momento che intendiamo utilizzare un unico template per tutti gli utenti, abbiamo specificato un URL con una sintassi leggermente diversa da quello dell'elenco degli utenti: /utenti/:userId
.
I due punti prima del nome userId
indicano che il valore successivo rappresenta un parametro, cioè una parte variabile il cui valore verrà passato al controller che ha in carico la gestione della vista.
Nel nostro caso si tratta del controller utenteCtrl
, che potrà recuperare il valore dell'identificatore dell'utente sfruttando il servizio $routeParams
. Questo servizio consente l'accesso ai parametri passati nell'URL tramite proprietà con lo stesso nome. Quindi, ad esempio, per accedere al parametro userId
accederemo alla proprietà userId
, come mostrato di seguito:
angular.module("myApp")
.controller("utenteCtrl", function($scope, $routeParams) {
var userId = $routeParams.userId;
...
};
Possiamo sfruttare l'identificatore per recuperare l'utente selezionato ed assegnarlo allo scope corrente, in modo che i suoi dettagli vengano visualizzati sulla vista del singolo utente:
angular.module("myApp")
.controller("utenteCtrl", function($scope, $routeParams, utentiService, filterFilter) {
var userId = $routeParams.userId;
$scope.utente = filterFilter(utentiService.utenti, { id: userId })[0];
};
In questo modo abbiamo creato una semplice applicazione SPA che consente all'utente di navigare caricando dinamicamente le viste associate alle scelte dell'utente.