Ciao a tutti.
La specifica JSR 286 ha indrodotto 2 modalità per lo scambio di informazioni tra portlet, noto come Inter-Portlet-Communication: uno basato sullo scambio di parametri (shared render parameters) e un modello ad eventi.
Il modello ad eventi è quello più interessante.
In breve, una portlet viene configurata per inviare un evento e una o più portlet vengono configurate per ricevere e consumare l'evento; la configurazione viene fatta nel file portlet.xml e un evento è un qualunque oggetto che sia serializable.
Supponiamo di avere una portlet che visualizza categorie ad albero nel modo classico: cliccando su una categoria appare il <div> che contiene le sotto categorie (stile portlet "Categories Navigation") e supponiamo di avereuna portlet che visualizza prodotti, filtrando la lettura dalla base dati tramite la categoria cliccata sull'altra (stile asset publisher).
Nel file portlet.xml,portlet che pubblica l’evento
<supported-publishing-event>
<name> catSelected </name>
</supported-publishing-event>
pe la portlet che processa l’evento
<supported-processing-event>
<name> catSelected </name>
</supported-processing-event>
Ogni portlet che riceve l'evento, implementa il metodo
@ProcessingEvent(name = “catSelected”)
public void processoCatSelected(EventRequest request, EventResponse response)
throws IOException, PortletException {
Event event = request.getEvent();
String catId = (String) event.getValue();
List <Prodotti> prodotti =readProdottiFromDb(catId);
request.getPortletSession().setAttribute(“prodotti”, prodotti);
}
L'evento viene consumato nel metodo e quindi è possibile, ad esempio, memorizzarlo nella portletSession per utilizzarlo successivamente in una pagina jsp. L'annotation serve per discriminare tra i metodi processAction che processano eventi differenti.
La portlet che invia l'evento, lo fa con l'istruzione seguente, posta in un metodo processAction
response.setEvent(“catSelected”, "123");
Viene quindi scambiato l'evento "selezionata categoria 123"
Tramite il "giro di giostra" descritto l'evento viene scambiato tra le portlet a livello server e questo implica che la pagina debba essere ricaricata interamente e che quindi dobbiamo memorizzare in qualche modo lo stato di visualizzazione delle portlet per poter ripresentare all'utente la schermata esattamente come l'ha lasciata: nel nostro caso devo ricordare quale è la categoria cliccata per poter mostrare l'albero delle categorie in modo corretto. Per portlet molto complesse questo può essere fastidioso.
Se solo si potesse utilizzare ajax...
Effettivamente possiamo fare la stessa cosa utilizzando una funzionalita di IPC messa a disposizione da LR 6.x nota come IPC client-side.
Si tratta delle funzioni javascriptLiferay.fire e Liferay.on, utilizzate rispettivamente per inviare e ricevere un evento in due portlet instanziate nella stessa pagina.
In Liferay 5.x si chiamavano Liferay.trigger e Liferay.bind; sono identiche alle rispettive attuali ma hanno cambiato nome per motivi a me oscuri.
La cosa interessante è che il tutto viene fatto a livello javascript senza dover configurare nulla nei files xml delle mie portlet e tutto il giro viene generato e consumato sulla pagina tra qualunque portlet.
Per inviare un evento:
...
jQuery(function () {
jQuery('a.categoria_item').click(
function(event) {
var catId = jQuery(this).next().val();
Liferay.fire('catSelected', {categoryId: catId}); return false;
}
)
}
);
...
<a href="#" class="categoria_item">mia categoria</a>
<input type="hidden" name="catId" value="123">
In questo esempio al click sul link viene inviato un evento "catSelected" che consiste in un JSON, perfetto in questo caso.
Nella portlet che processa l'evento:
<script type="text/javascript">
Liferay.on(
'catSelected',
function(event) {
var catId = event.categoryId;
jQuery('#prodottiWrapper').html('');
jQuery.ajax({
type: 'POST',
url: '<%=setCatAction%>',
data:"catId="+catId,
success: function(data){
jQuery('#prodottiWrapper').html(data);
}
});
return false;
}
);
</script>
In sostanza l'elaborazione dell'evento consiste nella chiamata ajax ad un action della mia portlet che legge dalla base dati i prodotti che appartengono alla categoria selezionata, mostrati nel <div id="prodottiWrapper"></div>.
Il risultato è che la user experience è molto più fluida, tutte le portlet che stanno intorno ignorano tutto questo dialogo e continuano a vivere felicemente nell'ignoranza, genero meno traffico di rete e infine non mi pongo il problema di memorizzare quale categoria è stata cliccata perchè il mio albero di categorie non viene ricaricato.
Hope it helps!
Marcello