Skip to content

6. Formulaires HTML

Jusqu'à maintenant, nous avons utilisé un unique formulaire ne comprenant que deux champs de saisie. Nous nous proposons ici de créer et traiter un formulaire utilisant les composants graphiques usuels (boutons radio, cases à cocher, champs de saisie, combobox, listes).

6.1. Les vues de l'application

L'application n'aura que deux vues. La première présente un formulaire vierge :

VUE 1 - formulaire

Image

Cette première vue appelée formulaire.jsp nous permettra de mettre en oeuvre différentes balises de la bibliothèque struts-html. L'utilisateur remplit le formulaire :

Image

Le bouton [Envoyer] permet d'avoir une confirmation des valeurs saisies. Ce sera la deuxième vue :

Image

Cette deuxième vue nous permettra d'utiliser deux autres bibliothèques de balises struts-bean et struts-logic. Le lien [Retour au formulaire] nous permet de retrouver le formulaire tel que l'on l'a rempli. On retrouve alors la première vue.

6.2. L'architecture de l'application

  • le formulaire (vue 1) sera représenté par un objet Struts dynamique appelé dynaFormulaire de type dérivé de DynaActionForm. Il sera affiché par la vue formulaire.jsp.
  • l'action Struts InitFormulaireAction aura pour but d'acquérir les données nécessaires à l'affichage du formulaire
  • le formulaire rempli sera traité par une action ForwardAction qui se contera de rediriger la requête vers la seconde vue confirmation.jsp. Celle-ci se chargera d'afficher les valeurs du formulaire.

6.3. La configuration de l'application

6.3.1. Le fichier server.xml

Le contexte de l'application s'appellera /formulaire2. On ajoutera donc la ligne suivante dans le fichier server.xml de Tomcat :

    <Context path="/formulaire2" docBase="e:/data/serge/web/struts/formulaire2" />

Ceci fait, nous relançons éventuellement Tomcat afin qu'il prenne en compte le nouveau contexte. Nous pouvons vérifier la validité de celui-ci en demandant l'URL http://localhost:8080/formulaire2 :

Image

6.3.2. Le fichier web.xml

Le fichier de configuration web.xml de l'application sera le suivant :

<?xml version="1.0" encoding="ISO-8859-1"?>

<!DOCTYPE web-app
    PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    "http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>
    <servlet>
      <servlet-name>action</servlet-name>
    <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
    <init-param>
        <param-name>config</param-name>
      <param-value>/WEB-INF/struts-config.xml</param-value>
    </init-param>
  </servlet>

  <servlet-mapping>
      <servlet-name>action</servlet-name>
    <url-pattern>*.do</url-pattern>
  </servlet-mapping>

  <taglib>
      <taglib-uri>/WEB-INF/struts-html.tld</taglib-uri>
    <taglib-location>/WEB-INF/struts-html.tld</taglib-location>
  </taglib>
  <taglib>
      <taglib-uri>/WEB-INF/struts-bean.tld</taglib-uri>
    <taglib-location>/WEB-INF/struts-bean.tld</taglib-location>
  </taglib>
  <taglib>
      <taglib-uri>/WEB-INF/struts-logic.tld</taglib-uri>
    <taglib-location>/WEB-INF/struts-logic.tld</taglib-location>
  </taglib>

</web-app>

Vis à vis des fichiers de configuration web.xml déjà rencontrés nous apportons quelques modifications :

  • nous introduisons deux nouvelles bibliothèques de balises struts-bean et struts-logic. Elles seront utilisées dans la vue confirmation.jsp. La vue formulaire.jsp utilisera, elle, la bibliothèque struts-html.

6.3.3. Le fichier struts-config.xml

Le fichier struts-config.xml sera lui 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="dynaFormulaire" type="istia.st.struts.formulaire.DynaFormulaire">
            <form-property name="opt" type="java.lang.String" initial="non"/>
            <form-property name="chk1" type="java.lang.String"/>
            <form-property name="chk2" type="java.lang.String"/>
            <form-property name="chk3" type="java.lang.String"/>            
            <form-property name="champSaisie" type="java.lang.String" initial=""/>
            <form-property name="mdp" type="java.lang.String" initial=""/>        
            <form-property name="boiteSaisie" type="java.lang.String" initial=""/>        
            <form-property name="combo" type="java.lang.String"/>
            <form-property name="listeSimple" type="java.lang.String"/>
            <form-property name="listeMultiple" type="java.lang.String[]"/>                
            <form-property name="secret" type="java.lang.String" initial="xxx"/>
            <form-property name="valeursCombo" type="java.lang.String[]" />
            <form-property name="valeursListeSimple" type="java.lang.String[]" />
            <form-property name="valeursListeMultiple" type="java.lang.String[]"/>                  
        </form-bean>            
    </form-beans>

    <action-mappings>
      <action
          path="/confirmation"
            name="dynaFormulaire"
            validate="false" 
            scope="session"
          parameter="/vues/confirmation.jsp"
          type="org.apache.struts.actions.ForwardAction"
      />

      <action
          path="/init"
            name="dynaFormulaire"
            validate="false" 
            scope="session"
          type="istia.st.struts.formulaire.InitFormulaireAction"
      >
            <forward name="afficherFormulaire" path="/vues/formulaire.jsp"/>
        </action>

      <action
          path="/affiche"
          parameter="/vues/formulaire.jsp"
          type="org.apache.struts.actions.ForwardAction"
      />

    </action-mappings>

        <message-resources 
      parameter="ApplicationResources"
        null="false"/>    

</struts-config>

On y retrouve les trois grandes sections :

  • la déclaration des formulaires dans la section <form-beans>
  • la déclaration des actions dans la section <action-mappings>
  • la déclaration du fichier de ressources dans <message-ressources>

6.3.4. Les objets (beans) formulaires de l'application

Les objets utilisés pour représenter les formulaires HTML de l'application sont des objets de type ActionForm ou dérivé (DynaActionForm, DynaValidatorForm, ...). On les appelle des beans parce que leur construction suit les règles des JavaBeans. Il n'y a qu'un bean formulaire dans notre application., appelé dynaFormulaire et de type dérivé de DynaActionForm. Il sera utilisé dans les situations suivantes :

  • contenir les données nécessaires à l'affichage de la vue n° 1
  • récupérer les valeurs du formulaire de la vue n°1 lorsque l'utilisateur va le valider (submit)
  • contenir les données nécessaires à l'affichage de la vue n° 2

La structure du bean dynaFormulaire est intimement lié au formulaire de la vue n° 1. Etudions celui-ci :

Type HTML

Rôle

1

<input name="opt" type="radio" value="oui">

<input name="opt" type="radio" value="non">

groupe de boutons radio liés entre-eux (même nom)

2

<input name="chk1" type="radio" value="on">

<input name="chk2" type="radio" value="on">

<input name="chk3" type="radio" value="on">

groupes de cases à cocher

indépendantes (pas le même nom)

3

<input type="text " name="champSaisie" >

un champ de saisie

4

<input type="password " name="mdp" >

un champ mot de passe

5

<textarea name="boiteSaisie">...</textarea>

un champ de saisie multi-lignes

6

<select name="combo" size="1">..</select>

un combo

7

<select name="listeSimple" size="3">..</select>

une liste à sélection unique

8

<select name="listeMultiple" size="3" multiple>..</select>

une liste à sélection multiple

9

<input type="button" value="Effacer"

onclick='effacerListe("listeSimple")'>

bouton permettant de désélectionner

les éléments sélectionnés dans listeSimple (7)

10

<input type="button" value="Effacer"

onclick='effacerListe("listeMultiple")'>

bouton permettant de désélectionner

les éléments sélectionnés dans listeMultiple (8)

11

<input type="submit" value="Envoyer">

bouton submit du formulaire

12

<input type="hidden" name="secret" value="...">

un champ caché

