IceFaces è un framework per lo sviluppo di applicazioni RIA(Rich Internet Application), basato su JavaServer Faces 2. IceFaces 3 estende JSF ampliando il set di componenti a disposizione e migliorando quindi le possibilità di sviluppo.
Attraverso questa libreria possiamo realizzare complesse interfacce grafiche per le nostre applicazioni web basate su JSF. In questo articolo e nei prossimi vogliamo dare un'idea delle potenzialità del framework, introducendo l'uso dei componenti più interessanti.
Configurazione
Prima di iniziare è opportuno avere un'ambiente configurato per mettere in pratica quanto illustrato. Il codice che troverete in allegato è relativo ad un progetto web J2EE su ambiente Eclipse, ma potete tranquillamente utilizzare i sorgenti in un progetto Netbeans o altri ide che consentono di sviluppare applicazioni JSF 2.
Tutto quello che dovete fare è creare un progetto JSF e successivamente inserire in WEB-INF / lib i seguenti jar ottenuti dal sito di icefaces (http://www.icesoft.org/downloads/icefaces-downloads.jsf) dopo essersi registrati:
- commons-beanutils.jar
- commons-collections.jar
- commons-digester.jar
- commons-logging.jar
- icefaces.jar
- icefaces-ace.jar
- icefaces-compat.jar
- icepush.jar
ed aggiungere nel web.xml la configurazione della servlet di IceFaces:
<servlet>
<servlet-name>Resource Servlet</servlet-name>
<servlet-class>com.icesoft.faces.webapp.CompatResourceServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>/icefaces/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>Resource Servlet</servlet-name>
<url-pattern>/xmlhttp/*</url-pattern>
</servlet-mapping>
Una volta pronto il nostro progetto JSF vuoto, ma configurato per l'utilizzo di IceFaces, possiamo iniziare a prendere confidenza con il ricco set di componenti. IceFaces divide i componenti in 3 categorie:
- Ice Components
- Ace Components
- Enterprise Components
Ice Components
Gli Ice Components sono i componenti originariamente sviluppati per la versione IceFaces 1.8. Tra essi troviamo gli utili:
- selectInputDate: un calendario grafico per la selezione delle date
- inputRichText: un avanzato editor tipicamente presente nei Cms
- dataTable e dataPaginator: per la visualizzazione dei dati utilizzando eventualmente la tecnica lazy
- selectInputText : una casella di input potenziata
- panelTabSet : un tab component per separare ed organizzare contenuti
- panelToolTip : mostra una finestra informativa quando passiamo con il mouse su qualcosa
- outputMedia: per riprodurre video in vari formati
e tanti altri, abbiamo elencato i precedenti perchè scelti nella trattazione di questo articolo ma potete davvero trovare tanta documentazione e tutorials sul sito ufficiale di IceFaces.
SelectInputDate e Calendar
Calendar
Il componente calendario ci consente di visualizzare e gestire date in un modo veloce ed elegante senza preoccuparci di operazioni come la formattazione della data. Possiamo avere una visualizzazione "inline" (sempre visibile) oppure a comparsa con un click. E' possibile inoltre personalizzare la modalità di visualizzazione della data.
IceFaces Calendar
Realizziamo un template di pagina xhtml che ci aiuterà, insieme a dei manage bean, nella visualizzazione e prova dei vari componenti:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ace="http://www.icefaces.org/icefaces/components"
xmlns:ice="http://www.icesoft.com/icefaces/component"
xmlns:icecore="http://www.icefaces.org/icefaces/core">
<h:head>
<ice:outputStyle href="./xmlhttp/css/rime/rime.css" rel="stylesheet" type="text/css" />
<title><ui:insert name="title">IceFaces</ui:insert></title>
</h:head>
<h:body>
</h:body>
</html>
Possiamo notare la dichiarazione per l'uso dei componenti IceFaces all'interno del tag html, la specifica di uno stile di visualizzazione per essi con il componente outputStyle, ed un body vuoto nel quale inseriremo di volta in volta il frammento di pagina per il componente che vogliamo visualizzare. Procediamo e vediamo finalmente come mostrare un calendario e interagire con esso. Utilizziamo il template ed inseriamo all'interno del tag body il componente selectInputDate:
<h:form>
<ice:selectInputDate></ice:selectInputDate>
</h:form>
salviamo una nuova pagina con il nome calendar.xhtml a livello root della web app.Il calendario deve essere sempre presente all'interno di un tag form altrimenti non può funzionare. Con il frammento appena visto, se invochiamo la visualizzazione della pagina con un url in locale simile al seguente http://localhost:8080/IceFaces/calendar.xhtml, dovremmo poter visualizzare il calendario con la grafica mostrata precedentemente. IceFaces è il nome dato alla web app di demo, potete utilizzarne uno qualsiasi. Adesso per poter interagire, abbiamo bisogno di un managed bean. Creiamone uno con scope request e diamo come nome di classe,ad esempio, CalendarManagedBean. Per procedere con ordine, possiamo creare un package it.html.icefaces all'interno del quale inseriremo i vari managed bean che andremo a creare.
package it.html.icefaces;
import java.util.Date;
import javax.faces.event.ActionEvent;
public class CalendarManagedBean {
private Date date;
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
public void process(ActionEvent event){
System.out.println(date);
}
}
SelectInputDate
Modifichiamo quindi il tag in modo tale da collegare al campo date del managed bean il selectInputDate. Aggiungiamo inoltre un attributo per formattare la data (popupDateFormat) ed un campo di testo per visualizzarla dopo il submit ( outputText ):
<h:form>
<h:outputText id="display" value="#{calendarManagedBean.date}">
<f:convertDateTime pattern="dd/MM/yyyy"/>
</h:outputText>
<ice:selectInputDate value="#{calendarManagedBean.date}"
popupDateFormat="dd/MM/yyyy"/>
<h:commandButton value="Submit"
actionListener="#{calendarManagedBean.process}"></h:commandButton>
</h:form>
Dove calendarManagedBean è il nome dato al managed bean in fase di creazione. Se invece preferite una visuale a popup del tipo:
Selezione della data
Utilizzando l'attributo renderAsPopup con valore true otteniamo proprio questo tipo di visuale:
<ice:selectInputDate value="#{calendarManagedBean.date}" renderAsPopup="true" readonly="true" />
Mentre attraverso l'attributo readonly, possiamo impedire (true) o consentire(false) l'editing diretto dell'utente nell'area testuale.
InputRichText
Se avete utilizzato un qualche tipo di Cms, avrete sicuramente incontrato questo tipo di editor:
Rich Editor con JSF2 Icefaces
Ci consente di editare contenuti html, inserendo testo,immagini,video... . Usarlo è davvero molto semplice. Riprendiamo sempre il nostro template di pagina ed inseriamo nel tag body:
<h:form>
<ice:inputRichText value=""
language="it" skin="silver"
toolbar="Default" height="450"/>
<h:commandButton value="Submit" />
</h:form>
Salviamo la nuova pagina con nome test.xhtml e visualizziamola sul browser ottenendo il risultato mostrato in figura. Anche l'interazione con il componente è immediata, come fatto precedentemente, definiamo un managed bean con scope request e nome classe TextManagedBean, nel package che abbiamo dedicato ai managed bean:
package it.html.icefaces;
import javax.faces.event.ActionEvent;
public class TextManagedBean {
private String text;
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public void process(ActionEvent event){
System.out.println(text);
}
}
e modifichiamo l'attributo value del componente inputRichText come segue aggiungendo anche un campo di testo per visualizzare il contenuto html inviato con il submit
:
<h:form>
<ice:inputRichText value="#{textManagedBean.text}" language="en" skin="silver"
toolbar="Default" height="450"/>
<h:commandButton value="Submit"
actionListener="#{textManagedBean.process}"/>
<h:outputText value="#{textManagedBean.text}"/>
</h:form>
visualizzate nuovamente la pagina, scrivete qualcosa all'interno dell'editor, ed eseguite il Submit. Noterete la stampa su console di una porzione di HTML, quella da voi editata, oltre che la visualizzazione dell'html inserito sotto il pulsante di Submit.
Nella prossima parte mostreremo esempi tabelle con paginazione e pannelli
DataTable e dataPaginator
Per realizzare la visualizzazione dei record di un database attraverso una semplice tabella paginabile, per consultare i dati un po' alla volta, è possibile utilizzare dataTable e dataPaginator.
Aspetto inoltre particolarmente interessante è la gestione lazy: se i dati da visualizzare sono tanti, non è efficiente caricarli tutti in memoria, ed idealmente sarebbe più efficiente poter via via caricare solo la pagina che stiamo visualizzando. A tale scopo il dataTable fornisce il supporto al lazy loading: solo la pagina che stiamo visualizzando in quel momento sarà effettivamente presente in memoria lato server.
Iniziamo questa volta dalla creazione del managed bean: vogliamo definire, per un esempio rapido e pratico, una lista al suo interno per visulizzarla con il dataTable e scorrere i record con il dataPaginator.
Supponiamo di voler mostrare una lista per l'acquisto di biglietti ferroviari. Definiamo una classe bean per rappresentare il singolo viaggio in treno all'interno del package it.html.icefaces
:
public class Train {
private int number;
private String from;
private String to;
private String price;
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
public String getFrom() {
return from;
}
public void setFrom(String from) {
this.from = from;
}
public String getTo() {
return to;
}
public void setTo(String to) {
this.to = to;
}
public String getPrice() {
return price;
}
public void setPrice(String price) {
this.price = price;
}
}
e quindi il managed bean che ne conserva una lista per la demo:
package it.html.icefaces;
import java.util.ArrayList;
import java.util.List;
public class DataTableManagedBean {
private List<Train> trains= new ArrayList<Train>();
public DataTableManagedBean(){
Train train = new Train();
train.setFrom("Rome");
train.setTo("Milan");
train.setNumber(1234);
train.setPrice("79 euro");
trains.add(train);
// aggiunta degli altri treni...
}
public List<Train> getTrains() {
return trains;
}
public void setTrains(List<Train> trains) {
this.trains = trains;
}
}
Siamo pronti per visualizzare la lista con la dataTable, inseriamo nella nostra pagina di template all'interno del tag form il seguente codice:
<ice:dataTable id="trainTable"
value="#{dataTableManagedBean.trains}"
var="train"
resizable="true"
rows="5"
width="60%" cellspacing="1" cellpadding="5">
<ice:column>
<f:facet name="header">Number</f:facet>
#{train.number}
</ice:column>
<ice:column>
<f:facet name="header">From</f:facet>
#{train.from}
</ice:column>
<ice:column>
<f:facet name="header">To</f:facet>
#{train.to}
</ice:column>
<ice:column>
<f:facet name="header">Price</f:facet>
#{train.price}
</ice:column>
</ice:dataTable>
La lista è collegata attraverso l'attributo value
, mentre var
è l'attributo che identifica la varibile di iterazione che ad ogni step contiene l'oggetto corrente.
Con rows
invece impostiamo il numero di elementi per pagina. Come possiamo intuire guardando il codice, si ragiona per colonne, abbiamo una colonna il cui nome è definito con il tag facet
, ed il cui valore è associato attraverso la variabile di iterazione train
, accedendo al nome del campo della classe che vogliamo mostrare in corrispondenza di questa colonna.
Se visualizziamo la pagina invoncandola da browser, noteremo la tabella priva però di paginatore, dobbiamo infatti collegare un paginatore alla dataTable per poter realmente le scorrere le pagine. Inseriamo subito dopo il seguente codice:
<ice:dataPaginator id="paginator"
for="trainTable"
paginator="true">
<f:facet name="first">
<h:graphicImage value="/xmlhttp/css/rime/css-images/arrow-first.gif"
style="width: 18px; height: 18px; border: none;"
alt="First" title="First"/>
</f:facet>
<f:facet name="last">
<h:graphicImage value="/xmlhttp/css/rime/css-images/arrow-last.gif"
style="width: 18px; height: 18px; border: none;"
alt="Last" title="Last"/>
</f:facet>
<f:facet name="previous">
<h:graphicImage value="/xmlhttp/css/rime/css-images/arrow-previous.gif"
style="width: 18px; height: 18px; border: none;"
alt="Previous" title="Previous"/>
</f:facet>
<f:facet name="next">
<h:graphicImage value="/xmlhttp/css/rime/css-images/arrow-next.gif"
style="width: 18px; height: 18px; border: none;"
alt="Next" title="Next"/>
</f:facet>
<f:facet name="fastforward">
<h:graphicImage value="/xmlhttp/css/rime/css-images/arrow-ff.gif"
style="width: 18px; height: 18px; border: none;"
alt="Fast Forward" title="Fast Forward"/>
</f:facet>
<f:facet name="fastrewind">
<h:graphicImage value="/xmlhttp/css/rime/css-images/arrow-fr.gif"
style="width: 18px; height: 18px; border: none;"
alt="Fast Rewind" title="Fast Rewind"/>
</f:facet>
</ice:dataPaginator>
Ad un primo impatto potrebbe impressionare, ma guardando bene il codice capiamo che c'e' in realtà molta decorazione per la varie funzioni che un sistema di paginazione offre, in particolare vediamo come attraverso il graphicImage
possiamo associare icone richiamandole da un tema proprio di IceFaces.
L'associazione con la dataTable avviene attraverso l'attributo for che punta l'id che abbiamo definito precedentemente per la dataTable. Successivamente con i tag facet
definiamo i controlli che intendiamo visualizzare. A questo punto possiamo vedere finalmente la dataTable che mostra i nostri dati:
SelectInputText
Conosciamo tutti le caselle di input che forniscono suggerimenti di autocompletamento: con la selectInputText possiamo realizzare principalmente questo tipo di funzionalità.
Ispirandoci agli esempi standard proposti per IceFaces, vogliamo digitare all'interno di una area di testo per la scelta di una città, e vedere comparire un suggerimento o lista di suggerimenti che forniscono scelte di completamento:
Cominciamo anche in questo caso dal managed bean da collegare al componente:
package it.html.icefaces;
import java.util.ArrayList;
import java.util.List;
import javax.faces.event.ActionEvent;
import javax.faces.model.SelectItem;
import com.icesoft.faces.component.selectinputtext.TextChangeEvent;
public class InputTextManagedBean {
private List<SelectItem> availableCities = new ArrayList<SelectItem>();
private List<SelectItem> selectedCities = new ArrayList<SelectItem>();
private String selectedText = null;
private String selectedCity;
private int results = 0;
public InputTextManagedBean(){
SelectItem item = new SelectItem("Rome","Rome");
availableCities.add(item);
item = new SelectItem("Milan","Milan");
availableCities.add(item);
item = new SelectItem("Turin","Turin");
availableCities.add(item);
item = new SelectItem("Naple","Naple");
availableCities.add(item);
item = new SelectItem("Paris","Paris");
availableCities.add(item);
}
public List<SelectItem> getAvailableCities() { return availableCities; }
public String getSelectedText() { return selectedText; }
public String getSelectedCity() { return selectedCity; }
public int getResults() { return results; }
public void setAvailableCities(List<SelectItem> availableCities) { this.availableCities = availableCities; }
public void setSelectedText(String selectedText) { this.selectedText = selectedText; }
public void setSelectedCity(String selectedCity) { this.selectedCity = selectedCity; }
public void setResults(int results) { this.results = results; }
public void textChanged(TextChangeEvent event) {
availableCities = wrapList(event.getNewValue().toString());
results = availableCities.size();
}
public void submitText(ActionEvent event) {
setSelectedCity(getSelectedText());
availableCities = null;
results = 0;
}
private List<SelectItem> wrapList(String value){
selectedCities.clear();
for(SelectItem item : availableCities){
if(item.getValue().toString().startsWith(value)){
this.selectedCities.add(item);
}
}
return selectedCities;
}
}
...dove availableCities
è la lista di tutte le possibili scelte, selectedCities
invece quella costruita durante la digitazione in base al testo selezionato.
In sostanza in quest'ultima lista, che rappresenta quella dei suggerimenti visualizzati, andremo ad inserire tutti gli elementi di availableCities
che soddisfano un determinato criterio definito all'interno del metodo wrapList()
: mentre digitiamo verrano ad esempio visualizzate tutte le città che iniziano con il testo che stiamo digitando.
Analogamente il campo selectedText
rappresenta il testo corrente mentre selectedCity
la scelta completa finale.
Il filtro sarà attivato da un evento change generato sul campo di input: questa chiamata è gestita dal metodo textChanged()
, che invocando wrapList()
sul testo inserito dall'utente, realizza la visualizzazione della lista di suggerimenti.
Spostiamoci adesso sulla pagina xhtml che utilizza dal lato presentazionale il componente ed il managed bean appena visto. All'interno del tag form
inseriamo il seguente codice:
<h:panelGrid columns="4">
<h:outputLabel value="Submitted City:"/>
<h:outputText value="#{inputTextManagedBean.selectedCity}"/>
<h:outputText value=""/><h:outputText value=""/>
<h:outputLabel value="Current Text:"/>
<h:outputText value="#{inputTextManagedBean.selectedText}"/>
<h:outputText value=""/><h:outputText value=""/>
<h:outputLabel value="Current Results:"/>
<h:outputText value="#{inputTextManagedBean.results}"/>
<h:outputText value=""/><h:outputText value=""/>
<h:outputLabel value="Choose a City:"/>
<ice:selectInputText id="autoIn"
value="#{inputTextManagedBean.selectedText}"
textChangeListener="#{inputTextManagedBean.textChanged}"
actionListener="#{inputTextManagedBean.submitText}"
rows="10" width="300">
<f:selectItems value="#{inputTextManagedBean.availableCities}"/>
</ice:selectInputText>
<h:commandButton value="Submit" actionListener="#{inputTextManagedBean.submitText}"/>
<h:message for="autoIn"/>
</h:panelGrid>
Leggendo il codice è molto intutivo associare ciascun attributo dei vari tag ai campi del managed bean, comprendendo il funzionamento globale. Non vi resta che invocare la pagina da browser e fare le vostre sperimentazioni con questo utile componente.
PanelTabSet
Il panelTabSet è un componente per organizzare i contenuti con classico layout a tab:
per vederla in azione utilizziamo il template come fatto finora per realizzare una nuova pagina ed inseriamo nel tag form
il seguente frammento:
<ice:panelTabSet width="500">
<ice:panelTab label="Tab 1">
<h:outputText value="Contents for tab1" />
</ice:panelTab>
<ice:panelTab label="Tab 2">
<h:outputText value="Contents for tab2"/>
</ice:panelTab>
<ice:panelTab label="Tab 3">
<h:outputText value="Contents for tab3"/>
</ice:panelTab>
</ice:panelTabSet>
PanelToolTip
Il tool tip è molto utile quando vogliamo informare l'utente al passaggio del mouse, ad esempio su qualche parola il cui significato necessita di ulteriori spiegazioni:
Realizziamo un pagina per il suo test ed inseriamo il codice:
<h:panelGroup style="width:200px">
<p>
<ice:panelGroup panelTooltip="icefaces"
styleClass="tooltipWrap">ICEfaces 2</ice:panelGroup>
is an open-source
Rich Internet Application (RIA) development
framework based on the JavaServer Faces (JSF) 2 standard. Like its
predecessor,ICEfaces 1.8, extends JSF to simplify development and enhance
the standard JSF feature set - simultaneously improving developer efficiency
and expanding the spectrum of RIA capabilities that can be included in any
JSF-based web application.
</p>
</h:panelGroup>
<ice:panelTooltip id="icefaces"
style="width: 200px;"
displayOn="hover"
hoverDelay="100">
<f:facet name="header">
ICEfaces
</f:facet>
<f:facet name="body">
ICEfaces 2 provides some key feature enhancements over JSF 2, while inheriting all the new features available in JSF 2.
</f:facet>
</ice:panelTooltip>
Possiamo notare come il panelToolTip venga collegato al testo, sul quale vogliamo che si attivi, attraverso un panelGroup
attraverso l'attributo panelToolTip
. All'interno del componente toolTip specifichiamo l'evento di attivazione attraverso l'attributo displayOn
(passaggio del mouse nel nostro caso) e il tempo di ritardo per la sua comparsa (hoverDelay
).
Visualizzando la pagina vedremo come al passaggio con il mouse sulla scritta IceFaces 2, il tooltip si attivi mostrando il suo contenuto.
OutputMedia
L'ultimo componente di cui vogliamo fare cenno in questa breve panoramica è OutputMedia, che ci permette di eseguire filmati Flash, Quicktime, WMV, Real Player all'interno del browser.
I controlli del player, così come stile e dimensioni possono essere personalizzati; l'uso è abbastanza semplice ed immediato.
Vediamo come riprodurre il filmato flash di IceFaces. Riprendiamo il solito template ed inseriamo all'interno del tag form il seguente codice:
<ice:outputMedia id="outputMedia"
player="flash" source="ICEfaces_Flash.swf"
style="width:300px; height:250px;">
<f:param name="play" value="true"/>
<f:param name="menu" value="true"/>
</ice:outputMedia>
dove con l'attributo player
definiamo il tipo di player che intendiamo usare, mentre con source il path della locazione del file da riprodurre.
Conclusioni
In questo articolo abbiamo mosso i primi passi con IceFaces all'interno di acluni dei principali componenti standard: con essi possiamo già realizzare applicazioni JSF2 in modo maggiormente veloce e graficamente più interessante.
Il sito di IceFaces è davvero ben documentato e ricco di esempi ed il lettore interessato potrà trovare tutto il materiale di cui ha bisogno per approfondimenti e maggiori dettagli. Con il prossimo articolo ci concentreremo invece sulla famiglia degli ace components.