4. Uso de formularios dinámicos
4.1. Declaración del formulario dinámico
Hemos visto que Struts utiliza objetos ActionForm para almacenar los valores de los formularios HTML procesados por los diferentes servlets de la aplicación. Para cada formulario de nuestra aplicación, debemos crear una clase derivada de ActionForm. Esto puede resultar bastante pesado, ya que para cada campo xx hay que escribir dos métodos: setXx y getXx. Struts nos ofrece la posibilidad de utilizar formularios
- cuya estructura se declara en el archivo struts-config.xml, en la sección <form-beans>
- , que el entorno Struts crea dinámicamente según la estructura declarada
Así, la clase utilizada para almacenar los valores «nombre» y «edad» de la aplicación strutspersonne podría definirse de la siguiente manera:
<form-beans>
<form-bean name="frmPersonne" type="org.apache.struts.actions.DynaActionForm">
<form-property name="nom" type="java.lang.String" initial=""/>
<form-property name="age" type="java.lang.String" initial=""/>
</form-bean>
</form-beans>
Para cada campo del formulario se define una etiqueta <form-property> con dos atributos:
- name: el nombre del campo
- type: su tipo Java
Dado que los valores de un formulario enviados por un cliente web son cadenas de caracteres, los tipos más utilizados son java.lang.String para los campos de valor único y java.lang.String[] para los campos de valores múltiples (casillas de verificación con el mismo nombre, listas de selección múltiple, etc.). La clase DynactionForm, al igual que la clase ActionForm, tiene un método validate que no hace nada. Para que compruebe la validez de los parámetros del formulario, hay que derivarla y escribir uno mismo el método validate. La declaración del formulario será, por tanto, la siguiente:
<form-beans>
<form-bean name="frmPersonne" type="istia.st.struts.personne.PersonneDynaForm">
<form-property name="nom" type="java.lang.String" initial=""/>
<form-property name="age" type="java.lang.String" initial=""/>
</form-bean>
</form-beans>
La clase istia.st.struts.personne.PersonneDynaForm deberá ser escrita por nosotros.
4.2. Escritura de la clase DynaActionForm asociada al formulario dinámico
Anteriormente, hemos asociado la clase istia.st.struts.personne.PersonneDynaForm al formulario (nombre, edad). Ahora escribimos esta clase:
package istia.st.struts.personne;
import javax.servlet.http.*;
import org.apache.struts.action.*;
public class PersonneDynaForm extends DynaActionForm {
// validation
public ActionErrors validate(ActionMapping mapping, HttpServletRequest request) {
// gestión de errores
ActionErrors erreurs = new ActionErrors();
// el nombre no debe estar vacío
String nom = (String)this.get("nom");
if (nom == null || nom.trim().equals("")) {
erreurs.add("nomvide", new ActionError("personne.formulaire.nom.vide"));
}
// la edad no debe estar vacía
String age = (String)this.get("age");
if (age == null || age.trim().equals("")) {
erreurs.add("agevide", new ActionError("personne.formulaire.age.vide"));
}
else {
// la edad debe ser un número entero positivo
if (!age.matches("^\\s*\\d+\\s*$")) {
erreurs.add("ageincorrect", new ActionError("personne.formulaire.age.incorrect", age));
// se devuelve la lista de errores
}
} //if
// se devuelve la lista de errores
return erreurs;
}
}//classe
Los puntos a tener en cuenta son los siguientes:
- la clase deriva de DynaActionForm
- se ha reescrito el método validate de la clase DynaActionForm. Cuando lo ejecuta el controlador Struts, se ha creado el objeto PersonneDynaForm. Contiene un diccionario cuyas claves son los campos del formulario «nombre» y «edad» y cuyos valores son los valores de dichos campos. Para acceder a un campo dentro de los métodos de DynaActionForm se utiliza el método Object get(String nomDuChamp). Si se desea asignar un valor a un campo, se puede utilizar el método void set(String nomDuChamp, Object valor). Consulte la definición de la clase DynaActionForm para obtener una descripción completa.
- Una vez recuperados los valores de los campos nombre y edad del formulario, el método validate no difiere del que se escribió cuando el formulario estaba asociado a un objeto ActionForm.
4.3. La nueva clase FormulaireAction
El archivo de configuración define la siguiente acción /main:
...
<form-beans>
<form-bean name="frmPersonne" type="istia.st.struts.personne.PersonneDynaForm">
<form-property name="nom" type="java.lang.String" initial=""/>
<form-property name="age" type="java.lang.String" initial=""/>
</form-bean>
</form-beans>
....
<action
path="/main"
name="frmPersonne"
scope="session"
validate="true"
input="/erreurs.do"
type="istia.st.struts.personne.FormulaireAction"
>
<forward name="reponse" path="/reponse.do"/>
</action>
Esta definición es la misma que en la aplicación strutspersonne. La acción /main se lleva a cabo mediante un objeto de tipo FormulaireAction. Este objeto recibía los valores del formulario frmPersonne en un objeto de tipo FormulaireBean. Ahora los recibe en un objeto de tipo PersonneDynaForm. Por lo tanto, hay que reescribir la clase:
package istia.st.struts.personne;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import javax.servlet.ServletException;
import istia.st.struts.personne.PersonneDynaForm;
public class FormulaireAction extends Action {
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response) throws IOException,ServletException {
// tenemos un formulario válido; de lo contrario, no habríamos llegado hasta aquí
PersonneDynaForm formulaire=(PersonneDynaForm)form;
request.setAttribute("nom",formulaire.get("nom"));
request.setAttribute("age",formulaire.get("age"));
return mapping.findForward("reponse");
}//execute
}
Los puntos a tener en cuenta son los siguientes:
- debemos importar la clase PersonneDynaForm, que contiene los datos del formulario, para poder acceder a su definición
- el método execute recupera el valor de los parámetros del formulario con el método [DynaActionForm].get.
En cuanto a la clase FormulaireAction de la aplicación strutspersonne, solo ha cambiado la forma de acceder a los valores del formulario.
4.4. Implementación y prueba de la aplicación strutspersonne1
4.4.1. Creación del contexto
Hemos llamado a esta nueva aplicación strutspersonne1. Creamos una nueva definición en el archivo <tomcat>\conf\serveur.xml de Tomcat 4.x:
Una vez hecho esto, hay que reiniciar Tomcat. Podemos comprobar la validez del contexto solicitando el URL:
http://localhost:8080/strutspersonne1/