Distinguons plusieurs cas :

  1. l'objet dynaFormulaire est utilisé pour contenir les valeurs du formulaire HTML ci-dessus qui sera posté par le bouton [Envoyer]. Il faut donc qu'il ait les mêmes champs que dans le formulaire HTML. Le type du champ est fixé par la règle suivante :
  • si le champ HTML ne fournit qu'une valeur, alors le champ de dynaFormulaire sera de type java.lang.String
  • si le champ HTML fournit plusieurs valeurs, alors le champ de dynaFormulaire sera de type java.lang.String[]

Dans le formulaire HTML ci-dessus, seul le champ listeMultiple est susceptible d'être associé à plusieurs valeurs (celles sélectionnées par l'utilisateur). Donc une première définition de l'objet dynaFormulaire serait la suivante :

        <form-bean name="dynaFormulaire" type="istia.st.struts.formulaire.DynaFormulaire">
            <form-property name="opt" type="java.lang.String" initial="non"/>
            <form-property name="chk1" type="java.lang.String"/>
            <form-property name="chk2" type="java.lang.String"/>
            <form-property name="chk3" type="java.lang.String"/>            
            <form-property name="champSaisie" type="java.lang.String" initial=""/>
            <form-property name="mdp" type="java.lang.String" initial=""/>        
            <form-property name="boiteSaisie" type="java.lang.String" initial=""/>        
            <form-property name="combo" type="java.lang.String"/>
            <form-property name="listeSimple" type="java.lang.String"/>
            <form-property name="listeMultiple" type="java.lang.String[]"/>                
            <form-property name="secret" type="java.lang.String" initial="xxx"/>
        </form-bean>            

Comment se fera le remplissage de dynaFormulaire avec les valeurs du formulaire HTML envoyées par le client web ?

opt

le champ opt recevra la valeur "oui" si le champ HTML <input type="radio" name="opt" value="oui"> a été coché, la valeur "non" si c'est le champ <input type="radio" name="opt" value="non"> qui a été coché.

chk1

le champ chk1 recevra la valeur "on" si le champ HTML <input name="chk1" type="radio" value="1"> a été coché, rien sinon. Dans ce dernier cas, le champ chk1 conservera sa précédente valeur.

chk2

idem

chk3

idem

champSaisie

le champ champSaisie recevra le texte mis par l'utilisateur dans le champ HTML <input type="text" name="champSaisie">. Ce texte peut être éventuellement la chaîne vide.

mdp

le champ mdp recevra le texte mis par l'utilisateur dans le champ HTML <input type="password" name="mdp">. Ce texte peut être éventuellement la chaîne vide.

boiteSaisie

le champ boiteSaisie recevra le texte mis par l'utilisateur dans le champ HTML <textarea name="boiteSaisie">...</textarea>. Ce texte forme une seule chaîne de caractères, formée des lignes tapées par l'utilisateur séparées les unes des autres par la séquence de caractères "\r\n". Le texte obtenu peut être éventuellement la chaîne vide.

combo

le champ combo recevra l'option sélectionnée par l'utilisateur dans le champ HTML <select name="combo" size="1">..</select>. L'option sélectionnée est celle qui apparaît dans le combo. Si l'option HTML sélectionnée est du type <option value="XX">YY</option>, le champ combo recevra la valeur "XX". Si l'option HTML sélectionnée est du type <option>YY</option>, le champ combo recevra la valeur "YY".

listeSimple

le champ listeSimple recevra l'option sélectionnée par l'utilisateur dans le champ HTML <select name="listeSimple" size="..">..</select> s'il y en a une. S'il n'y en a pas, le champ listeSimple ne recevra aucune valeur et gardera sa valeur précédente. La valeur affectée réellement au champ listeSimple suit les règles indiquées pour le combo.

listeMultiple

le champ listeMultiple de type String[] recevra les options sélectionnées par l'utilisateur dans le champ HTML <select name="listeMultiple" size=".." multiple>..</select> s'il y en a. S'il n'y en a pas, le tableau listeMultiple ne recevra aucune valeur et son contenu restera inchangé. Les valeurs affectées réellement au tableau listeMultiple suivent les règles indiquées pour le combo.

secret

le champ secret recevra la valeur XX du champ HTML <input type="hidden" name="secret" value="XX">. Ce texte peut être éventuellement la chaîne vide.

  1. l'objet dynaFormulaire est utilisé pour donner le contenu initial de la vue n° 1. Les valeurs des champs précédents seront utilisées aux fins suivantes :
opt

devra avoir la valeur "oui" ou "non" pour que le navigateur sache quel bouton radio cocher

chk1

si chk1 a la valeur "on" la case à cocher sera cochée sinon elle ne le sera pas

chk2

idem

chk3

idem

champSaisie

la valeur du champ sera affichée dans la zone de saisie champSaisie

mdp

la valeur du champ sera affichée dans la zone de saisie mdp

boiteSaisie

la valeur du champ sera affichée dans la zone de saisie boiteSaisie

combo

la valeur de ce champ indique quel élément du combo doit être sélectionné à l'affichage du formulaire

listeSimple

idem

listeMultiple

les valeurs du tableau listeMultiple indiquent quels éléments de la liste multiple doivent être sélectionnés à l'affichage du formulaire

secret

la valeur du champ sera affectée à l'attribut value du champ HTML secret.

La vue n° 1 a besoin d'autres informations :

  • la liste des valeurs à afficher dans la liste combo
  • la liste des valeurs à afficher dans la liste listeSimple
  • la liste des valeurs à afficher dans la liste listeMultiple

Il existe plusieurs façons de fournir ces informations à la vue. Des tableaux placés dans la requête passée à la vue feraient par exemple l'affaire. Ici, nous plaçons ces tableaux dans le bean dynaFormulaire :

        <form-bean name="dynaFormulaire" type="istia.st.struts.formulaire.DynaFormulaire">
...
            <form-property name="valeursCombo" type="java.lang.String[]" />
            <form-property name="valeursListeSimple" type="java.lang.String[]" />
            <form-property name="valeursListeMultiple" type="java.lang.String[]"/>                  
        </form-bean>            

Le formulaire dynaFormulaire sera initialisé par l'action /init qui fera appel à un objet dérivé de Action appelé InitFormulaireAction. C'est cet objet qui se chargera de créer les trois tableaux nécessaires à l'affichage des trois listes et de les mettre dans le bean dynaFormulaire. Le fichier de configuration donne à ce bean une portée égale à la session. Ceci entraîne que le contrôleur Struts placera ce bean dans la session. Nous n'aurons ainsi pas besoin de le régénérer entre deux cycles demande-réponse. Aussi l'action /init ne sera-t-elle appelée qu'une fois.

  1. l'objet dynaFormulaire est également utilisé pour donner le contenu de la vue n° 2. Celle-ci se contente d'en afficher les valeurs.

6.3.5. Les actions de l'application

Les actions sont assurées par des objets de type Action ou dérivé. La configuration des actions est faite à l'intérieur des balises <action-mappings> :

    <action-mappings>
      <action
          path="/confirmation"
            name="dynaFormulaire"
            validate="false" 
            scope="session"
          parameter="/vues/confirmation.jsp"
          type="org.apache.struts.actions.ForwardAction"
      />

      <action
          path="/init"
            name="dynaFormulaire"
            validate="false" 
            scope="session"
          type="istia.st.struts.formulaire.InitFormulaireAction"
      >
            <forward name="afficherFormulaire" path="/vues/formulaire.jsp"/>
        </action>

      <action
          path="/affiche"
          parameter="/vues/formulaire.jsp"
          type="org.apache.struts.actions.ForwardAction"
      />

    </action-mappings>

