Gli AutoFields sono un componente AlloyUI da utilizzare all'interno di un form per inserire dinamicamente elementi multipli.

Liferay lo utilizza in molti punti: avete mai provato ad inserire un numero di telefono o un indirizzo nel vostro profilo utente? Avete notato i 2 pulsanti + e -? Premendo tali pulsanti vengono aggiunte (o rimosse) intere porzioni HTML del form, permettendovi così di aggiungere elementi multipli; il tutto integrato con la gestione della cronologia di cancellazione.

Vediamo quindi come usare tale componente in un plugin custom.

Lato frontend

La prima cosa da realizzare è la pagina JSP che contiene il form e gli elementi da replicare:

<liferay-portlet:actionURL name="save" var="saveURL" />
<aui:form action="<%=saveURL %>" method="post" name="fm">
	<div id="auto-fields-container">
		<div class="lfr-form-row lfr-form-row-inline">
			<aui:input label="first-name" name="firstName1" />
			<aui:input label="last-name" name="lastName1" required="true" />
		</div>
	</div>

	<aui:button-row>
		<aui:button type="submit" value="save" />
    </aui:button-row>
</aui:form>

<aui:script use="liferay-auto-fields">
new Liferay.AutoFields({
	contentBox: '#auto-fields-container',
	fieldIndexes: '<portlet:namespace />rowIndexes',
	on: {
		'clone': function(event) {
			console.log('Clone event');
			console.log(event);

			console.log('Cloned node');
			console.log(event.row);
		},
		'delete': function(event) {
			console.log('Delete event');
			console.log(event);
		}
	},
	sortable: true,
	sortableHandle: '.lfr-form-row'
}).render();
</aui:script>

Cerchiamo di capire il contenuto della JSP: come prima cosa va definito ovviamente il form HTML con la sua action di salvataggio.

Dopodichè va definito il contenitore degli AutoFields ossia l'elemento che conterrà tutte le porzioni di codice clonate; tale elemento è rappresentato da div#auto-fields-container.

All'interno del contenitore va poi definito il blocco elementare da clonare; tale blocco deve essere racchiuso all'interno di un div le cui classi CSS sono lfr-form-row lfr-form-row-inline.

Passiamo ora alla parte Javascript, la più importante! Qui andremo semplicemente ad istanziare un nuovo oggetto Liferay.AutoFields impostandone il relativo oggetto di configurazione, di cui vi mostro un esempio molto ricco di attributi:

  • contentBox, rappresenta l'elemento contenitore visto sopra;
  • fieldIndexes, rappresenta il nome del campo nascosto che contiene gli indici degli AutoFields attivi (non è necessario aggiungerlo perché lo fa per noi il componente);
  • on clone, evento scatenato quando si clona un blocco (opzionale);
  • on delete, evento scatenato quando si cancella un blocco (opzionale);
  • sortable, determina se i blocchi clonati possono essere spostati con il mouse (opzionale);
  • sortableHandle, rappresenta il selettore CSS dell'elemento da spostare con il mouse (opzionale).

Ma come funziona esattamente il componente? E a cosa serve l'attributo fieldIndexes?

Come avrete notato, gli elementi clonabili del form hanno come suffisso 1 (firstName1 e lastName1); ogni volta che si preme il pulsante +, il blocco HTML viene clonato ed i suffissi dei nuovi elementi vengono incrementati di 1 ogni volta (firstName2 e lastName2, firstName3 e lastName3, ...). Quando invece si preme il pulsante -, il blocco i-esimo viene semplicemente nascosto.

Ad esempio, se clono 3 volte il blocco HTML, ottengo elementi i cui nomi finiscono con 1, 2, 3 e 4; se poi cancello i blocchi 2 e 3, rimangono visibili solamente i blocchi 1 e 4. L'attributo fieldIndexes  rappresenta proprio gli indici dei blocchi, sotto forma di una stringa separata da virgola: 1,4.

Lato backend

Vediamo ora cosa succede lato server quando facciamo submit del form; tutto ruota attorno all'attributo fieldIndexes.

public void save(ActionRequest actionRequest, ActionResponse actionResponse) throws Exception {
	int[] rowIndexes = ParamUtil.getIntegerValues(actionRequest, "rowIndexes", new int[0]);

	for (int i : rowIndexes) {
		String firstName = ParamUtil.getString(actionRequest, "firstName" + i);
		String lastName = ParamUtil.getString(actionRequest, "lastName" + i);

		System.out.println(firstName + StringPool.SPACE + lastName);
	}
}

Il primo passo è il recupero degli indici dei blocchi attivi (ricordo che fieldIndexes è solo il nome dell'attributo di configurazione ma l'elemento nascosto si chiama rowIndexes).

A questo punto è sufficiente fare un ciclo sugli indici e recuperare tutti i campi del form HTML con il loro suffisso e farne quello che serve.

 

Mancherebbe solo un'ultima cosa da fare, ma la lascio a voi come esercizio: una volta recuperati i valori del form, questi andranno salvati probabilmente su database e quindi la JSP dovrà prevalorizzare tutti i blocchi HTML clonati.

Come fare? Basta recuperare in pagina l'elenco degli elementi da visualizzare e fare poi un ciclo che inserisca tanti div.lfr-form-row.lfr-form-row-inline quanti sono gli elementi, avendo cura di incrementare ogni volta di 1 il suffisso dei nomi; al resto penserà poi il componente AutoFields.