Navigazione in JSF 2.0

Introduzione

Le regole per la navigazione tra le pagine che compongono una user interface realizzata in JSF possono essere definiti in diversi modi. Nella versione 1 del framework il solo punto di configurazione era il file faces-config.xml, mentre con la versione 2 sono possibili diverse opzioni che descriveremo nel seguito.

Implicit Navigation

In JSF 2 è implementato un meccanismo automatico di risoluzione delle pagine chiamato implicit navigation (navigazione implicita). Esso consiste sostanzialmente nella possibilità di utilizzare i nomi delle view per definire le regole di navigazione, lasciando a JSF il compito di ricercare la view corretta all’interno dell’applicazione deployata. La navigazione implicita si applica nel momento in cui non esistono altre regole definite nel file di configurazione. Per utilizzare la navigazione implicita abbiamo due possibilità:

  1. inserire il nome della view direttamente nell’attributo action di un componente JSF;
  2. restituire il nome della view attraverso l’outcome di una managed bean.

Vediamo i due casi nell’esempio seguente di index.xhtml:

Nel primo commandButton l’action è valorizzato con la stringa page2. Quando il pulsante è premuto JSF risolverà la vista page2 con page2.xhtml e la ricercherà nella directory corrente. Analogamente accadrà alla selezione del secondo pulsante. Il metodo moveToPag2 del bean navigationController sarà eseguito e l’output interpretato come detto sopra.

Forward vs Redirect

Nell’esempio presentato quando ci siamo mossi dalla pagina index.xhtml verso la pagina page2.xhtml l’url mostrato nel browser è rimasto index.xhtml. Questo perché JSF 2 di default esegue un forward e non una redirect verso la nuova pagina, conseguentemente l’url del browser è sempre uno passo indietro rispetto anna navigazione. E’ importante comprendere la differenza tra questi due casi, in particolare per quanto riguarda il reload della pagina dal browser:

Forward:

  • Il forward è eseguito internamente dalla servlet;
  • Il browser non è cosciente del fatto che ci sia stato un forward e quindi l’url nella barra degli indirizzi rimane invariato;
  • Eseguendo un reload della pagina dal browser esso risottometterà la request originale dall’url originale.

Redirect:

  • Un redirect è un processo in due fasi, in cui l’applicazione web indica al browser di recuperare un secondo URL, che differisce dall’originale;
  • Un reload eseguito dal browser non ripeterà la request originale ma semplicemente richiederà al server la seconda url;
  • La redirect è leggermente più lenta rispetto al forward essendo un processo a due fasi;
  • Gli oggetti collocati nello scope originale non saranno disponibili nella seconda request.

Se vogliamo utilizzare la redirect piuttosto che il forward è sufficiente appendere la stringa faces-redirect=true agli outcome come nel seguente esempio:

File di Configurazione

L’opzione di configurare la navigazione nel file faces-config.xml è ovviamente ancora valida in JSF 2  ed è comunque utile per definire casi di navigazione di default. Iniziamo con analizzare un caso semplice ma utile di navigazione per la gestione del login utente. Inseriamo quindi il seguente codice html nella pagina index.xhtml:

Quindi implementiamo il bean loginController come segue:
Se non facciamo altro e clicchiamo sul pulsante di login JSF mostrerà il seguente messaggio:

in cui ci avvisa che non esiste regola di navigazione che partendo dalla vista index.xhtml ed applicando l’action loginController.login con output failure definisca la vista successiva. Per farlo inseriamo la navigation-rule nel file faces-config.xml come segue:

che fa esattamente quanto JSF ci ha chiesto nel messaggio ovvero a partire dalla vista (from-view-id) index.xhtml ed a seguito dell’applicazione dell’action (from-action) loginController.login definisce due percorsi di navigazione rispettivamente per l’outcome (from-outcome) success e failure verso le pagine (to-view-id) login-success.xhtml o login-failure.xhtml.

In generale un elemento navigation-rule può contenere zero o più elementi navigation-case. Il navigation-case definisce una serie di criteri di corrispondenza. Quando questi criteri sono soddisfatti, l’applicazione passare alla pagina definita dall’elemento to-view-id contenuto nello stesso elemento navigation-case.

Navigazione Condizionale

JSF 2 fornisce un meccanismo flessibile di navigazione condizionale che permette di implementare regole di navigazione molto complesse. Consideriamo come esempio il caso in cui vogliamo gestire la possibilità che la password del nostro abbia una validità limitata nel tempo.

Per gestire questa situazione abbiamo due possibilità. La prima consiste nell’utilizzare l’outcome del metodo login ad esempio prevedendo il risultato expired ed inserendo un navigation-case nel faces-config.xhtml che redireziona l’utente verso la pagina expired-password.xhtml. Ma questo non aggiunge nulla a quanto visto sopra.

La seconda possibilità consiste nell’inserire una condizione nel navigation-case nel modo seguente:

in cui le espressioni guidano il flusso di navigazione per password con età maggiore o uguale a 50 (ge sta per >=) verso la relativa pagina di gestione.

Vediamo un altro interessante caso di navigazione condizionale utilizzando le manaded properties. Supponiamo di avere il seguente frammento di codice html nel file index.xhtml:

Ai due commandLink sono è aggiunto il parametro pageId alla request inviata al managed bean navigationController così definito:

La proprietà pageId è annotata come ManagedProperty quindi JSF recupera il valore da iniettare dalla request.

Conclusioni

Le regole di navigazione definite negli esempi precedenti sono molto specifiche in quanto applicate a casi ben definiti. Nel seguito vediamo come sia possibile ottenere delle regole più generali attraverso pochi accorgimenti.

Innanzitutto se il tag from-view-id è omesso, la regola definita nello specifico navigation-rule è applicabile a tutte le pagine dell’applicazione. Il tag inoltre supporta l’utilizzo delle wildcard con le quali è possibile indicare un sottoinsieme di pagine per cui vale la regola. Nell’esempio seguente da qualunque pagina il logout comporta il ritorno alla pagina di login.

Anche gli elementi from-outcomefrom-action sono opzionali, consentendo di specificare, ad esempio, una navigazione di default per gli errori.

Codice Sorgente

Il codice sorgente degli esempi in questo post è scaricabile in jsf-navigation.