Introduzione
La serie introduttiva dovrebbe aver messo in evidenza quali siano gli elementi su cui è fondata l'architettura di JavaServer Faces. JSF è un framework orientato allo sviluppo di applicazioni web Java EE che indirizza principalmente la realizzazione del layer di interfaccia utente. Dal punto di vista dell'interazione dell'utente con l'interfaccia dell'applicazione, vengono mutuati nel mondo web concetti analoghi a quelli ben noti nell'ambito dello sviluppo di applicazioni stand-alone, tipo Swing. In JSF la gestione delle azioni che l'utente compie sull'interfaccia è gestita mediante un meccanismo a eventi. Le azioni eseguite dagli utenti sono rappresentate da Event Objects che mantengono l'informazione sul tipo di evento generato e sul componente che ha dato origine all'evento.
In JSF gli eventi rientrano in tre categorie, una delle quali in realtà non legata alle azioni utente sull'interfaccia ma piuttosto al ciclo di vita di JSF:
Action Events
Eventi scatenati dai componenti che generano comandi quali command button e hyperlink.
Value Change Events
Eventi scatenati dai componenti di tipo input ai quali mediante l'interfaccia si assegnano dei valori, quali text-box, radio button, checkbox etc.
Phase Events
Eventi associati non ad azioni dell'utente ma al compimento delle varie fasi del ciclo di vita di gestione di una richiesta JSF.
Tutti gli eventi generati da componenti dell'interfaccia utente sono rappresentati da oggetti i cui tipi estendono la classe base javax.faces.event.FacesEvent che a sua volta estende la classe java.util.EventObject.
Nella classe base sono molto importanti i metodi:
public UIComponent getComponent() public PhaseId getPhaseId() public void setPhaseId(PhaseId phaseId)
e il metodo:
public void proccessAction(ActionEvent evento)
Per registrare un listener tramite tag si usa:
<h:commandButton value="Invia" action="#{utente.login}">
<f:actionListener type="it.mokabyte.jsf.listeners.ButtonListener"/>
</h:commandButton>
Alternativamente si può usare un action listener method:
<h:commandButton value="Invia" actionListener="#{utente.ascolta}" action="#{utente.login}">
</h:commandButton>
dove il metodo:
public void ascolta(ActionEvent evento)
è inserito nel backing-bean della pagina come listener dell'action event e non una classe dedicata, ma il meccanismo è lo stesso. La differenza è che è possibile definire un solo action listener method mentre invece si possono associare più action listener class ad un componente.
Per i value change event si usano sintassi analoghe:
<h:inputText valueChangeListener="#{utente.cambia}" value="#{utente.username}" id="utente" />
oppure:
<h:inputText value="#{utente.username}" id="utente">
<f:valueChangeListener type="it.mokabyte.jsf.listeners.InputTextListener"/>
</h:inputText>
Come per gli action event anche in questo caso è possibile registrare un solo value-change listener method ma più di una value-change listener class. In genere è bene usare le classi listener sia per eventi action che value change quando il codice di gestione dell'evento è riutilizzabile per più di un componente in modo da ottimizzare il codice e consentirne il riuso, altrimenti le due modalità sono equivalenti.
I PhaseEvent
A differenza dei precedenti, i phase event non vengono generati a seguito di azioni dell'utente sull'interfaccia dell'applicazione, ma a fronte dell'esecuzione delle varie fasi del ciclo di vita di gestione delle richieste di JSF. Mentre con i precedenti eventi si ha a che fare sempre nello sviluppo di una applicazione JSF, con questi ultimi avviene più di rado anche se è bene conoscerli e sapere come utilizzarli.
I phase event sono rappresentati da istanze della classe javax.faces.event.PhaseEvent. Sono gestiti da classi listener che implementano l'interfaccia javax.faces.event.PhaseEventListener. Le istanze di queste classi listener vengono registrate per ascoltare gli eventi generati dalla classe che rappresenta il ciclo di vita di JSF, ovvero javax.faces.lifecycle.Lifecycle che mette a disposizione i metodi:
addPhaseListener(PhaseListener listener)per registrare un listenerremovePhaseListener(PhaseListener listener)per rimuovere un listenergetPhaseListeners()per acquisire un array contenente i riferimenti agli oggetti di tipo PhaseListener registrati
Un utilizzo dei PhaseListener può essere ad esempio quello di inizializzare un backing-bean con dei valori di default prima della phase di RenderResponse, per supplire alla carenza di JSF di non avere un metodo che consenta di effettuare questa operazione prima che la vista venga visualizzata.
Applicazione di esempio
Allo scopo di fornire anche dei semplici esempi pratici associati alla discussione teorica, alleghiamo delle applicazioni di esempio. All'applicazione di esempio presentata nella serie precedente, sarà aggiunto a ogni articolo un elemento riguardante l'argomento trattato. In questo caso abbiamo aggiunto una pagina JSP (output.jsp) nella quale la selezione di uno dei possibili valori in una list-box modifica il valore di una label della pagina. Per fare ciò è stato sviluppato un value change listener che sta in ascolto sul cambiamento di valore selezionato nella lista e modifica di conseguenza il valore della label.
Il codice dell'esempio è molto semplice. Vale la pena di sottolineare solo le seguenti particolarità:
Primo, solo i componenti di tipo command provocano l'invio del form di una pagina. Affinché ciò avvenga anche quando modifichiamo il valore della nostra list-box è necessario associare questa operazione all'evento onchange del componente:
<h:selectOneListbox id="select1" size="1" onchange="submit()"> <f:selectItem itemLabel="Valore 1" itemValue="1"/> <f:selectItem itemLabel="Valore 2" itemValue="2"/> <f:selectItem itemLabel="Valore 3" itemValue="3"/> <f:valueChangeListener type="it.mokabyte.jsf.listeners.SelectListener"/> </h:selectOneListbox>
Nel listener è possibile accedere ai componenti della pagina tramite:
UIViewRoot root = FacesContext.getCurrentInstance().getViewRoot();
HtmlOutputText outputText = (HtmlOutputText)root.findComponent("form1:outputText1");