Notons qu'il n'y a pas toujours un formulaire associé à une action. C'est le cas, ci-dessus, de l'action /affiche. Avant de détailler chaque action, rappelons le fonctionnement du couple action-formulaire associé au sein d'une balise <action> :

  • une action commence avec une requête d'un client web et se termine par l'envoi d'une page réponse. C'est le cycle demande-réponse du client-serveur web. La requête est reçue par le contrôleur Struts de type ActionServlet ou dérivé. C'est aussi ce contrôleur qui envoie la réponse.
  • le bean du formulaire de type ActionForm ou dérivé est créé s'il n'existe pas déjà. Le contrôleur regarde s'il peut trouver un objet de nom name dans la portée indiquée par scope. Si oui, il l'utilise. Si non, il le crée et le met dans la portée indiquée par scope, associé à l'attribut indiqué par name.

Dans l'exemple de l'action /init par exemple, le contrôleur fera un request.getSession().getAttribute("dynaFormulaire") pour savoir si dynaFormulaire a déjà été créé ou non. Si ce n'est pas le cas, il le créera et le mettra dans la session par une instruction du genre request.getSession().setAttribute("dynaFormulaire",new DynaFormulaire(...)).

  • le contrôleur va également chercher un objet Action du type indiqué par l'attribut type. S'il ne trouve pas, il le crée, sinon il l'utilise.
  • la méthode reset du bean formulaire va être appelée. Celui-ci, sauf lors de sa création initiale, est recyclé. Il contient alors des données qu'on peut vouloir "nettoyer". C'est dans la méthode reset du bean ActionForm ou dérivé que l'on fera cela.
  • si l'action est la cible d'un formulaire posté, alors les valeurs du formulaire qui se trouvent dans la requête du client sont copiées dans les champs de même nom du bean formulaire. On notera que la méthode reset a été appelée avant cette recopie.
  • si la configuration précise l'attribut validate="true", la méthode validate du bean formulaire sera appelée. Celle-ci doit alors vérifier les données du bean. Cette vérification n'a lieu le plus souvent que lorsque le formulaire vient de recevoir de nouvelles données via un formulaire posté et qu'on veut vérifier la validité de ces données. Cette méthode rend au contrôleur une éventuelle liste d'erreurs dans un objet ActionErrors.
  • si l'objet ActionErrors est non vide, le contrôleur fait afficher la vue précisée par l'attribut input de l'action.
  • si la validation des données n'est pas demandée ou si elle s'est bien passée, le contrôleur fait exécuter la méthode execute de l'objet de type Action ou dérivé associé à l'action en cours. C'est dans cette méthode que la demande du client web est traitée. La méthode execute renvoie un objet ActionForward indexé par des clés de type chaîne de caractères. Ces clés sont celles déclarées par les balises forward de l'action configurée. Dans notre exemple, l'action /init a une seule balise forward. Elle associe la clé "afficherFormulaire" à la vue formulaire.jsp.
  • le contrôleur fait afficher la vue à laquelle est associée la clé reçue. Cette vue peut être en fait une action, auquel cas le processus précédent est répété.

L'action /init

        <action
          path="/init"
            name="dynaFormulaire"
            validate="false" 
            scope="session"
          type="istia.st.struts.formulaire.InitFormulaireAction"
      >
            <forward name="afficherFormulaire" path="/vues/formulaire.jsp"/>
        </action>
  • l'action /init a lieu normalement une fois lors du 1er cycle demande-réponse lorsque l'utilisateur demande l'URL http://localhost:8080/formulaire2/init.do
  • l'objet dynaFormulaire est créé ou recyclé. Il est récupéré (recyclage) ou placé (création) dans la session comme le demande l'attribut scope.
  • sa méthode reset est appelée. Que doit-elle faire ? Normalement, on remet les champs de l'objet ActionForm à des valeurs par défaut. Seulement ici, nous ne le ferons pas, car l'objet dynaFormulaire est placé dans la session (scope="session"). Les champs de dynaFormulaire doivent donc garder leurs valeurs. Quelles sont celles-ci lors de la création initiale de l'objet dynaFormulaire ? Il y a deux cas :
  • le champ a une valeur initiale indiquée dans le fichier de configuration :
            <form-property name="opt" type="java.lang.String" initial="non"/>

Dans ce cas, le contrôleur Struts créera ce champ avec cette valeur initiale.

  • le champ n'a pas de valeur initiale par configuration : les règles d'initialisation de Java s'appliquent. En général, les champs numériques auront la valeur zéro, les chaînes de caractères auront pour valeur la chaîne vide, les autres objets ayant la valeur null.

Regardons la configuration initiale de dynaFormulaire :

        <form-bean name="dynaFormulaire" type="istia.st.struts.formulaire.DynaFormulaire">
            <form-property name="opt" type="java.lang.String" initial="non"/>
            <form-property name="chk1" type="java.lang.String"/>
            <form-property name="chk2" type="java.lang.String"/>
            <form-property name="chk3" type="java.lang.String"/>            
            <form-property name="champSaisie" type="java.lang.String" initial=""/>
            <form-property name="mdp" type="java.lang.String" initial=""/>        
            <form-property name="boiteSaisie" type="java.lang.String" initial=""/>        
            <form-property name="combo" type="java.lang.String"/>
            <form-property name="listeSimple" type="java.lang.String"/>
            <form-property name="listeMultiple" type="java.lang.String[]"/>                
            <form-property name="secret" type="java.lang.String" initial="xxx"/>
            <form-property name="valeursCombo" type="java.lang.String[]" />
            <form-property name="valeursListeSimple" type="java.lang.String[]" />
            <form-property name="valeursListeMultiple" type="java.lang.String[]"/>             
        </form-bean>            

Les valeurs initiales des champs de dynaFormulaire après sa création seront les suivantes :

Champ

Valeur initiale

opt

"non"

chk1, chk2, chk3

chaîne vide

champSaisie

chaîne vide

mdp

chaîne vide

boiteSaisie

chaîne vide

combo

chaîne vide

listeSimple

chaîne vide

listeMultiple

tableau de chaînes vides

secret

"xxx"

valeursCombo, listeSimple, listeMultiple

