Lavorare con le proprie entità custom integrate alle categorie di Liferay è, tutto sommato, abbastanza semplice ed indolore. Tuttavia mi sono trovato recentemente a dover affrontare un problema spinoso ma, fortunamente, abbastanza inconsueto.
La situazione è la seguente:
- ho definito 2 mie entità custom, che chiameremo
EntitaA
ed EntitaB
; - ho definito i relativi
AssetRender
in modo da potermi integrare con le categorie; - ho creato un vocabolario ed associato ad entrambe le entità custom.
All'interno della pagina JSP (per esigenze che non sto ad approfondire) ho dovuto inserire 2 distinti form HTML, uno per EntitaA
e l'altro EntitaB
; in entrambi ho utilizzato la taglib di Alloy per la gestione delle categorie, ossia:
<aui:input name="categories" type="assetCategories" />
Il problema è insito nel funzionamento interno di tale taglib, che si appoggia infatti sull'altra taglib di Liferay: <liferay-ui:asset-categories-selector />
. Questa taglib infatti crea un campo di testo nascosto per ogni vocabolario e gli assegna, sia come name che come id, la stringa: assetCategoryIds_<vocabularyId>
.
La valorizzazione del campo nascosto è demandata al codice di Alloy della taglib; il problema è proprio qui perchè i form HTML sono sì separati ma il mio vocabolario è associato ad entrambe le entità custom e quindi nella pagina ci sono 2 elementi nascosti con lo stesso nome e con lo stesso id.
Di conseguenza quando si seleziona una categoria per la seconda entità custom EntitaB
, Alloy valorizza il campo nascosto della prima; questo succede perchè in presenza di più campi con lo stesso id, Alloy prende il primo. Quindi al submit del secondo form, il campo nascosto è vuoto e la categoria non viene di conseguenza salvata.
Dopo un pò di scouting sui sorgenti di Liferay, la soluzione è stata abbastanza semplice da mettere in piedi: differenziare gli id. Ok, ma come?
Nel secondo form, e solo lì, ho sostituito la taglib di Alloy con quella di Liferay andando a specificare esplicitamente tutti gli attributi necessari:
<liferay-ui:asset-categories-selector
className="<%=EntitaB.class.getName() %>"
classPK="<%=entitaB.getPrimaryKey() %>"
contentCallback='<%= renderResponse.getNamespace() + "getSuggestionsContent" %>'
hiddenInput="assetCategoryIdsEntitaB" />
Il punto focale è l'attributo hiddenInput
che mi permette di specificare il nome che dovrà avere il campo nascosto; ma attenzione perchè, affinchè tutto funzioni, è necessario che il valore dell'attributo hiddenInput
inizi con assetCategoryIds
; infatti solo in questo modo il ServiceContext
è in grado di recuperare correttamente i dati dalla request.
Enjoy!