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 distintos servlets de la aplicación. Para cada formulario de nuestra aplicación, debemos crear una clase derivada de ActionForm. Esto puede volverse bastante pesado rápidamente, ya que para cada campo xx debemos 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>
- , y que son creados dinámicamente por el entorno de Struts 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 en 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 selección con el mismo nombre, listas de selección múltiple, etc.). Tanto la clase DynactionForm como la clase ActionForm cuentan con un método validate que no hace nada. Para que verifique la validez de los parámetros del formulario, es necesario derivarla y escribir el método validate por cuenta propia. Por lo tanto, la declaración del formulario será 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 creada por nosotros.
4.2. Creación de la clase DynaActionForm asociada al formulario dinámico
Anteriormente, asociamos 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 {
// validación
public ActionErrors validate(ActionMapping mapping, HttpServletRequest request) {
// manejo 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
}
} //si
// se devuelve la lista de errores
return erreurs;
}
}//clase
Los puntos a destacar son los siguientes:
- la clase deriva de DynaActionForm
- el método validate de la clase DynaActionForm se ha reescrito. Cuando lo ejecuta el controlador Struts, se ha creado el objeto PersonneDynaForm. Este 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 había escrito 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 la lleva a cabo 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");
}//ejecutar
}
Los puntos a tener en cuenta son los siguientes:
- debemos importar la clase PersonneDynaForm, que contiene los datos del formulario, para tener acceso a su definición
- el método `execute` recupera el valor de los parámetros del formulario mediante 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 verificar 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 que la segunda utiliza a 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, con la excepción de 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 muestran algunas capturas de pantalla que el lector debe reproducir.
Se solicita el URLhttp://localhost:8080/strutspersonne1/formulaire.do:

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

Lo intentamos de nuevo, pero aparece un error en el campo «edad»:

Se obtiene la siguiente respuesta:

Lo intentamos de nuevo, esta vez con los valores correctos:

Se obtiene la siguiente respuesta:

4.6. Conclusión
El uso de formularios dinámicos facilita la creación 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, ciertas 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 verificación estándar se puede definir en el archivo de configuración struts-config.html. Si todas las verificaciones que se deben realizar son «estándar», entonces ya no es necesario escribir una clase para el formulario. Eso es lo que veremos ahora.