tableau de chaînes vides

  • on pourrait imaginer que la méthode reset de dynaFormulaire donne des valeurs aux trois tableaux devant alimenter les trois listes de la vue formulaire.jsp. Ce serait possible ici car les données de ces trois tableaux sont générées de façon arbitraire. Néanmoins, le cas le plus usuel est que ces données proviennent du modèle de l'application, le M de MVC. Ici, nous prendrons une position intermédiaire, pour ne pas compliquer l'exemple, en faisant générer ces valeurs par l'action InitFormulaireAction donc par le C de MVC.
  • il n'y a pas obligation d'écrire une méthode reset à dynaFormulaire, la classe ActionForm dont elle dérive ayant une telle méthode qui ne fait rien (pas d'initialisations).
  • une fois la méthode reset de dynaFormulaire appelée, le contrôleur regarde l'attribut validate de l'action. Ici, il a la valeur "false". La méthode validate de dynaFormulaire ne sera pas appelée.
  • l'objet InitFormulaireAction est créé ou recyclé s'il existait déjà et sa méthode execute lancée. C'est elle qui va affecter des valeurs arbitraires aux trois tableaux de dynaFormulaire : valeursCombo, valeursListeSimple et valeursListeMultiple. La méthode rend un ActionForward de clé "afficherFormulaire".
  • le contrôleur fait afficher la vue /vues/formulaire.jsp qui a été associée à la clé "afficherFormulaire" par une balise forward de l'action /init.

L'action /confirmation

      <action
          path="/confirmation"
            name="dynaFormulaire"
            validate="false" 
            scope="session"
          parameter="/vues/confirmation.jsp"
          type="org.apache.struts.actions.ForwardAction"
      />
  • l'action /confirmation a lieu lorsque l'utilisateur appuie sur le bouton [Envoyer] de la vue n° 1. Le navigateur "poste" alors au contrôleur Struts le formulaire rempli par l'utilisateur.
  • l'objet dynaFormulaire est pris dans la session
  • sa méthode reset est appelée. Une fois qu'elle aura été appelée, le contrôleur Struts va copier les valeurs des champs du formulaire posté par le client dans les champs de même nom de dynaFormulaire. Reprenons la liste des champs de ce dernier et voyons comment s'opère cette recopie :

Champ

Code HTML associé

Valeur du champ

après recopie des valeurs du formulaire

opt

<input type="radio" name="opt" value="oui">Oui

<input type="radio" name="opt" value="non" checked="checked">Non

- "oui" ou "non" selon le bouton radio coché

chk1

<input type="checkbox" name="chk1" value="on">

- "on" si la case chk1 a été cochée

- garde sa précédente valeur si la case chk1 n'a pas été cochée

chk2

<input type="checkbox" name="chk2" value="on">

- "on" si la case chk2 a été cochée

- garde sa précédente valeur si la case chk2 n'a pas été cochée

chk3

<input type="checkbox" name="chk2" value="on">

- "on" si la case chk3 a été cochée

- garde sa précédente valeur si la case chk3 n'a pas été cochée

champSaisie

<input type="text" name="champSaisie" value="">

- valeur tapée par l'utilisateur dans champSaisie

mdp

<input type="password" name="mdp" value="">

- valeur tapée par l'utilisateur dans mdp

boiteSaisie

<textarea name="boiteSaisie"></textarea>

- valeur tapée par l'utilisateur dans boiteSaisie

combo

<select name="combo">...</select>

- valeur sélectionnée par l'utilisateur dans combo

listeSimple

<select name="listeSimple" size="3">...</select>

- valeur sélectionnée par l'utilisateur dans listeSimple

listeMultiple

<select name="listeMultiple" multiple="multiple" size="5">

- tableau de chaînes contenant les valeurs sélectionnées par l'utilisateur dans listeMultiple

secret

<input type="hidden" name="secret" value="xxx">

- "xxx".

On a une difficulté avec les champs qui ne reçoivent pas forcément une valeur dans la requête envoyée par le navigateur. C'est le cas des cases à cocher chk1 à chk3 et des deux listes listeSimple et listeMultiple. Dans ce cas, ces champs gardent leur valeur précédente, celles acquises au cours du cycle demande-réponse précédent.

Raisonnons sur la case à cocher chk1 par exemple et supposons que dans le précédent cycle demande-réponse, l'utilisateur avait coché cette case. Le navigateur avait alors envoyé dans la chaîne de paramètres de sa requête l'information chk1="on". Le constructeur a donc affecté la valeur "on" au champ chk1 de dynaFormulaire. Supposons maintenant que dans le cycle en cours, l'utilisateur ne coche pas la case chk1. Dans ce cas, dans la chaîne de paramètres de la nouvelle requête, le navigateur n'envoie pas quelque chose comme chk1="off" mais n'envoie rien. Du coup, le champ chk1 de dynaFormulaire va garder sa valeur "on" et donc avoir une valeur qui ne reflète pas celle du formulaire validé par l'utilisateur. On utilisera la méthode reset de dynaFormulaire pour résoudre ce problème. On mettra dans cette méthode les trois champs chk1, chk2, chk3 à "off". Dans notre exemple de chk1, soit l'utilisateur :

  • coche la case chk1. Alors le navigateur envoie l'information chk1="on" et le champ chk1 de dynaFormulaire va passer à "on"
  • ne coche pas la case chk1. Alors le navigateur n'envoie pas de valeur pour le champ chk1 qui va garder sa précédente valeur "off". Dans les deux cas, la valeur enregistrée dans le champ chk1 de dynaFormulaire est correcte.

Le problème est analogue pour les deux listes listeSimple et listeMultiple. Si aucune option n'a été sélectionnée dans ces listes, elles ne seront pas présentes dans les paramètres de la requête et vont donc conserver leurs valeurs précédentes. Dans la méthode reset de dynaFormulaire, nous ré-initialiserons listeSimple avec une chaîne de caractères vide et listeMultiple avec un tableau de chaînes de longueur 0.

  • une fois la méthode reset de dynaFormulaire appelée, le contrôleur recopie dans les champs de dynaFormulaire les informations qui lui ont été envoyées dans la requête du client
  • un objet ForwardAction est créé ou recyclé et sa méthode execute appelée. ForwardAction est une classe prédéfinie qui rend un objet ActionForward pointant sur la vue définie par l'attribut "parameter" de l'action, ici /vues/confirmation.jsp.
  • le contrôleur envoie cette vue. Le cycle est terminé.

L'action /affiche

      <action
          path="/affiche"
          parameter="/vues/formulaire.jsp"
          type="org.apache.struts.actions.ForwardAction"
      />
  • l'action /affiche est déclenchée par activation du lien [Retour vers le formulaire] de la vue n° 2.
  • ici il n'y a pas de formulaire associée à l'action. On passe donc tout de suite à l'exécution de la méthode execute d'un objet ForwardAction qui va rendre un objet ActionForward pointant sur la vue /vues/formulaire.jsp.

6.3.6. Le fichier de messages de l'application

La troisième section du fichier struts-config.xml est celui du fichier des messages :

        <message-resources 
          parameter="ApplicationResources"
            null="false"
      />    

Le fichier ApplicationResources.properties est placé dans WEB-INF/classes. Il sera vide. Même vide, il doit cependant être déclaré dans le fichier de configuration sinon la bibliothèquede balises struts-bean que nous verrons un peu plus loin génère une erreur. Cette bibliothèque est utilisée par la vue confirmation.jsp.

6.4. Le code des vues

6.4.1. La vue formulaire.jsp

Rappelons que cette vue est affichée dans deux cas :

  • à l'appel de l'action /init lors du 1er cycle demande-réponse
  • à l'appel de l'action /affiche lors des cycles suivants

Le code de la vue formulaire.jsp est le suivant :

<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>

<html>
  <head>
      <title>formulaire</title>
  </head>  
  <body background='<html:rewrite page="/images/standard.jpg"/>'>
      <h3>Formulaire Struts</h3>
    <hr>
    <html:form action="/confirmation" name="dynaFormulaire" type="istia.st.struts.formulaire.DynaFormulaire">
      <table border="0">
        <tr>
          <td>bouton radio</td>
          <td>
                        <html:radio name="dynaFormulaire" property="opt" value="oui">Oui</html:radio>
                        <html:radio name="dynaFormulaire" property="opt" value="non">Non</html:radio>                                            
          </td>
        </tr>
        <tr>
          <td>Cases à cocher</td>
          <td>
                        <html:checkbox name="dynaFormulaire" property="chk1">1</html:checkbox>
                        <html:checkbox name="dynaFormulaire" property="chk2">2</html:checkbox>
                        <html:checkbox name="dynaFormulaire" property="chk3">3</html:checkbox>                                                
          </td>
        </tr>
        <tr>
          <td>Champ de saisie</td>
          <td>
                        <html:text name="dynaFormulaire" property="champSaisie" />
          </td>
        </tr>
        <tr>
          <td>Mot de passe</td>
          <td>
              <html:password name="dynaFormulaire" property="mdp" />
          </td>
        </tr>
        <tr>
          <td>Boîte de saisie multilignes</td>
          <td>
              <html:textarea name="dynaFormulaire" property="boiteSaisie" />
          </td>
        </tr>
        <tr>
          <td>Combo</td>
          <td>
              <html:select name="dynaFormulaire" property="combo">
                            <html:options name="dynaFormulaire" property="valeursCombo"/>
                        </html:select>
          </td>
        </tr>
        <tr>
          <td>
                        <table>
                            <tr>
                                <td>Liste à sélection unique</td>
                            </tr>
                            <tr>
                                <td>
                                    <input type="button" value="Effacer" onclick="this.form.listeSimple.selectedIndex=-1"/>
                                </td>
                            </tr>
                        </table>
          <td>
              <html:select name="dynaFormulaire" property="listeSimple" size="3">
                            <html:options name="dynaFormulaire" property="valeursListeSimple"/>                        
                        </html:select>
          </td>
        </tr>
        <tr>
                    <td>
                        <table>
                            <tr>
                                <td>Liste à sélection multiple</td>
                            </tr>
                            <tr>
                                <td>
                                    <input type="button" value="Effacer" onclick="this.form.listeMultiple.selectedIndex=-1"/>                                
                                </td>
                            </tr>
                        </table>
                    </td>
          <td>
              <html:select name="dynaFormulaire" property="listeMultiple" size="5" multiple="true">
                            <html:options name="dynaFormulaire" property="valeursListeMultiple"/>                        
                        </html:select>
          </td>
        </tr>
      </table>
      <html:hidden name="dynaFormulaire" property="secret"/>
            <br>
            <hr>
            <html:submit>Envoyer</html:submit> 
    </html:form>
  </body>
</html>

Cette page JSP utilise des balises provenant de la bibliothèque struts-html. Rappelons que pour utiliser une bibliothèque de balises, il faut :

  • la déclarer dans le fichier web.xml de l'application avec une balise <tag-lib>
  <taglib>
      <taglib-uri>/WEB-INF/struts-html.tld</taglib-uri>
    <taglib-location>/WEB-INF/struts-html.tld</taglib-location>
  </taglib>
  • placer le code de cette bibliothèque quelque-part dans l'arborescence de l'application, ici WEB-INF/struts-html.tld
  • déclarer l'utilisation de cette bibliothèque au début des pages JSP qui l'utilisent :
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>

La vue formulaire.jsp utilise des balises que nous expliquons maintenant :

balise

<body background="<html:rewrite page="/images/standard.jpg"/>">

traduction HTML
<body background="/formulaire2/images/standard.jpg">
explication

la balise html:rewrite permet de s'affranchir du nom de l'application dans des URL. Elle a un attribut :

page

URL à réécrire

Ainsi ci-dessus, si on décide d'appeler l'application formulaire3, le code de l'attribut background n'a pas à être réécrit. La balise html:rewrite génèrera le nouveau code HTML

background="/formulaire3/images/standard.jpg"

balise

<html:form action="/confirmation" name="dynaFormulaire" type="istia.st.struts.formulaire.DynaFormulaire">

traduction HTML
<form name="dynaFormulaire" method="post" action="/formulaire2/confirmation.do">
explication

la balise html:form permet de générer la balise HTML form. Elle a plusieurs attributs :

action

action Struts à laquelle doit être posté le formulaire - doit correspondre à l'une des actions définies dans struts-config.xml

name

facultatif - nom du bean ActionForm ou dérivé dans lequel devront être placées ou lues les valeurs du formulaire posté. En l'absence de cet attribut, c'est le formulaire associé à l'action définie par l'attribut action qui est utilisé. Cette information est trouvée dans le fichier de configuration (attribut name de l'action).

type

facultatif - classe Java à instancier pour le formulaire. En l'absence de cet attribut, c'est la classe associée à l'action définie par l'attribut action qui est utilisée. Cette information est trouvée dans le fichier de configuration (attribut type de l'action).

On voit que par défaut, le code HTML généré utilise la méthode POST. Dans ce même code HTML, l'URL de l'action a été réécrite pour être préfixée par le nom de l'application et suffixée par .do.

balise

<html:radio name="dynaFormulaire" property="opt" value="oui">Oui</html:radio>

traduction HTML
<input type="radio" name="opt" value="oui">
explication

la balise html:radio sert à générer la balise HTML <input type="radio" ...>. Elle admet divers attributs :

name

facultatif - nom du bean ActionForm ou dérivé dans lequel devra être placée ou lue la valeur du champ. En l'absence de cet attribut, c'est le formulaire associé à la balise <html:form> qui est utilisé.

property

nom du champ du bean formulaire associé au bouton radio en lecture et écriture.

value

facultatif - valeur à donner au champ HTML

Le texte entre la balise de début et de fin est le texte qui sera affiché à côté du bouton radio.

balise

<html:checkbox name="dynaFormulaire" property="chk1">1</html:checkbox>

traduction HTML
<input type="checkbox" name="chk1" value="on">
explication

la balise html:checkbox sert à générer la balise HTML <input type="checkbox" ...>. Elle admet divers attributs :

name

facultatif - nom du bean ActionForm ou dérivé dans lequel devra être placée ou lue la valeur du champ. En l'absence de cet attribut, c'est le formulaire associé à la balise <html:form> qui est utilisé.

property

nom du champ du bean formulaire associé à la case à cocher en lecture et écriture.

value

facultatif - valeur à donner au champ HTML

Le texte entre la balise de début et de fin est le texte qui sera affiché à côté de la case à cocher.

balise

<html:text name="dynaFormulaire" property="champSaisie" />

traduction HTML
<input type="text" name="champSaisie" value="">
explication

la balise html:text sert à générer la balise HTML <input type="text" ...>. Elle admet divers attributs :

name

facultatif - nom du bean ActionForm ou dérivé dans lequel devra être placée ou lue la valeur du champ. En l'absence de cet attribut, c'est le formulaire associé à la balise <html:form> qui est utilisé.

property

nom du champ du bean formulaire associé au champ de saisie en lecture et écriture.

value

facultatif - valeur à donner au champ HTML

balise

<html:password name="dynaFormulaire" property="mdp" />

traduction HTML
<input type="password" name="mdp" value="">
explication

la balise html:password sert à générer la balise HTML <input type="password" ...>. Elle admet divers attributs :

balise

<html:textarea name="dynaFormulaire" property="boiteSaisie" />

traduction HTML
<textarea name="boiteSaisie"></textarea>
explication

la balise html:textarea sert à générer la balise HTML <textarea>...</textarea>. Elle admet divers attributs :

name

facultatif - nom du bean ActionForm ou dérivé dans lequel devra être placée ou lue la valeur du champ. En l'absence de cet attribut, c'est le formulaire associé à la balise <html:form> qui est utilisé.

property

nom du champ du bean formulaire associé au champ mot de passe en lecture et écriture.

value

facultatif - valeur à donner au champ HTML

balise

<html:select name="dynaFormulaire" property="combo">....</html:select>

traduction HTML
<select name="combo">...</select>
explication

la balise html:select sert à générer la balise HTML <select>...</select>. Elle admet divers attributs :

name

facultatif - nom du bean ActionForm ou dérivé dans lequel devra être placée ou lue la valeur du champ. En l'absence de cet attribut, c'est le formulaire associé à la balise <html:form> qui est utilisé.

property

nom du champ du bean formulaire associé au champ de saisie en lecture et écriture.

value

facultatif - valeur à donner au champ HTML. L'option du combo ayant cette valeur sera sélectionnée.

balise

<html:select name="dynaFormulaire" property="combo">

<html:options name="dynaFormulaire" property="valeursCombo"/>

</html:select>

traduction HTML
<option value="combo1">combo1</option>
<option value="combo2">combo2</option>
explication

la balise html:options sert à générer les balises HTML <option>...</option> à l'intérieur d'une balise HTML <select>. Il y a diverses façons de préciser comment trouver les valeurs de remplissage du select. Ici, nous avons utilisé les attributs name et property :

name

facultatif - nom du bean ActionForm ou dérivé dans lequel devra être placée ou lue la valeur du champ. En l'absence de cet attribut, c'est le formulaire associé à la balise <html:form> qui est utilisé.

property

nom du champ du bean formulaire qui contient les valeurs du select. Ce doit être un tableau de chaînes de caractères.

Les deux autres listes sont générées de façon analogue à la précédente :

          <html:select name="dynaFormulaire" property="listeSimple" size="3">
            <html:options name="dynaFormulaire" property="valeursListeSimple"/>            
        </html:select>

Ci-dessus, on précise un attribut size différent de 1 pour avoir une liste au lieu d'un combo.

<html:select name="dynaFormulaire" property="listeMultiple" size="5" multiple="true">
        <html:options name="dynaFormulaire" property="valeursListeMultiple"/>                
    </html:select>

Ci-dessus, on précise l'attribut multiple="true" pour avoir une liste à sélection multiple.

balise

<html:hidden name="dynaFormulaire" property="secret"/>

traduction HTML
<input type="hidden" name="secret" value="xxx">
explication

la balise html:hidden sert à générer la balise HTML <input type="hidden" ...>.

name

facultatif - nom du bean ActionForm ou dérivé dans lequel devra être placée ou lue la valeur du champ. En l'absence de cet attribut, c'est le formulaire associé à la balise <html:form> qui est utilisé.

property

nom du champ du bean formulaire associé au champ caché. La valeur "xxx" attribuée au champ HTML secret vient de la définition du formulaire dans le fichier de configuration. On y a défini une valeur initiale "xxx" pour le champ secret.

Pour bien comprendre le lien entre la vue formulaire.jsp et le bean dynaFormulaire qui la représente en mémoire, il faut se rappeler que le bean dynaFormulaire s'utilise à la fois en lecture et écriture :

La requête a lieu lorsque l'utilisateur clique sur le bouton [Envoyer] du formulaire. Le navigateur "poste" alors le formulaire HTML à l'action /confirmation. Nous avons déjà expliqué ce qui se passait alors et notamment que les champs de dynaFormulaire allaient recevoir les valeurs des champs de même nom du formulaire HTML.

Que se passe-t-il lorsque le contrôleur demande l'affichage de la vue formulaire.jsp en réponse à une requête ? Reconsidérons les balises une à une :

balise

<body background="<html:rewrite page="/images/standard.jpg"/>">

fonction

génère le code HTML

<body background="/formulaire2/images/standard.jpg">
balise

<html:form action="/confirmation" name="dynaFormulaire" type="istia.st.struts.formulaire.DynaFormulaire">

...

</html:form>

fonction

génère le code HTML

<form name="dynaFormulaire" method="post" action="/formulaire2/confirmation.do">
balise

<html:radio name="dynaFormulaire" property="opt" value="oui">Oui</html:radio>

<html:radio name="dynaFormulaire" property="opt" value="non">Non</html:radio>

fonction

Si le champ opt de dynaFormulaire vaut "oui", génère le code HTML

<input type="radio" name="opt" value="oui" checked="checked">Oui
<input type="radio" name="opt" value="non">Non
balise

<html:checkbox name="dynaFormulaire" property="chk1">1</html:checkbox>

<html:checkbox name="dynaFormulaire" property="chk2">2</html:checkbox>

<html:checkbox name="dynaFormulaire" property="chk3">3</html:checkbox>

fonction

Si les champs chk1 et chk3 de dynaFormulaire valent "on" et si le champ chk2 vaut "off", génère le code HTML

<input type="checkbox" name="chk1" value="on" checked="checked">1
<input type="checkbox" name="chk2" value="on">2
<input type="checkbox" name="chk3" value="on" checked="checked">3                                                
balise

<html:text name="dynaFormulaire" property="champSaisie" />

fonction

si le champ champSaisie vaut "ceci est un essai", génère le code HTML

<input type="text" name="champSaisie" value="ceci est un essai">
balise

<html:password name="dynaFormulaire" property="mdp" />

fonction

si le champ mdp vaut "azerty", génère le code HTML

<input type="password" name="mdp" value="azerty">
balise

<html:password name="dynaFormulaire" property="mdp" />

fonction

si le champ mdp vaut "azerty", génère le code HTML

<input type="password" name="mdp" value="azerty">
balise

<html:password name="dynaFormulaire" property="mdp" />

fonction

si le champ mdp vaut "azerty", génère le code HTML

<input type="password" name="mdp" value="azerty">
balise

<html:select name="dynaFormulaire" property="combo">

<html:options name="dynaFormulaire" property="valeursCombo"/>

</html:select>

fonction

si le champ combo vaut "combo2", génère le code HTML

<select name="combo">
    <option value="combo0">combo0</option>
    <option value="combo1">combo1</option>
    <option value="combo2" selected="selected">combo2</option>
    <option value="combo3">combo3</option>
    <option value="combo4">combo4</option>
</select>
balise

<html:select name="dynaFormulaire" property="listeSimple" size="3">

<html:options name="dynaFormulaire" property="valeursListeSimple"/>

</html:select>

fonction

si le champ listeSimple vaut "simple1", génère le code HTML

<select name="listeSimple" size="3">
    <option value="simple0">simple0</option>
    <option value="simple1" selected="selected">simple1</option>
    <option value="simple2">simple2</option>
...
</select>
balise

<html:select name="dynaFormulaire" property="listeMultiple" size="5" multiple="true">

<html:options name="dynaFormulaire" property="valeursListeMultiple"/>

</html:select>

fonction

si le champ listeMultiple est le tableau {"multiple0","multiple2"}, génère le code HTML

<select name="listeMultiple" multiple="multiple" size="5">
    <option value="multiple0" selected="selected">multiple0</option>
    <option value="multiple1">multiple1</option>
    <option value="multiple2" selected="selected">multiple2</option>
    <option value="multiple3">multiple3</option>
...
</select>
balise

<html:hidden name="dynaFormulaire" property="secret"/>

fonction

si le champ secret a la valeur "xxx", génère le code HTML

<input type="hidden" name="secret" value="xxx">
balise

<html:submit>Envoyer</html:submit>

fonction

génère le code HTML

<input type="submit" value="Envoyer">

La dernière chose à expliquer est le code javascript inclus dans la page JSP et lié aux deux boutons [Effacer] qui désélectionnent les éléments sélectionnés dans les listes listeSimple et listeMultiple :

<input type="button" value="Effacer" onclick="this.form.listeSimple.selectedIndex=-1"/>                                
<input type="button" value="Effacer" onclick="this.form.listeMultiple.selectedIndex=-1"/>

La balise

<html:form action="/confirmation" name="dynaFormulaire" type="istia.st.struts.formulaire.DynaFormulaire">

donne naissance au code HTML suivant :

<form name="dynaFormulaire" method="post" action="/formulaire2/confirmation.do">

Pour comprendre le code Javascript lié aux boutons [Effacer], rappelons comment sont désignés les différents éléments d'un document web au sein d'un code Javascript exploitant ce document :

Donnée

Signification

document

désigne la totalité du document web

document.forms

désigne la collection des formulaires définis dans le document

document.forms[i]

désigne le formulaire n° i du document

document.forms["nomFormulaire"]

désigne le formulaire <form> ayant l'attribut name égal à "nomFormulaire"

document.nomFormulaire

désigne le formulaire <form> ayant l'attribut name égal à "nomFormulaire"

document.[formulaire].elements

désigne la collection des éléments membres du formulaire désigné par l'expression [formulaire]. Cette collection comprend toutes les balises <input>, <textarea>, <select> du formulaire désigné.

document.[formulaire].elements[i]

désigne l'élément n° i de [formulaire]

document.[formulaire].elements["nomComposant"]

désigne l'élément de [formulaire] ayant l'attribut name égal à nomComposant

document.[formulaire]. nomComposant

désigne l'élément de [formulaire] ayant l'attribut name égal à nomComposant

document.[formulaire].[composant].value

désigne la valeur du composant [composant] du formulaire [formulaire] lorsque le code HTML de celui-ci peut avoir un attribut value (<input>, <textarea>)

document.[formulaire].[select].selectedIndex

désigne l'indice de l'option sélectionnée dans une liste. S'utilise en lecture et écriture. Mettre cette propriété à -1 désélectionne tous les éléments de la liste.

document.[formulaire].[select].options

désigne le tableau des options associées à une balise <select>

document.[formulaire].[select].options[i]

désigne l'option n° i de la balise <select> indiquée

document.[formulaire].[select].options[i].selected

booléen indiquant si l'option n° i de la balise [select] indiquée est sélectionnée (true) ou non. Peut s'utiliser en lecture et écriture

Revenons sur le code javascript des deux boutons :

<input type="button" value="Effacer" onclick="this.form.listeSimple.selectedIndex=-1"/>                                
<input type="button" value="Effacer" onclick="this.form.listeMultiple.selectedIndex=-1"/>

Sur l'événement clic sur le bouton, le code associé à l'attribut "onclick" est exécuté. Ici, c'est un code en ligne. Le plus souvent, on écrit onclick="fonction(...)" où fonction est une fonction définie au sein d'une balise <script language="javascript">...</script>. Que fait le code ci-dessus ? Commentons le code du premier bouton :

this

désigne le document web dans lequel se trouve le bouton

this.form

désigne le formulaire dans lequel se trouve le bouton

this.form.listeSimple

désigne le composant listeSimple du formulaire

this.form.listeSimple.selectedIndex

désigne l'indice de l'option sélectionnée dans listeSimple. Mettre à -1 cette propriété entraîne la désélection de toute option.

6.4.2. La vue confirmation.jsp

Rappelons que cette vue est affichée à l'issue de l'action /confirmation, c.a.d. après que le formulaire contenu dans la vue formulaire.jsp ait été posté par le client web. Elle a pour seul but d'afficher les valeurs saisies par l'utilisateur. Son code est le suivant :

<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>

<html>
    <head>
      <title>Confirmation</title>
  </head>
  <body background="<html:rewrite page="/images/standard.jpg"/>">
      <h3>Confirmation des valeurs saisies</h3>
    <hr/>
    <table border="1">
        <tr>
        <td>Bouton radio</td>
        <td><bean:write name="dynaFormulaire" scope="session" property="opt"/></td>
      </tr>
        <tr>
        <td>Case à cocher chk1</td>
        <td><bean:write name="dynaFormulaire" scope="session" property="chk1"/></td>
      </tr>
        <tr>
        <td>Case à cocher chk2</td>
        <td><bean:write name="dynaFormulaire" scope="session" property="chk2"/></td>
      </tr>
        <tr>
        <td>Case à cocher chk3</td>
        <td><bean:write name="dynaFormulaire" scope="session" property="chk3"/></td>
      </tr>

        <tr>
        <td>Champ de saisie</td>
        <td><bean:write name="dynaFormulaire" scope="session" property="champSaisie"/></td>
      </tr>
        <tr>
        <td>Mot de passe</td>
        <td><bean:write name="dynaFormulaire" scope="session" property="mdp"/></td>
      </tr>

        <tr>
        <td>Boîte de saisie</td>
        <td><bean:write name="dynaFormulaire" scope="session" property="boiteSaisie"/></td>
      </tr>

        <tr>
        <td>combo</td>
        <td><bean:write name="dynaFormulaire" scope="session" property="combo"/></td>
      </tr>
        <tr>
        <td>liste simple</td>
        <td><bean:write name="dynaFormulaire" scope="session" property="listeSimple"/></td>
      </tr>
            <logic:iterate id="choix" indexId="index" name="dynaFormulaire" property="listeMultiple">
          <tr>
          <td>liste multiple[<bean:write name="index"/>]</td>
          <td><bean:write name="choix"/></td>
        </tr>
            </logic:iterate>
    </table>
        <br>
    <html:link page="/affiche.do">
            Retour au formulaire
        </html:link>    
  </body>
</html>

Nous introduisons ici deux nouvelles bibliothèques de balises : struts-bean et struts-logic. La bibliothèque struts-bean permet d'avoir accès à des objets placés dans la requête, la session ou le contexte de l'application. La bibliothèque struts-logic permet d'introduire de la logique d'exécution au moyen de balises. Ces deux bibliothèques ne sont nullement indispensables. Comme nous l'avons vu, une page JSP peut :

  • récupérer des objets dans la requête (request.getAttribute(...)), la session (session.getAttribute(...), ou le contexte de l'application
  • inclure des parties dynamiques dans le code HTML au moyen de variables <%= variable %>
  • comporter du code Java <% code Java %>

L'inclusion de code Java dans des pages JSP gêne tous ceux qui voudraient une stricte séparation entre logique applicative (code java) et présentation (utilisation de balises). Aussi a-t-on inventé pour eux des bibliothèques de balises.

Nous allons procéder comme pour la vue formulaire.jsp et expliquer chacune des balises présentes dans le code de confirmation.jsp si elles n'ont pas été déjà rencontrées dans la vue formulaire.jsp. Tout d'abord, remarquons que la page commence par déclarer les trois bibliothèques de balises qu'elle va utiliser :

<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>

Rappelons aussi que ces trois bibliothèques doivent être déclarées dans le fichier web.xml de l'application. Nous commentons maintenant les balises du document formulaire.jsp :

balise
<bean:write name="dynaFormulaire" scope="session" property="opt"/>
fonction

écrit une valeur dans le flux HTML courant. La balise bean:write admet les attributs suivants :

name : nom de l'objet à utiliser

scope : portée (request, session, context) dans laquelle chercher cet objet

property : champ de l'objet désigné par name dont il faut écrire la propriété. Ce champ peut être un objet de tout type. Ce sera la méthode toString de l'objet qui sera utilisée.

Ici, la valeur du champ opt de dynaFormulaire est écrite. On aura soit "oui" si l'utilisateur a coché le bouton radio ayant l'attribut value="oui", soit "non" s'il a coché le bouton radio ayant l'attribut value="non"

balise
<bean:write name="dynaFormulaire" scope="session" property="chk1"/>
fonction

écrit la valeur du champ chk1 de dynaFormulaire. On aura soit "on" si l'utilisateur a coché la case, soit "off" sinon. Idem pour chk2, chk3.

balise
<bean:write name="dynaFormulaire" scope="session" property="champSaisie"/>
fonction

écrit la valeur du champ champSaisie de dynaFormulaire, c'est à dire le texte tapé par l'utilisateur dans ce champ. Idem pour mdp, boiteSaisie.

balise
<bean:write name="dynaFormulaire" scope="session" property="combo"/>
fonction

écrit la valeur du champ combo de dynaFormulaire. On aura l'attribut value de l'élément <option> sélectionné par l'utilisateur.

balise
<bean:write name="dynaFormulaire" scope="session" property="listeSimple"/>
fonction

écrit la valeur du champ listeSimple de dynaFormulaire. On aura l'attribut value de l'élément <option> sélectionné par l'utilisateur s'il y en a eu un. Sinon on aura la chaîne vide.

balise
            <logic:iterate id="choix" indexId="index" name="dynaFormulaire" property="listeMultiple">
          <tr>
          <td>liste multiple[<bean:write name="index"/>]</td>
          <td><bean:write name="choix"/></td>
        </tr>
            </logic:iterate>
fonction

Ici, nous introduisons des balises de logique. Nous avons affaire à une liste à choix multiple. La valeur du champ listeMultiple de l'objet dynaFormulaire est un tableau de String. En java, nous écririons une boucle. La balise logic:iterate nous permet de faire cette même boucle sans écrire de code Java. La balise logic:iterate a dans l'exemple les attributs suivants :

name="dynaFormulaire" : nom de l'objet à utiliser

property="listeMultiple" : nom de la propriété qui dans l'objet indiqué par name contient la collection qui va être parcourue dans la boucle. Ici cette collection est le tableau des valeurs sélectionnées dans listeMultiple. Ce tableau peut être vide.

id="choix" : identificateur désignant l'élément courant du tableau à chaque tour de boucle. Lors de la 1ère itération, choix représentera listeMultiple[0], à la seconde listeMultiple[1] et ainsi de suite.

indexID="index" : identificateur désignant l'indice de l'élément courant du tableau à chaque tour de boucle. Lors de la 1ère itération, index aura la valeur 0, à la seconde la valeur 1 et ainsi de suite.

Le code HTML contenu entre les balises <logic:iterate ...> et </logic:iterate> est répété pour chaque élément de la collection désignée par le couple (name,property). La partie dynamique de ce code est le suivant :

          <td>liste multiple[<bean:write name="index"/>]</td>
          <td><bean:write name="choix"/></td>

De ce qui a été dit précédemment, à l'itération n° i (i>=0), le code HTML généré est équivalent au code suivant :

          <td>liste multiple[<%=i%>]</td>
          <td><%=listeMultiple[i]%></td>
balise
    <html:link page="/affiche.do">
            Retour au formulaire
        </html:link>    
fonction

génère un lien relatif au contexte de l'application, ce qui évite de connaître celui-ci. Le code HTML généré par cette balise est le suivant :

<a href="/formulaire2/affiche.do">Retour au formulaire</a>

6.5. Les classes Java

Le fichier de configuration struts-config.xml référence deux classes Java :

        <form-bean name="dynaFormulaire" type="istia.st.struts.formulaire.DynaFormulaire">
...
      <action
          path="/init"
            name="dynaFormulaire"
            validate="false" 
            scope="session"
          type="istia.st.struts.formulaire.InitFormulaireAction"
      >

La classe DynaFormulaire est la classe qui contiendra les valeurs de la vue n° 1 formulaire.jsp. La classe InitFormulaireAction est la classe qui traitera les valeurs du formulaire posté par le bouton [Envoyer] de formulaire.jsp.

6.5.1. La classe DynaFormulaire

Pour contenir les valeurs d'un formulaire, un objet de type DynaActionForm est suffisant sauf si on a besoin de redéfinir l'une des méthodes reset ou validate de cette classe. Ici, la méthode validate n'a pas besoin d'être redéfinie puisqu'on ne fait aucune validation de données. Mais la méthode reset a elle besoin d'être redéfinie. En effet, les champs de l'objet DynaFormulaire vont recevoir leurs valeurs du formulaire posté par le client web. Or certains champs peuvent ne pas recevoir de valeur s'ils ne sont pas présents dans la requête. Cela se produit dans les cas suivants :

  • une case à cocher qui n'a pas été cochée par l'utilisateur
  • une liste de taille supérieure à 1 ou aucune option n'a été sélectionnée

Pour des formulaires ayant ce type de composant, la méthode reset doit

  • affecter la valeur "off" au champ associé à la case à cocher
  • affecter la chaîne vide au champ associé à une liste à sélection unique
  • affecter un tableau de taille nulle de chaînes de caractères au champ associé à une liste à sélection multiple

Ainsi, si ces champs ne reçoivent pas de valeur de la requête, ils conservent la valeur attribuée par reset, valeur qui correspond à l'état du composant dans le formulaire validé par l'utilisateur (case non cochée, liste avec aucun élément sélectionné).

Le code de la classe DynaFormulaire, classe dérivée de DynaActionForm est le suivant :

package istia.st.struts.formulaire;

import org.apache.struts.action.DynaActionForm;
import org.apache.struts.action.ActionMapping;
import javax.servlet.http.HttpServletRequest;

public class DynaFormulaire extends DynaActionForm {
  public void reset(ActionMapping mapping, HttpServletRequest request){
    // reset des cases à cocher - valeur off
    set("chk1","off");
    set("chk2","off");
    set("chk2","off");
    // reset listeSimple - chaîne vide
    set("listeSimple","");
    // reset listeMultiple - tableau vide
    set("listeMultiple",new String[]{});
  }
}

6.5.2. La classe InitFormulaireAction

La classe InitFormulaireAction est, dans le fichier struts-config.xml, associée à l'action /init :

      <action
          path="/init"
            name="dynaFormulaire"
            validate="false" 
            scope="session"
          type="istia.st.struts.formulaire.InitFormulaireAction"
      >
            <forward name="afficherFormulaire" path="/vues/formulaire.jsp"/>
        </action>

L'action /init n'est utilisée qu'une fois lors de la construction initiale de l'objet DynaFormulaire. Elle a pour but de fournir du contenu aux trois listes du formulaire combo, listeSimple, listeMultiple. Ce contenu est fourni sous forme de trois tableaux, propriétés de l'objet dynaFormulaire :

        <form-bean name="dynaFormulaire" type="istia.st.struts.formulaire.DynaFormulaire">
            <form-property name="opt" type="java.lang.String" initial="non"/>
...
            <form-property name="valeursCombo" type="java.lang.String[]" />
            <form-property name="valeursListeSimple" type="java.lang.String[]" />
            <form-property name="valeursListeMultiple" type="java.lang.String[]"/>                                          
        </form-bean>            

Une fois que les tableaux valeursCombo, valeursListeSimple, valeursListeMultiple auront été initialisés par InitFormulaireAction, ils n'auront plus besoin de l'être. En effet, l'objet dynaFormulaire est placé dans la session et conserve donc sa valeur au fil des cycles demande-réponse. C'est pourquoi l'action /init n'est exécutée qu'une fois. Le code de InitFormulaireAction est le suivant :

package istia.st.struts.formulaire;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import org.apache.struts.action.*;

public class InitFormulaireAction
  extends Action {
  public ActionForward execute(ActionMapping mapping, ActionForm form,
       HttpServletRequest request, HttpServletResponse response) throws IOException,    ServletException {

    // prépare le formulaire à afficher

    // on met les informations nécessaires au formulaire dans son bean
    DynaFormulaire formulaire = (DynaFormulaire) form;
    formulaire.set("valeursCombo", getValeurs(5, "combo"));
    formulaire.set("valeursListeSimple", getValeurs(7, "simple"));
    formulaire.set("valeursListeMultiple", getValeurs(10, "multiple"));
    // on rend la main
    return mapping.findForward("afficherFormulaire");
  } //execute

  // liste des valeurs du combo
  private String[] getValeurs(int taille, String label) {
    String[] valeurs = new String[taille];
    for (int i = 0; i < taille; i++) {
      valeurs[i] = label + i;
    }
    return valeurs;
  }
}
  • la classe dérive de la classe Action. C'est obligatoire.
  • le contrôleur Struts utilise un objet Action via sa méthode execute. C'est donc elle qu'il faut redéfinir. Cette méthode reçoit les pramètres suivants :
    • ActionMapping mapping : un objet représentant la configuration de l'aplication dans struts-config.xml
    • ActionForm form : le formulaire associé à l'action s'il y en a un défini dans la configuration de l'action (attribut name de l'action).
    • HttpServletRequest request : la requête du client
    • HttpServletResponse : la réponse au client
  • la classe InitFormulaireAction doit initialiser le formulaire dynaFormulaire. Celui arrive à la méthode execute sous la forme du paramètre ActionForm form. Rappelons que dynaFormulaire est de type DynaFormulaire, classe dérivée de la classe DynaActionForm elle-même dérivée de la classe ActionForm.
  • dans la méthode execute, des valeurs sont affectées aux trois champs valeursCombo, valeursListeSimple, valeursListeMultiple grâce à la méthode set de la classe DynaActionForm. Ces valeurs sont des tableaux arbitraires par souci de simplicité. Remarquons que la méthode set affecte une valeur à un champ existant. Elle ne peut pas servir à créer de nouveaux champs. C'est pourquoi, il est nécessaire de définir les trois champs valeursCombo, valeursListeSimple, valeursListeMultiple dans la définition de l'objet dynaFormulaire dans struts-config.xml.
  • la méthode execute se termine en renvoyant au contrôleur la clé de la vue à afficher comme réponse au client. Ici, c'est la clé afficherFormulaire qui dans le fichier struts-config.xml a été associée à la vue /vues/formulaire.jsp.

6.6. Déploiement

L'arborescence de l'application est la suivante :

  

Image

Image

Rappelons que le fichier ApplicationResources.properties ci-dessus est nécessaire à la bibliothèque de balises struts-bean. On sait que ce fichier contient les messages de l'application. Ceux-ci sont accessibles à la bibliothèque struts-bean. Ici, notre application ne définit aucun message. Aussi le fichier ApplicationResources.properties existe mais est vide.

6.7. Conclusion

Nous avons détaillé dans cette leçon la façon de gérer les différents composants d'un formulaire HTML. Nous pouvons maintenant utiliser des formulaires complexes dans nos applications Struts.