4.4.2. Las vistas
Copiaremos la carpeta «vues» de la aplicación strutspersonne a la carpeta de la aplicación strutspersonne1. De hecho, las vistas no han cambiado.

4.4.3. Compilación de las clases
Tenemos que crear dos clases: PersonneDynaForm y FormulaireAction, siendo esta última dependiente de la primera. Podemos crearlas y compilarlas utilizando un proyecto de JBuilder:

4.4.4. La carpeta WEB-INF
Copiaremos la carpeta WEB-INF de la aplicación strutspersonne a la carpeta de la aplicación strutspersonne1. Algunos archivos cambian:

El archivo de configuración struts-config.xml queda así:
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE struts-config PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 1.1//EN"
"http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd">
<struts-config>
<form-beans>
<form-bean name="frmPersonne" type="istia.st.struts.personne.PersonneDynaForm">
<form-property name="nom" type="java.lang.String" initial=""/>
<form-property name="age" type="java.lang.String" initial=""/>
</form-bean>
</form-beans>
<action-mappings>
<action
path="/main"
name="frmPersonne"
scope="session"
validate="true"
input="/erreurs.do"
type="istia.st.struts.personne.FormulaireAction"
>
<forward name="reponse" path="/reponse.do"/>
</action>
<action
path="/erreurs"
parameter="/vues/erreurs.personne.jsp"
type="org.apache.struts.actions.ForwardAction"
/>
<action
path="/reponse"
parameter="/vues/reponse.personne.jsp"
type="org.apache.struts.actions.ForwardAction"
/>
<action
path="/formulaire"
parameter="/vues/formulaire.personne.jsp"
type="org.apache.struts.actions.ForwardAction"
/>
</action-mappings>
<message-resources parameter="ressources.personneressources"/>
</struts-config>
Este archivo es idéntico al de la aplicación strutspersonne, salvo por la definición dinámica del formulario (parte enmarcada).
En la carpeta WEB-INF/classes, se colocarán las clases compiladas por JBuilder:

En la carpeta WEB-INF\classes\ressources, se coloca el archivo de mensajes. No ha cambiado.
personne.formulaire.nom.vide=<li>Vous devez indiquer un nom</li>
personne.formulaire.age.vide=<li>Vous devez indiquer un age</li>
personne.formulaire.age.incorrect=<li>L'âge [{0}] est incorrect</li>
errors.header=<ul>
errors.footer=</ul>

4.5. Pruebas
Estamos listos para las pruebas. A continuación se incluyen algunas capturas de pantalla que el lector puede reproducir.
Se solicita el URL http://localhost:8080/strutspersonne1/formulaire.do :

Se utiliza el botón [Envoyer] sin rellenar los campos:

Volvemos a intentarlo con un error en el campo «edad»:

Se obtiene la siguiente respuesta:

Volvemos a intentarlo, esta vez introduciendo los valores correctos:

Se obtiene la siguiente respuesta:

4.6. Conclusión
El uso de formularios dinámicos facilita la escritura de clases del tipo ActionForm encargadas de almacenar los valores de los formularios. Podemos ir un poco más allá en la simplificación de la gestión de los formularios. En la aplicación strutspersonne1, hemos creado una clase PersonneDynaForm para verificar la validez de los valores (nombre, edad) del formulario. En la práctica, algunas verificaciones se repiten con frecuencia: campo no vacío, campo que verifica una determinada expresión regular, campo entero, campo de fecha, etc. Este tipo de comprobación estándar se puede solicitar entonces en el archivo de configuración struts-config.html. Si todas las comprobaciones que hay que realizar son «estándar», entonces ya no hay que escribir ninguna clase para el formulario. Esto es lo que veremos ahora.