Me lo han vuelto a decir, que por qué no escribo sobre informática... Será algo que quiero olvidar.
La cosa es que me han preguntado y además es algo que tenemos que solucionar en el proyecto en el que trabajo y en futuros proyectos basados en Struts1.x.
Struts es útil para lo que es y es que con tanto framework surgir de tanto framework limitado a un solo ámbito del desarrollo de software (¿sólo se hacen frameworks web en el mundo?) pues uno se encuentra con muros que saltar a cada paso.
A pesar de que, tengo que reconocer, en la empresa vamos a lomos de gigantes, he tirado para solucionar esto de archivo. Me he ido a una vieja aplicación que hice para otra empresa.
El problema es tener una selección múltiple en un formulario web, manejarla y guardarla en request siempre que haga falta… El número de elementos seleccionados varía dinámicamente entre petición y petición por javascript. Se puede cambiar de página y se debe conservar la selección de la anterior.
En aquel tiempo, aunque sí sabía que eran buenas prácticas, no hacía tags para esto. Hoy se que no hay nada mejor que MVC para el mantenimiento y separación de responsabilidades, quita muchísimos dolores de cabeza. Entonces usábamos Struts y ahora también. Pero ahora voy a hacer esto con tags, qué carajo. A ver si hay gente libre y aprendemos todos algo.
Pregunté a gentes (con experiencia) y me dijeron que pasando un array de objetos javascript iba muy bien porque “ya lo habían hecho” en su día. Yo también, pero no lo recordaba… Vale, me mintieron como bellacos. Lo he comprobado, un array de objetos al controlador se la suda. Hay que utilizar propiedades indexadas (o mapeadas) y no hay más remedio.
En mi aplicación utilizaba propiedades mapeadas para acceder a objetos anidados de un FormBean, era
En los formularios hay que acceder a las propiedades que son arrays (propiedades indexadas) con una nomenclatura especial que Commons Beanutils comprende ya per se y, por tanto, Struts también.
Así para enviar al servidor un array de dos elementos, hay que crear dos hidden y no uno sólo:
<input value=”uno” name=”lista[0]” type=”hidden”>
<input value=”otro” name=”lista[1]” type=”hidden”>
En el formbean tiene que haber un método que reciba como entrada un array del tipo que sea el valor, en este caso de String:
public void setList (String [] in) {…
Esto no es una propiedad indexada del todo. Del todo sería esta:
public void setElement (int i, String value) {…
public String getElement (int i) { …
Sin que haya un set para la lista.
Ambos tienen problemas, al primero no se le pueden pasar elementos salteados porque el controlador va a crear un array con el número de elementos que tiene la lista en el documento cliente, si tenemos algo del tipo:
<input value=”uno” name=”list” type=”hidden”>
<input value=”otro” name=”list" type=”hidden”>
Provocará un ArrayIndexOutOfBoundsException. Porque se crea un array de dos posiciones y se intenta acceder a la sexta…
El otro tiene el problema de que no puede ser “desatendido”, quiero decir, que se tienen que controlar los índices que llegan y hacer crecer el array que recibirá los elementos en consecuencia.
Mi vieja solución pasaba por usar mapas. Cuando llegaba un índice lo almacenaba en un TreeMap que es ordenado y devolverá siempre los elementos como si fuesen de un array al iterar. La clave era un Integer con lo que me pasasen.
Cojonudo… Ahora sólo queda programar en javascript la creación y borrado de elementos de un formulario o algo parecido para poder hacer el post con ese formato de corchetes para propiedades que son múltiples.
¿Qué son propiedades mapeadas? Pues son exactamente igual pero la clave para mapear es un String. En el FormBean los métodos quedan como:
public void setMap (Map in) {…
public void setElement (String key, String in) {…
public String getElement (Strin key) {…
En este no importa que los elementos sean salteados ni nada por el estilo. El formato del documento html cambia en que ahora las claves se dan entrecomilladas para denotar que son String:
<input value=”uno” name=”list" type=”hidden”>
<input value=”otro” name=”list" type=”hidden”>
Al final tenía un setElement parecido a esto:
public void setElement(String key, String in) {
Integer intKey = null;
Try{
Int n = Integer.parseInt(key);
intKey = new Integer(n);
} catch (Exception e) {
//nothing to do
}
If (intKey == null) {
map.put(key, in);
} else {
Map.put(intKey, in);
}
}
Así podia mapear con Strings numéricos y ordenar. Además, donde hacía falta, pasaba una clave del tipo “padre.hijo.nieto” y parseaba hasta el punto para llamar a setters de propiedades anidadas con Beanutils.