I componenti ACE (ICEFaces Advanced Components) sono i componenti di ultima generazione di ICEFaces: oltre 40 componenti che utilizzano tecniche di rendering sia lato server che lato client, per fornire un'esperienza un'ottima esperienza utente.
Gli aspetti chiave possono essere sintetizzati in 3 punti:
- Potenti componenti javascript (ad esempio jQuery), che sollevano lo sviluppatore dal loro utilizzo diretto.
- Vasto supporto alle funzionalità client-side che migliorano i tempi di risposta e le potenzialità dei componenti.
- Flessibile approccio alla creazione di temi grazie all'uso di jQuery e dei ThemeRoller themes.
In questo articolo continuiamo l'esplorazione inziata con l'articolo introduttivo sui componenti ICEFaces, esaminando stavolta alcuni componenti ACE, mantenendo un approccio pratico e semplice.
Il componente Accordion
L'accordion è un componente particolarmente utile per la costruzione di menu strutturati in pannelli che si aprono e chiudono mostrando contenuti
Per l'illustrazione di questo e di altri componenti, riprendiamo il nostro progetto ed il template di pagina del primo articolo. Costruiamo quindi una pagina accordion.xhtml ed inseriamo all'interno del tag form il codice per l'uso di esempio di questo componente:
<ace:accordion id="accordion" collapsible="true" autoHeight="false">
<ace:accordionPane title="Title 1">
<h:outputText value="item 1"/>
<h:outputText value="item 2"/>
</ace:accordionPane>
<ace:accordionPane title="Title 2">
<h:outputText value="item 1"/>
<h:outputText value="item 2"/>
</ace:accordionPane>
<ace:accordionPane title="Title 3">
<h:outputText value="item 1"/>
<h:outputText value="item 2"/>
</ace:accordionPane>
</ace:accordion>
Se eseguiamo la pagina possiamo visualizzare il componente in azione:
Come possiamo notare dal codice, il tag <ace:accordionPane/> ci permette di definire voci di primo livello, mentre suo interno possiamo inserire diverse tipologie di componenti: un link, un'immagine , una tabella, etc. Il componente ha diversi attributi da esplorare, provate ad esempio ad aggiungere in <ace:accordion ../> l'attributo effect con valore bounceslide, noterete un diverso comportamento grafico dell'accordion all'apertura delle voci di menù.
Il componente animation
ICEFaces fornisce un sistema di animazioni collegabili a molti componenti ACE. L'esempio che segue è tratto dallo showcase di ICEFaces e mostra tutti i possibili effetti applicabili.
L'esempio mostra diverse lettere, cliccando su ciascuna di esse possiamo scatenare diversi effetti grafici di animazione:
Per realizzarlo all’interno del nostro progetto, creiamo una pagina animation.xhtml
usando il template ed inseriamo all'interno del tag form:
<h:panelGrid columns="5" width="100">
<h:panelGrid>
<h:panelGrid>
<h:outputText value="Blind Effect"/>
<h:graphicImage id="blind" value="../resources/images/a-blue_small.png" alt="a-blue"/>
<ace:animation event="click" name="blind"/>
<ace:animation event="click" name="highlight"/>
</h:panelGrid>
</h:panelGrid>
<h:panelGrid>
<h:panelGrid>
<h:outputText value="Fade Effect"/>
<h:graphicImage id="fade" value="../resources/images/m-blue_small.png" alt="m-blue"/>
<ace:animation event="click" name="fade" />
<ace:animation event="click" name="highlight"/>
</h:panelGrid>
</h:panelGrid>
[...]
<h:panelGrid>
<h:panelGrid>
<h:outputText value="Highlight Effect"/>
<h:graphicImage id="highlight" value="../resources/images/o-black_small.png" alt="o-black"/>
<ace:animation event="click" name="highlight"/>
</h:panelGrid>
</h:panelGrid>
[...]
</h:panelGrid>
Guardando il codice è facile capire come funzioni il componente animation
. Lo inseriamo all'interno del tag interessato, specifichiamo l'evento che fa scattare l'animazione (attributo event
), nel nostro caso il click, ed infine definiamo con l'attributo name
il tipo di animazione.
Il componente file Entry : File upload manager
Avere a disposizione un componente in grado di implementare facilmente le funzionalità di upload può essere molto utile: il File Entry
ha numerosi attributi con i quali possiamo definire, ad esempio, percorso relativo alla web app o assoluto al file system di salvataggio del file, limitazioni sul numero di file in upload contemporaneo, dimensioni e cosi via…
L’esempio che segue è tratto dalle demo di ICEFaces, ma semplificato per l’uso all’interno del nostro progetto.
Realizziamo un semplice upload manager che in due step ci consente di inviare in upload un file. Vediamo subito l’immagine di ciò che andremo a realizzare:
Questa volta abbiamo bisogno di un managed bean
da collegare al componente per gestire il processo di ricezione del file. Realizziamo una classe FileEntryManagedBean
nel package it.html.icefaces
del progetto, inseriamo quindi la definizione nel faces-config.xml
:
<managed-bean>
<managed-bean-name>fileEntryManagedBean</managed-bean-name>
<managed-bean-class>it.html.icefaces.FileEntryManagedBean</managed-bean-class>
<managed-bean-scope>view</managed-bean-scope>
</managed-bean>
Vogliamo gestire la lista dei file in upload attraverso una dataTable
, definiamo quindi un campo per questa lista:
private List<String> fileData;
implementiamo il classico metodo get
per questo campo.
Passiamo adesso al cuore della classe, la realizzazione del metodo di listener che gestisce il processo di upload. Questo metodo viene collegato al fileEntry attraverso l'attributo fileEntryListener
e la sua definizione è la seguente:
public void sampleListener(FileEntryEvent e)
dove FileEntryEvent
è una classe ICEFaces per la gestione dell'evento relativo a questo componente.
Vediamo invece adesso il codice da inserire all'interno di questo metodo: come prima operazione si recupera la lista dei file in upload:
FileEntry fe = (FileEntry)e.getComponent();
FileEntryResults results = fe.getResults();
File parent = null;
Si valorizza la lista per la dataTable
fileData = new ArrayList();
Si itera la lista dei file ottenuti dall'upload valorizzando anche la lista associata alla dataTable costruendo cosi un report finale di upload:
for (FileEntryResults.FileInfo i : results.getFiles()) {
fileData.add("File Name: " + i.getFileName());
if (i.isSaved()) {
fileData.add("File Size: " + i.getSize() + " bytes");
File file = i.getFile();
if (file != null) {
parent = file.getParentFile();
}
} else {
fileData.add("File was not saved because: " + i.getStatus().getFacesMessage(FacesContext.getCurrentInstance(), fe, i).getSummary());
}
}
if (parent != null) {
long dirSize = 0;
int fileCount = 0;
for (File file : parent.listFiles()) {
fileCount++;
dirSize += file.length();
}
fileData.add("Total Files In Upload Directory: " + fileCount);
fileData.add("Total Size of Files In Directory: " + dirSize + " bytes");
}
Il codice è relativamente semplice, ed è facile capire quanto lavoro faccia per noi il componente.
Abbiamo completato la parte logica del nostro mini sistema, realizziamo adesso la parte grafica partendo dal solito template all’interno del quale andiamo ad inserire il codice che segue, sempre all’interno del tag form:
<h:panelGrid>
<h:outputText value="File Upload Manager" />
</h:panelGrid>
<ace:panel style="font-size:11px;width:650px">
<h:panelGrid columns="3" >
<h:panelGrid>
<h:panelGrid ><h:outputText value="Step 1" /></h:panelGrid>
<ace:panel style="width: 250px; height: 40px; border: 0px;">
<ace:fileEntry id="File-uploader"
relativePath="/files/" maxFileCount="1"
maxFileCountMessage="Limited to 1 files uploaded concurrantly."
fileEntryListener="#{fileEntryManagedBean.sampleListener}"
maxFileSize="6291456" maxFileSizeMessage="Submitted file is too large."
maxTotalSize="18874368" maxTotalSizeMessage="Total size of submitted files is too large."
required="true" requiredMessage="The file is required to submit this form."
useOriginalFilename="true" useSessionSubdir="true" />
</ace:panel>
</h:panelGrid>
<h:panelGrid width="100%" >
<h:graphicImage value="/resources/images/navigateForward.png" />
</h:panelGrid>
<h:panelGrid width="100%">
<h:panelGrid><h:outputText value="Step 2"/></h:panelGrid>
<ace:panel style="width: 100px; height: 30px; border: 0px;">
<h:commandButton id="submit" type="submit" value="Send File"/>
</ace:panel>
</h:panelGrid>
</h:panelGrid>
<br/>
<hr/>
<br/>
<h:dataTable id="tbl" value="#{fileEntryManagedBean.fileData}" var="fileDataVal">
<h:column>
<f:facet name="header"><h:outputText value="Upload results"/></f:facet>
<h:outputText id="fileData" value="#{fileDataVal}"/>
</h:column>
</h:dataTable>
<h:messages id="msgs" layout="table" for="File-Uploader"/>
</ace:panel>
Nel tag del componente ace:fileEntry
notiamo numerosi attributi tra cui:
il collegamento al nostro listener di gestione
fileEntryListener="#{fileEntryManagedBean.sampleListener}"il path di salvataggio del file
relativePath=”/files/”in questo caso si è specificato un path relativo alla root della web application ma esiste anche l'attributoabsolutePath
per specificare un percorso del file systemattributi per dimensione e quantità dei file
Gli attributi che iniziano per max consento di definire limitazioni su numero e dimensioni di file:
useOriginalFilename
imposta o meno l'uso del nome del file nel salvataggio,useSessionSubdir
invece ci fornisce la possibilità di salvare i file in sottocartelle con il nome ricavato dalla sessione dell'’utente.I componenti drag & drop
Concludiamo il nostro viaggio negli ACE components con i componenti
ace:draggable
edace:droppable
ed il loro uso con ladataTable
.Prendendo spunto dall'esempio dello showcase realizziamo un carello della spesa con tecnica drag & drop: selezioniamo gli elementi che intendiamo acquistare trascinandoli e rilasciandoli poi in una determinata area. Vediamo subito immagini relative al funzionamento:
Per questo esempio abbiamo bisogno di una classe (nel package
it.html.icefaces
) per gli item davvero molto semplice di cui mostriamo immediatamente il codice:
public class Item { private String picture; private String description; private String type; private int price; . . . . // get and set methods }
Dobbiamo inoltre definire un
managed bean
:
public class DataTableDragDropManagedBean { private List<Item> availableItems; private List<Item> purchasedItems; @PostConstruct private void initializeData() { availableItems = new ArrayList<Item>(); int price=10; Item item = new Item(); item.setType("electronic device"); item.setDescription("Laptop"); item.setPrice(price); item.setPicture("/resources/images/laptop.png"); price+=10; availableItems.add(item); // [...] purchasedItems = new ArrayList<Item>(); } public void handleDrop(DragDropEvent e){ Item item = (Item)e.getData(); purchasedItems.add(item); availableItems.remove(item); } // get/set AvailableItems // get/set PurchasedItems }
La classe ha come obiettivo quello di gestire le liste di elementi disponibili (
availableItems
) e di quelli che si intendono acquistare (purchasedItems
).
Con il metodoinitializeData()
inizializziamo la lista di elementi disponibili con items di esempio per la nostra demo.
Il metodo
public void handleDrop(DragDropEvent e)
è ciò che ci consente di gestire l'evento di rilascio dell'elemento trascinato nell'area sensibile al drop.
In questo metodo ricaviamo l'elemento selezionato, lo aggiungiamo alla lista di quelli acquistati, e lo rimuoviamo da quelli disponibili. Per quanto riguarda il codice il nostro lavoro è completato: proseguiamo quindi realizzando l'interfaccia vista in precedenza.
Come è ormai nostra abitudine, riprendiamo la nostra pagina template ed inseriamo all'interno del tag form il seguente codice ICEFaces:
<ace:dataTable id="shoppingList" value="#{dataTableDragDropManagedBean.availableItems}" var="availableItem" style="width:500px"> <ace:column headerText="Picture"> <h:graphicImage id="itemImg" value="#{availableItem.picture}"> <ace:draggable revert="true" stack=".imageStyle" opacity="0.7"/> </h:graphicImage> </ace:column> <ace:column headerText="Description"> <h:outputText id="description" value="#{availableItem.description}"/> </ace:column> <ace:column headerText="Type"> <h:outputText id="type" value="#{availableItem.type}"/> </ace:column> <ace:column headerText="Price"> <h:outputText id="price" value="#{availableItem.price}"/> </ace:column> </ace:dataTable>
In questa prima porzione è presente la
dataTable
degli elementi disponibili. È estremamente interessante notare come la funzionalità di drag venga realizzata semplicemente inserendo all'interno del componentegraphicImage
il componenteace:draggable
.
<ace:panel id="shoppingCart" header="Shopping Cart" style="display: block; font-size: 12pt;width:500px"> <h:outputText id="cartText" value="Shop with confedence ! To begin drag any item image above to the shopping cart." rendered="#{empty dataTableDragDropManagedBean.purchasedItems}"/> <ace:dataTable id="purchasedItems" value="#{dataTableDragDropManagedBean.purchasedItems}" var="item" rendered="#{not empty dataTableDragDropManagedBean.purchasedItems}"> <ace:column headerText="Description"> <h:outputText id="name" value="#{item.description}"/> </ace:column> <ace:column headerText="Type"> <h:outputText id="type" value="#{item.type}"/> </ace:column> <ace:column headerText="Price"> <h:outputText id="price" value="#{item.price}"/> </ace:column> </ace:dataTable> <ace:droppable tolerance="touch" datasource="shoppingList" dropListener="#{dataTableDragDropManagedBean.handleDrop}" activeStyleClass="slot"> <ace:ajax execute="@this" render="shoppingCart shoppingList" /> </ace:droppable> </ace:panel>
La lista degli elementi da acquistare è ancora una volta una
datasource
, che contiene però al suo interno il componenteace:droppable
collegato al listener di gestione.Dobbiamo inoltre notare come venga collegato alla
datasource
di partenza attraverso l'attributodatasource
. Si può inoltre evidenziare come venga fatto uso del componente ajax per il refresh delladatasource
di partenza e del panel che contiene la dataTable di destinazione.Infatti l'operazione drag & drop realizzata utilizza ajax, e se non utilizzassimo delle chiamate di render per aggiornare lo stato dei componenti, non vedremmo alcun risultato.
Conclusioni
Abbiamo visto alcuni interessanti componenti ACE e preso familiarità con essi realizzando alcuni semplici esempi.
Vi sono molti altri componenti da esplorare e testare ed il consiglio è sicuramente quello di farlo approfittando della buona documentazione a disposizione sul sito di ICEFaces: il prossimo passo è quello di mettere in pratica le nostre conoscenze per realizzare qualcosa di più vicino alle problematiche reali attraverso l'implementazione di una web application che faccia uso di ICEFaces.