4. Utilisation de formulaires dynamiques
4.1. Déclaration du formulaire dynamique
Nous avons vu que Struts utilisait des objets ActionForm pour stocker les valeurs des formulaires HTML traitées par les différentes servlets de l'application. Pour chaque formulaire de notre application, il nous faut construire une classe dérivée de ActionForm. Ceci peut devenir vite assez lourd puisque pour chaque champ xx, il nout écrire deux méthodes setXx et getXx. Struts nous offre la possibilité d'utiliser des formulaires
- dont la structure est déclarée dans le fichier struts-config.xml dans la section <form-beans>
- qui sont créés dynamiquement par l'environnement Struts selon la structure déclarée
Ainsi la classe utilisée pour stocker les valeurs nom et age de l'application strutspersonne pourrait être définie comme suit :
<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>
Pour chaque champ du formulaire on définit une balise <form-property> avec deux attributs :
- name : le nom du champ
- type : son type java
Parce que les valeurs d'un formulaire envoyées par un client web sont des chaînes de caractères, les types les plus usités sont java.lang.String pour les champs à valeur unique et java.lang.String[] pour les champs à valeurs multiples (boîtes à cocher portant le même nom, listes à sélection multiple, ...). La classe DynactionForm comme la classe ActionForm a une méthode validate qui ne fait rien. Pour lui faire vérifier la validité des paramètres du formulaire, il faut la dériver et écrire soi-même la méthode validate. La déclaration du formulaire sera donc la suivante :
<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 classe istia.st.struts.personne.PersonneDynaForm devra être écrite par nos soins.
4.2. Écriture de la classe DynaActionForm associée au formulaire dynamique
Ci-dessus, nous avons associé la classe istia.st.struts.personne.PersonneDynaForm au formulaire (nom,age). Nous écrivons maintenant cette classe :
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) {
// gestion des erreurs
ActionErrors erreurs = new ActionErrors();
// le nom doit être non vide
String nom = (String)this.get("nom");
if (nom == null || nom.trim().equals("")) {
erreurs.add("nomvide", new ActionError("personne.formulaire.nom.vide"));
}
// l'âge doit être non vide
String age = (String)this.get("age");
if (age == null || age.trim().equals("")) {
erreurs.add("agevide", new ActionError("personne.formulaire.age.vide"));
}
else {
// l'âge doit être un entier positif
if (!age.matches("^\\s*\\d+\\s*$")) {
erreurs.add("ageincorrect", new ActionError("personne.formulaire.age.incorrect", age));
// on rend la liste des erreurs
}
} //if
// on rend la liste d'erreurs
return erreurs;
}
}//classe
Les points à noter sont les suivants :
- la classe dérive de DynaActionForm
- la méthode validate de la classe DynaActionForm a été réécrite. Lorsqu'elle est exécutée par le contrôleur Struts, l'objet PersonneDynaForm a été construit. Il contient un dictionnaire ayant pour clés les champs du formulaire nom et age et pour valeurs les valeurs de ces champs. Pour avoir accès à un champ au sein des méthodes de DynaActionForm on utilise la méthode Object get(String nomDuChamp). Si on veut fixer une valeur à un champ, on pourra utiliser la méthode void set(String nomDuChamp, Object valeur). On se reportera à la définition de la classe DynaActionForm pour une définition complète.
- une fois récupérées les valeurs des champs nom et age du formulaire, la méthode validate ne diffère pas de celle qui avait été écrite lorsque le formulaire était associé à un objet ActionForm.
4.3. La nouvelle classe FormulaireAction
Le fichier de configuration définit l'action /main suivante :
...
<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>
Cette définition est la même que dans l'application strutspersonne. L'action /main est réalisée par un objet de type FormulaireAction. Cet objet reçevait les valeurs du formulaire frmPersonne dans un objet de type FormulaireBean. Maintenant il les reçoit dans un objet de type PersonneDynaForm. Il faut donc réécrire la classe :
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 {
// on a un formulaire valide, sinon on ne serait pas arrivé là
PersonneDynaForm formulaire=(PersonneDynaForm)form;
request.setAttribute("nom",formulaire.get("nom"));
request.setAttribute("age",formulaire.get("age"));
return mapping.findForward("reponse");
}//execute
}
Les points à noter sont les suivants :
- nous devons importer la classe PersonneDynaForm qui contient les données du formulaire pour avoir accès à sa définition
- la méthode execute récupère la valeur des paramètres du formulaire avec la méthode [DynaActionForm].get.
Vis à vis de la classe FormulaireAction de l'application strutspersonne, seule la façon d'accéder aux valeurs du formulaire a changé.
4.4. Déploiement et test de l'application strutspersonne1
4.4.1. Création du contexte
Nous avons appelé strutspersonne1 cette nouvelle application. Nous créons une nouvelle définition dans le fichier <tomcat>\conf\serveur.xml de Tomcat 4.x :
Ceci fait, il faut relancer Tomcat. On peut vérifier la validité du contexte en demandant l'URL :
http://localhost:8080/strutspersonne1/

4.4.2. Les vues
On copiera le dossier vues de l'application strutspersonne dans le dossier de l'application strutspersonne1. En effet, les vues n'ont pas changé.

4.4.3. Compilation des classes
Nous avons deux classes à créer : PersonneDynaForm et FormulaireAction, la dernière utilisant la première. On pourra les créer et les compiler à l'aide d'un projet Jbuilder :

4.4.4. Le dossier WEB-INF
On copiera le dossier WEB-INF de l'application strutspersonne dans le dossier de l'application strutspersonne1. Quelques fichiers changent :

Le fichier de configuration struts-config.xml devient le suivant :
<?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>
Ce fichier est identique à celui de l'application strutspersonne à l'exception de la définition dynamique du formulaire (partie encadrée).
Dans le dossier WEB-INF/classes, on mettra les classes compilées par Jbuilder :

Dans le dossier WEB-INF\classes\ressources, on place le fichier des messages. Il n'a pas changé.
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. Tests
Nous sommes prêts pour les tests. On trouvera ci-dessous quelques copies d'écran que le lecteur est invité à reproduire.
On demande l'URL http://localhost:8080/strutspersonne1/formulaire.do :

On utilise le bouton [Envoyer] sans remplir les champs :

On recommence avec une erreur sur le champ age :

On obtient la réponse suivante :

On recommence en mettant cette fois-ci des valeurs correctes :

On obtient la réponse suivante :

4.6. Conclusion
L'utilisation de formulaire dynamiques facilite l'écriture des classes de type ActionForm chargées de stocker les valeurs des formulaires. On peut aller un peu plus loin dans la simplification de la gestion des formulaires. Dans l'application strutspersonne1, nous avons créé une classe PersonneDynaForm pour vérifier la validité des valeurs (nom,age) du formulaire. Dans la pratique, certaines vérifications reviennent fréquemment : champ non vide, champ vérifiant une certaine expression régulière, champ entier, champ date, ... Ce type de vérification standard peut alors être demandé dans le fichier de configuration struts-config.html. Si toutes les vérifications à faire sont "standard", alors il n'y a plus de classe à écrire pour le formulaire. C'est ce que nous voyons maintenant.