IX. Exemple 07 - Les balises de formulaire▲
Nous allons construire et exploiter le formulaire suivant :
Nous allons étendre le concept d'injection de paramètres vu pour un champ de saisie texte aux autres éléments Html d'un formulaire.
IX-A. Le projet Netbeans▲
- en [1], les vues du projet [Formx.jsp]
- en [2], le fichier de configuration de Struts et les messages internationalisés
- en [3], les actions [Formx.java] et un autre fichier de configuration de Struts.
IX-B. Configuration de Struts▲
Le fichier [struts.xml] est le suivant :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd"
>
<struts>
<constant
name
=
"struts.custom.i18n.resources"
value
=
"messages"
/>
<include
file
=
"example/example.xml"
/>
<package
name
=
"default"
namespace
=
"/"
extends
=
"struts-default"
>
<default-action-ref
name
=
"index"
/>
<action
name
=
"index"
>
<result
type
=
"redirectAction"
>
<param
name
=
"actionName"
>
Form</param>
<param
name
=
"namespace"
>
/example</param>
</result>
</action>
</package>
</struts>
- ligne 9 : inclut un autre fichier de configuration [example.xml]. C'est lui qui va configurer les actions.
- lignes 11-19 : le package default. Définit une adresse de redirection pour l'Url /. Elle sera redirigée vers l'Url /example/Form.action.
Le fichier [example.xml] qui configure les actions est le suivant :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd"
>
<struts>
<package
name
=
"example"
namespace
=
"/example"
extends
=
"struts-default"
>
<action
name
=
"Form"
class
=
"example.Form"
>
<result
name
=
"success"
>
/example/Form.jsp</result>
</action>
<action
name
=
"Form1"
class
=
"example.Form1"
>
<result
name
=
"success"
>
/example/Form1.jsp</result>
</action>
<action
name
=
"Form2"
class
=
"example.Form2"
>
<result
name
=
"success"
>
/example/Form2.jsp</result>
</action>
</package>
</struts>
- lignes 7-17 : définissent les actions /example/Form, /example/Form1, /example/Form2 où /example est le namespace (ligne 7) et Formx les actions.
- lignes 8-10 : l'exécution de l'action Form produira l'affichage de la vue /example/Form.jsp.
- lignes 11-13 : l'exécution de l'action Form1 produira l'affichage de la vue /example/Form1.jsp.
- lignes 14-16 : l'exécution de l'action Form2 produira l'affichage de la vue /example/Form2.jsp.
IX-C. Les fichiers des messages▲
Nous ne nous attarderons pas sur l'internationalisation du projet. Nous l'avons déjà étudiée dans l'exemple 01. Nous donnons le contenu du fichier [messages.properties] afin que le lecteur puisse comprendre les vues qui vont suivre.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
Form.francais=Fran\u00E7ais
Form.anglais=Anglais
Form.titre=Struts 2 - les tags de formulaire
Form.message=Struts 2 - les tags de formulaire
Form.langues=langues
Form.textfield=1-textfield
Form.password=2-password
Form.textarea=3-textarea
Form.select1=4-select (multiple=false, size=1)
Form.select1.header=<-- select1 -->
Form.select2=5-select (multiple=false, size=3)
Form.select3=6-select (multiple=true, size=3)
Form.radio=7-radio
Form.checkbox=8-checkbox
Form.checkboxlist=9-checkboxlist
Form.hidden=10-hidden
Form.submitText=Valider
Form.buttonRazText=Raz
Confirmation.message=Confirmation des valeurs saisies
Confirmation.champ=champ
Confirmation.valeur=valeur
Confirmation.textfield=1-textfield
Confirmation.password=2-password
Confirmation.textarea=3-textarea
Confirmation.select1=4-select (multiple=false, size=1)
Confirmation.select2=5-select (multiple=false, size=3)
Confirmation.select3=6-select (multiple=true, size=3)
Confirmation.radio=7-radio
Confirmation.checkbox=8-checkbox
Confirmation.checkboxlist=9-checkboxlist
Confirmation.hidden=10-hidden
IX-D. La vue [Form.jsp] - partie saisie▲
Son apparence visuelle est la suivante :
C'est la suivante :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
<%@
page
contentType
=
"text/html; charset=UTF-8"
pageEncoding=
"UTF-8"
%>
<%@
taglib
prefix
=
"s"
uri
=
"/struts-tags"
%>
<html>
<head>
<title><s
:
text
name=
"Form.titre"
/></title>
<s
:
head
/>
</head>
<body background=
"<s:url value="
/ressources/standard.jpg
"/>"
>
<h2><s
:
text
name=
"Form.message"
/></h2>
<h3><s
:
text
name=
"Form.langues"
/></h3>
<ul>
<li>
<s
:
url
id=
"url"
action=
"Form"
>
<s
:
param
name=
"request_locale"
>
en</s
:
param
>
</s
:
url
>
<s
:
a
href=
"%{url}"
><s
:
text
name=
"Form.anglais"
/></s
:
a
>
</li>
<li>
<s
:
url
id=
"url"
action=
"Form"
>
<s
:
param
name=
"request_locale"
>
fr</s
:
param
>
</s
:
url
>
<s
:
a
href=
"%{url}"
><s
:
text
name=
"Form.francais"
/></s
:
a
>
</li>
</ul>
<s
:
form
name=
"formulaire"
>
<s
:
textfield
name=
"textfield"
key=
"Form.textfield"
/>
<s
:
password
name=
"password"
key=
"Form.password"
/>
<s
:
textarea
name=
"textarea"
key=
"Form.textarea"
cols=
"40"
rows=
"5"
/>
<s
:
select
name=
"select1"
list=
"select1Values"
size=
"1"
key=
"Form.select1"
headerValue=
"<-- select 1 -->"
headerKey=
"-1"
/>
<s
:
select
name=
"select2"
size=
"3"
list=
"select2Values"
key=
"Form.select2"
/>
<s
:
select
name=
"select3"
size=
"3"
list=
"select3Values"
key=
"Form.select3"
multiple=
"true"
/>
<s
:
radio
name=
"radio"
list=
"radioValues"
key=
"Form.radio"
/>
<s
:
checkbox
name=
"checkbox"
key=
"Form.checkbox"
/>
<s
:
checkboxlist
name=
"checkboxlist"
list=
"checkboxlistValues"
key=
"Form.checkboxlist"
/>
<s
:
hidden
name=
"hidden"
key=
"Form.hidden"
/>
<s
:
submit
key=
"Form.submitText"
name=
"submitText"
/>
</s
:
form
>
<hr/>
...
</body>
- le formulaire commence ligne 26 et se termine ligne 38. Ligne 26, l'attribut action est absent. Cet attribut précise à quelle action, les données du formulaire seront postées. En l'absence de l'attribut action elles seront postées à l'action qui a conduit à l'affichage de la vue, ici l'action [Form].
- ligne 27 : un champ de saisie. L'attribut key précise son libellé. L'attribut name est le nom du paramètre qui sera posté. La chaîne postée pour ce champ sera de la forme :
textfield=texte_saisi
Le nom du paramètre correspond à un champ de l'action [Form]. La valeur du champ de saisie texte_saisi sera ici injectée dans le champ textfield de l'action [Form]. Inversement, si la vue [Form.jsp] est affichée suite à l'action [Form], la valeur du champ de saisie sera celle du champ textfield de l'action [Form]. Ce champ est donc utilisé en lecture et écriture. La classe [Form] doit avoir les méthodes getTextField et setTextField pour cela.
Ce raisonnement est valable pour toutes les balises de saisie qui vont suivre. Nous ne le répéterons pas. - ligne 28 : saisie d'un mot de passe. La chaîne postée à l'action [Form] sera de la forme
password=mot_de_passe_saisi
Un champ password avec ses get / set doit exister dans l'action [Form]. - ligne 29 : saisie d'un texte multi-lignes dans une zone de 5 lignes et 40 colonnes. La chaîne postée à l'action [Form] sera de la forme
textarea=saisie
Un champ textarea avec ses get / set doit exister dans l'action [Form]. - ligne 30 : une liste déroulante (size=1) à sélection unique. Une seule valeur de la liste peut être sélectionnée. La chaîne postée à l'action [Form] sera de la forme :
select1=valeur_sélectionnée
Un champ select1 avec ses get / set doit exister dans l'action [Form].
L'attribut key est le libellé de la liste. L'attribut list définit la liste des valeurs à mettre dans le combo. Ici, la méthode getSelect1Values de l'action [Form] sera appelée pour remplir le combo. Cette méthode peut rendre une collection, un dictionnaire ou un tableau. Ici, elle rendra un tableau de chaînes de caractères [chaine1, chaine2, ...] qui générera le code Html suivant :
<option value="chaine1">chaine1</option>
<option value="chaine2">chaine2</option>
…
Le texte entre les balises <option>...</option> sera affiché dans le combo. L'attribut value désigne la valeur à poster si l'option est sélectionnée par l'utilisateur. Ici l'attribut value et le texte affiché dans le combo sont identiques. Le plus souvent, ce n'est pas le cas. Si la deuxième option est sélectionnée, la chaîne suivante sera postée à l'action [Form] :
select1=chaine2
Le champ [Form].select1 recevra alors la valeur chaine2. Inversement, si au moment de l'affichage du formulaire, ce champ vaut chaine3, alors visuellement, le 3ième élément
<option value="chaine3">chaine3</option>
apparaîtra sélectionné dans le combo.
L'attribut headerValue fixe le texte à afficher comme 1ère option du combo et headerKey la valeur à poster si cette option est choisie par l'utilisateur. - ligne 31 : là encore une liste de taille 3 (size=3) : contrairement à la précédente liste où un seul élément est visible, ici 3 éléments de la liste seront visibles. Le mode de sélection reste le même. On ne peut sélectionner qu'un élément.
- ligne 32 : de nouveau une liste mais à sélection multiple (multiple=true). Dans ce cas, la valeur postée à l'action [Form] sera de la forme :
select3=chaine2&select3=chaine5
si les options chaine2 et chaine5 ont été sélectionnées. Lorsque plusieurs valeurs sont postées pour un même paramètre, ici select3, l'action doit avoir un champ ayant le nom du paramètre et être de type tableau. Donc par exemple, ici l'action [Form] pourrait avoir un champ du genre :
String[] select3 ;
et les méthodes get / set qui vont avec. Le tableau select3 recevra alors le tableau de chaînes de caractères [chaine2,chaine5]. Inversement, à l'affichage de la vue [Form.jsp], les valeurs du champ [Form].select3 déterminent les options de la liste select3 qui apparaîtront sélectionnées. On est toujours en lecture / écriture. - ligne 33 : un groupe de boutons radio. Le libellé du groupe est fourni par l'attribut key. Les libellés des boutons individuels sont fournis par l'attribut list. La valeur de cet attribut est le nom d'une méthode de l'action [Form] qui doit rendre une collection, un dictionnaire ou un tableau. Ici, la méthode [Form].getRadioValues rendra un tableau de chaînes de caractères [chaine1,chaine2,...] qui génèrera les balises Html suivantes :
<input type="radio" name="radio" ... value="chaine1"/>chaine1
<input type="radio" name="radio" ... value="chaine2"/>chaine2
…
Si le bouton radio chaine2 est coché, la valeur postée sera
radio=chaine2
et cette valeur sera injectée dans le champ [Form].radio. - ligne 34 : une case à cocher. Si elle est cochée, la chaîne de paramètres suivante sera postée à l'action [Form]
checkbox=true
Si elle n'est pas cochée, aucune chaîne de paramètres n'est postée. Struts 2 a un mécanisme interne qui fait que tout se passe comme si la chaîne
checkbox=false
avait été postée. - ligne 35 : une liste de cases à cocher. On peut en cocher plusieurs. Le libellé du groupe est fourni par l'attribut key. Les libellés des cases à cocher individuelles sont fournis par l'attribut list. La valeur de cet attribut est le nom d'une méthode de l'action [Form] qui doit rendre une collection, un dictionnaire ou un tableau. Ici, la méthode [Form].getCheckboxlistValues rendra un tableau de chaînes de caractères [chaine1,chaine2,...] qui génèrera les balises Html suivantes :
<input type="checkbox" name="checkboxlist" ... value="chaine1"/>chaine1
<input type="checkbox" name="checkboxlist" ... value="chaine2"/>chaine2
…
Si l'utilisateur sélectionne les cases libellées chaine2 et chaine5, la chaîne de caractère suivante sera postée à l'action [Form] :
checkboxlist=chaine2&checkboxlist=chaine5
Le tableau [chaine2, chaine5] sera affecté au champ [Form].checkboxlist qui doit donc être de type tableau de chaînes de caractères comme pour les listes à sélection multiple. Inversement, à l'affichage de la vue [Form.jsp], les valeurs du champ [Form].checkboxlist déterminent les cases à cocher qui seront cochées. - ligne 36 : un champ caché. Un champ caché est un champ qui est posté sans être saisi par l'utilisateur. Sa valeur est fixé par le programmeur. Ici le champ caché prendra sa valeur du champ [Form].hidden. Cette même valeur sera postée sous la forme :
hidden=qqchose
et réaffecté au champ [Form].hidden. - ligne 37 : un bouton de type submit. Un clic sur ce bouton, postera toutes les valeurs du formulaire à l'action [Form]. La chaîne des paramètres postés ressemblera à ceci :
textfield=texte&password=&textarea=ligne1%0D%0Aligne2%0D%0A&select1=z
%C3%A9ro&select2=trois&select3=z
%C3%A9ro&select3=deux&__multiselect_select3=&radio=bleu&checkbox=true&__checkbox_
checkbox=true&checkboxlist=v
%C3%A9lo&checkboxlist=bus&__multiselect_checkboxlist=&hidden=initial&submitText=Valider
IX-E. L'action [Form] - partie saisie▲
Son code est le suivant :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
package
example;
import
com.opensymphony.xwork2.ActionSupport;
public
class
Form extends
ActionSupport {
// constructeur sans paramètre
public
Form
(
) {
}
// champs du formulaire
private
String textfield =
"texte"
;
private
String password =
"secret"
;
private
String textarea =
"ligne1
\n
ligne2
\n
"
;
private
String select1 =
"zéro"
;
private
String[] select1Values =
new
String[]{
"zéro"
, "un"
, "deux"
}
;
private
String select2 =
"trois"
;
private
String[] select2Values =
new
String[]{
"zéro"
, "un"
, "deux"
, "trois"
, "quatre"
}
;
private
String[] select3 =
new
String[]{
"zéro"
, "deux"
}
;
private
String[] select3Values =
new
String[]{
"zéro"
, "un"
, "deux"
, "trois"
, "quatre"
}
;
private
String radio =
"bleu"
;
private
String[] radioValues =
new
String[]{
"bleu"
, "blanc"
, "rouge"
}
;
private
Boolean checkbox =
false
;
private
String[] checkboxlist =
new
String[]{
"vélo"
, "bus"
}
;
private
String[] checkboxlistValues =
new
String[]{
"voiture"
, "tram"
, "vélo"
, "bus"
, "métro"
}
;
private
String hidden =
"initial"
;
private
String submitText;
// valeurs sélectionnées dans champ select3
public
String getSelect3SelectedValues
(
) {
return
getValue
(
select3);
}
// valeurs sélectionnées dans champ checkboxlist
public
String getCheckboxlistSelectedValues
(
) {
return
getValue
(
checkboxlist);
}
// méthode utilitaire
public
String getValue
(
String[] values) {
String result =
""
;
for
(
String value : values) {
result +=
" "
+
value;
}
return
result;
}
// getters et setters des champs
....
}
- ligne 1 : le champ associé à la balise textfield du formulaire. Servira à initialiser ce champ de saisie aussi bien qu'à en récupérer la valeur. Reçoit au moment du POST, la valeur saisie dans ce champ.
- ligne 2 : le champ associé à la balise password du formulaire. Reçoit au moment du POST, la valeur saisie dans ce champ.
- ligne 13 : associé à la balise textarea du formulaire. Reçoit au moment du POST, le texte saisi dans ce champ.
- ligne 14 : associé à la balise select1 du formulaire, une liste à choix unique. Sa valeur initiale sélectionne un élément du combo. C'est l'élément ayant son attribut value égal à cette valeur qui sera sélectionnée. Au moment du POST, reçoit l'attribut value de l'option sélectionnée dans le combo.
- ligne 15 : les chaînes de caractères qui vont alimenter le combo select1.
- lignes 16 et 17 : idem select1
- ligne 18 : associé à la balise select1 du formulaire, une liste à choix multiple. Les valeurs initiales dans le tableau sélectionnent les éléments correspondants de la liste. Ce sont les éléments ayant leur attribut value égal à l'un de ces éléments qui seront sélectionnés. Au moment du POST, reçoit les attributs value des différentes options sélectionnées dans le combo.
- ligne 19 : les chaînes de caractères qui vont alimenter la liste select3
- ligne 20 : associé à la balise radio du formulaire. Sa valeur sert à cocher l'un des boutons radio, celui qui a son attribut value égal à cette valeur. Reçoit au moment du POST, la valeur du bouton radio coché.
- ligne 21 : les différents libellés et valeurs des boutons radio.
- ligne 22 : associé à la balise checkbox du formulaire. Sa valeur initiale sert à cocher / décocher la case. Reçoit au moment du POST, la valeur true si la case a été cochée, false sinon.
- ligne 23 : associé à la balise checkboxlist du formulaire. Les valeurs initiales dans le tableau sélectionnent les cases à cocher correspondantes. Ce sont les cases ayant leur attribut value égal à l'un de ces valeurs initiales qui seront cochées. Au moment du POST, reçoit les attributs value des différentes cases cochées.
- ligne 24 : les chaînes de caractères qui vont alimenter les libellés des cases de checkboxlist.
- ligne 25 : associé au champ caché hidden. Sa valeur initiale va fixer la valeur du champ caché. Au moment du POST, recevra cette même valeur car l'utilisateur n'a pas la possibilité de la modifier.
- ligne 26 : associé à la balise submit. Au moment du POST, reçoit le libellé du bouton. On peut se passer de ce champ.
- les méthodes des lignes 29, 34 et 39 seront commentées un peu plus loin.
IX-F. La vue [Form.jsp] - partie Confirmation▲
La vue [Form.jsp] a une partie " Confirmation des saisies " qui n'a pas encore été vue :
Son code est le suivant :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
<%@
page
contentType
=
"text/html; charset=UTF-8"
pageEncoding=
"UTF-8"
%>
<%@
taglib
prefix
=
"s"
uri
=
"/struts-tags"
%>
...
<hr/>
<h2><s
:
text
name=
"Confirmation.message"
/></h2>
<table border=
"1"
>
<tr>
<th><s
:
text
name=
"Confirmation.champ"
/></th>
<th><s
:
text
name=
"Confirmation.valeur"
/></th>
</tr>
<tr>
<td><s
:
text
name=
"Form.textfield"
/></td>
<td><s
:
property
value=
"textfield"
/>
</tr>
<tr>
<td><s
:
text
name=
"Form.password"
/></td>
<td><s
:
property
value=
"password"
/>
</tr>
<tr>
<td><s
:
text
name=
"Form.textarea"
/></td>
<td><s
:
property
value=
"textarea"
/>
</tr>
<tr>
<td><s
:
text
name=
"Form.select1"
/></td>
<td><s
:
property
value=
"select1"
/>
</tr>
<tr>
<td><s
:
text
name=
"Form.select2"
/></td>
<td><s
:
property
value=
"select2"
/>
</tr>
<tr>
<td><s
:
text
name=
"Form.select3"
/></td>
<td><s
:
property
value=
"select3SelectedValues"
/>
</tr>
<tr>
<td><s
:
text
name=
"Form.radio"
/></td>
<td><s
:
property
value=
"radio"
/>
</tr>
<tr>
<td><s
:
text
name=
"Form.checkbox"
/></td>
<td><s
:
property
value=
"checkbox"
/>
</tr>
<tr>
<td><s
:
text
name=
"Form.checkboxlist"
/></td>
<td><s
:
property
value=
"checkboxlistSelectedValues"
/>
</tr>
<tr>
<td><s
:
text
name=
"Form.hidden"
/></td>
<td><s
:
property
value=
"hidden"
/>
</tr>
</table>
</body>
</html>
- ligne 13 : affiche la valeur du champ [Form].textfield
- ligne 17 : affiche la valeur du champ [Form].password
- ligne 21 : affiche la valeur du champ [Form].textarea
- ligne 25 : affiche la valeur du champ [Form].select1
- ligne 29 : affiche la valeur du champ [Form].select2
- ligne 33 : affiche les valeurs du tableau [Form].select3. Utilise pour cela, la méthode [Form].getSelect3SelectedValues.
- ligne 37 : affiche la valeur du champ [Form].radio
- ligne 41 : affiche la valeur du champ [Form].checkbox
- ligne 45 : affiche les valeurs du tableau [Form].checkboxlist. Utilise pour cela, la méthode [Form].getCheckboxlistSelectedValues.
- ligne 49 : affiche la valeur du champ [Form].hidden
IX-G. L'action [Form] - partie confirmation▲
Les méthodes getSelect3SelectedValues et getCheckboxlistSelectedValues citées précédemment, sont définies dans la classe [Form] :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
// valeurs sélectionnées dans champ select3
public
String getSelect3SelectedValues
(
) {
return
getValue
(
select3);
}
// valeurs sélectionnées dans champ checkboxlist
public
String getCheckboxlistSelectedValues
(
) {
return
getValue
(
checkboxlist);
}
// méthode utilitaire
public
String getValue
(
String[] values) {
String result =
""
;
for
(
String value : values) {
result +=
" "
+
value;
}
return
result;
}
Elles se contentent de concaténer les éléments des tableaux qu'elles doivent afficher.
IX-H. Les tests▲
A l'exécution du projet Netbeans, on obtient la page initiale suivante :
Les valeurs affichées en [1] et [2], proviennent des valeurs initiales des champs de l'action [Form]. Prenons quelques exemples :
private
String textarea =
"ligne1
\n
ligne2
\n
"
;
La valeur du champ textarea explique les affichages [1A] et [2A].
private
String[] select3 =
new
String[]{
"zéro"
, "deux"
}
;
private
String[] select3Values =
new
String[]{
"zéro"
, "un"
, "deux"
, "trois"
, "quatre"
}
;
La valeur du champ select3Values explique le contenu de la liste en [1B]. La valeur du champ select3 explique les éléments sélectionnés en [1B] et affichés en [2B].
Si nous saisissons d'autres valeurs dans le formulaire et que nous validons celui-ci, les valeurs saisies vont être postées dans les champs correspondants de l'action [Form]. Puis, la vue [Form.jsp] va être réaffichée. On est donc dans le même cas que précédemment, sauf que les valeurs initiales ont été remplacées par les valeurs postées. Voici un exemple :
IX-I. L'action [Form1] et la vue [Form1.jsp]▲
Le fichier de configuration [example.xml] configure l'action [Form1] suivante :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd"
>
<struts>
<package
name
=
"example"
namespace
=
"/example"
extends
=
"struts-default"
>
<action
name
=
"Form"
class
=
"example.Form"
>
<result
name
=
"success"
>
/example/Form.jsp</result>
</action>
<action
name
=
"Form1"
class
=
"example.Form1"
>
<result
name
=
"success"
>
/example/Form1.jsp</result>
</action>
<action
name
=
"Form2"
class
=
"example.Form2"
>
<result
name
=
"success"
>
/example/Form2.jsp</result>
</action>
</package>
</struts>
Lignes 11-13, l'appel à l'action [Form1] génère la vue [Form1.jsp].
L'action [Form1] est la suivante :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
package
example;
import
com.opensymphony.xwork2.ActionSupport;
public
class
Form1 extends
ActionSupport{
// constructeur sans paramètre
public
Form1
(
) {
}
// champs du formulaire
private
Data data=
new
Data
(
);
/**
*
@return
the data
*/
public
Data getData
(
) {
return
data;
}
/**
*
@param
data
the data to set
*/
public
void
setData
(
Data data) {
this
.data =
data;
}
}
L'action [Form1] est identique à l'action [Form] si ce n'est que sa partie modèle a été reléguée dans une classe annexe, la classe [Data], ligne11. La classe [Data] est la suivante :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
package
example;
public
class
Data {
// constructeur sans paramètre
public
Data
(
) {
}
// champs du formulaire
private
String textfield =
"texte"
;
private
String password =
"secret"
;
private
String textarea =
"ligne1
\n
ligne2
\n
"
;
private
String select1 =
"zéro"
;
private
String[] select1Values =
new
String[]{
"zéro"
, "un"
, "deux"
}
;
private
String select2 =
"trois"
;
private
String[] select2Values =
new
String[]{
"zéro"
, "un"
, "deux"
, "trois"
, "quatre"
}
;
private
String[] select3 =
new
String[]{
"zéro"
, "deux"
}
;
private
String[] select3Values =
new
String[]{
"zéro"
, "un"
, "deux"
, "trois"
, "quatre"
}
;
private
String radio=
"bleu"
;
private
String[] radioValues =
new
String[]{
"bleu"
, "blanc"
, "rouge"
}
;
private
Boolean checkbox =
false
;
private
String[] checkboxlist =
new
String[]{
"vélo"
, "bus"
}
;
private
String[] checkboxlistValues =
new
String[]{
"voiture"
, "tram"
, "vélo"
, "bus"
, "métro"
}
;
private
String hidden =
"initial"
;
private
String submitText;
// valeurs sélectionnées dans champ select3
public
String getSelect3SelectedValues
(
) {
return
getValue
(
select3);
}
// valeurs sélectionnées dans champ checkboxlist
public
String getCheckboxlistSelectedValues
(
) {
return
getValue
(
checkboxlist);
}
// méthode utilitaire
public
String getValue
(
String[] values) {
String result =
""
;
for
(
String value : values) {
result +=
" "
+
value;
}
return
result;
}
// getters et setters
....
}
On retrouve les champs déclarés précédemment dans l'action [Form].
Assez logiquement, la vue [Form1.jsp] est proche de la vue [Form.jsp] :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
<%@
page
contentType
=
"text/html; charset=UTF-8"
pageEncoding=
"UTF-8"
%>
<%@
taglib
prefix
=
"s"
uri
=
"/struts-tags"
%>
<html>
<head>
<title><s
:
text
name=
"Form.titre"
/></title>
<s
:
head
/>
</head>
<body background=
"<s:url value="
/ressources/standard.jpg
"/>"
>
<h2><s
:
text
name=
"Form.message"
/></h2>
<h3><s
:
text
name=
"Form.langues"
/></h3>
<ul>
<li>
<s
:
url
id=
"url"
action=
"Form"
>
<s
:
param
name=
"request_locale"
>
en</s
:
param
>
</s
:
url
>
<s
:
a
href=
"%{url}"
><s
:
text
name=
"Form.anglais"
/></s
:
a
>
</li>
<li>
<s
:
url
id=
"url"
action=
"Form"
>
<s
:
param
name=
"request_locale"
>
fr</s
:
param
>
</s
:
url
>
<s
:
a
href=
"%{url}"
><s
:
text
name=
"Form.francais"
/></s
:
a
>
</li>
</ul>
<s
:
form
name=
"formulaire"
>
<s
:
textfield
name=
"data.textfield"
key=
"Form.textfield"
/>
<s
:
password
name=
"data.password"
key=
"Form.password"
/>
<s
:
textarea
name=
"data.textarea"
key=
"Form.textarea"
cols=
"40"
rows=
"5"
/>
<s
:
select
name=
"data.select1"
list=
"data.select1Values"
size=
"1"
key=
"Form.select1"
headerValue=
"<-- select 1 -->"
headerKey=
"-1"
/>
<s
:
select
name=
"data.select2"
size=
"3"
list=
"data.select2Values"
key=
"Form.select2"
/>
<s
:
select
name=
"data.select3"
size=
"3"
list=
"data.select3Values"
key=
"Form.select3"
multiple=
"true"
/>
<s
:
radio
name=
"data.radio"
list=
"data.radioValues"
key=
"Form.radio"
/>
<s
:
checkbox
name=
"data.checkbox"
key=
"Form.checkbox"
/>
<s
:
checkboxlist
name=
"data.checkboxlist"
list=
"data.checkboxlistValues"
key=
"Form.checkboxlist"
/>
<s
:
hidden
name=
"data.hidden"
key=
"Form.hidden"
/>
<s
:
submit
key=
"Form.submitText"
name=
"data.submitText"
/>
</s
:
form
>
<hr/>
<h2><s
:
text
name=
"Confirmation.message"
/></h2>
<table border=
"1"
>
<tr>
<th><s
:
text
name=
"Confirmation.champ"
/></th>
<th><s
:
text
name=
"Confirmation.valeur"
/></th>
</tr>
<tr>
<td><s
:
text
name=
"Form.textfield"
/></td>
<td><s
:
property
value=
"data.textfield"
/>
</tr>
<tr>
<td><s
:
text
name=
"Form.password"
/></td>
<td><s
:
property
value=
"data.password"
/>
</tr>
<tr>
<td><s
:
text
name=
"Form.textarea"
/></td>
<td><s
:
property
value=
"data.textarea"
/>
</tr>
<tr>
<td><s
:
text
name=
"Form.select1"
/></td>
<td><s
:
property
value=
"data.select1"
/>
</tr>
<tr>
<td><s
:
text
name=
"Form.select2"
/></td>
<td><s
:
property
value=
"data.select2"
/>
</tr>
<tr>
<td><s
:
text
name=
"Form.select3"
/></td>
<td><s
:
property
value=
"data.select3SelectedValues"
/>
</tr>
<tr>
<td><s
:
text
name=
"Form.radio"
/></td>
<td><s
:
property
value=
"data.radio"
/>
</tr>
<tr>
<td><s
:
text
name=
"Form.checkbox"
/></td>
<td><s
:
property
value=
"data.checkbox"
/>
</tr>
<tr>
<td><s
:
text
name=
"Form.checkboxlist"
/></td>
<td><s
:
property
value=
"data.checkboxlistSelectedValues"
/>
</tr>
<tr>
<td><s
:
text
name=
"Form.hidden"
/></td>
<td><s
:
property
value=
"data.hidden"
/>
</tr>
</table>
</body>
</html>
La différence entre les vues [Form] et [Form1] peut être illustrée par la ligne 27 reproduite ci-dessous :
<s
:
textfield
name=
"data.textfield"
key=
"Form.textfield"
/>
Le champ de saisie est lié en lecture et écriture au champ data.textfield de l'action [Form1]. On rappelle qu'après exécution, les propriétés de l'action [Form1] sont accessibles à la vue. Ainsi l'attribut name précédent va être évalué comme [Form1].getData().getTextField(). Il faut donc que les deux méthodes get précédentes existent.
IX-J. L'action [Form2] et la vue [Form2.jsp]▲
Le fichier de configuration [example.xml] configure l'action [Form2] suivante :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd"
>
<struts>
<package
name
=
"example"
namespace
=
"/example"
extends
=
"struts-default"
>
<action
name
=
"Form"
class
=
"example.Form"
>
<result
name
=
"success"
>
/example/Form.jsp</result>
</action>
<action
name
=
"Form1"
class
=
"example.Form1"
>
<result
name
=
"success"
>
/example/Form1.jsp</result>
</action>
<action
name
=
"Form2"
class
=
"example.Form2"
>
<result
name
=
"success"
>
/example/Form2.jsp</result>
</action>
</package>
</struts>
Lignes 14-16, l'appel à l'action [Form2] génère la vue [Form2.jsp].
L'action [Form2] est la suivante :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
package
example;
import
com.opensymphony.xwork2.ActionSupport;
import
com.opensymphony.xwork2.ModelDriven;
public
class
Form2 extends
ActionSupport implements
ModelDriven {
// constructeur sans paramètre
public
Form2
(
) {
}
// modèle de l'action
public
Object getModel
(
) {
return
new
Data
(
);
}
}
- ligne 6 : l'action [Form2] implémente l'interface [ModelDriven]. Il n'y a qu'une méthode à implémenter, la méthode getModel de la ligne 12. Celle-ci rend une instance de la classe [Data] étudiée précédemment.
Le fait que l'action [Form2] implémente l'interface [ModelDriven] a la conséquence suivante : la vue affichée à la suite de l'action [Form2] a un accès direct aux propriétés du modèle de l'action [Form2], celui-ci étant rendu par la méthode getModel(). aussi la vue [Form2.jsp] redevient ce qu'elle était avec l'action [Form] initiale :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
<%@
page
contentType
=
"text/html; charset=UTF-8"
pageEncoding=
"UTF-8"
%>
<%@
taglib
prefix
=
"s"
uri
=
"/struts-tags"
%>
<html>
<head>
<title><s
:
text
name=
"Form.titre"
/></title>
<s
:
head
/>
</head>
<body background=
"<s:url value="
/ressources/standard.jpg
"/>"
>
<h2><s
:
text
name=
"Form.message"
/></h2>
<h3><s
:
text
name=
"Form.langues"
/></h3>
<ul>
<li>
<s
:
url
id=
"url"
action=
"Form"
>
<s
:
param
name=
"request_locale"
>
en</s
:
param
>
</s
:
url
>
<s
:
a
href=
"%{url}"
><s
:
text
name=
"Form.anglais"
/></s
:
a
>
</li>
<li>
<s
:
url
id=
"url"
action=
"Form"
>
<s
:
param
name=
"request_locale"
>
fr</s
:
param
>
</s
:
url
>
<s
:
a
href=
"%{url}"
><s
:
text
name=
"Form.francais"
/></s
:
a
>
</li>
</ul>
<s
:
form
name=
"formulaire"
>
<s
:
textfield
name=
"textfield"
key=
"Form.textfield"
/>
<s
:
password
name=
"password"
key=
"Form.password"
/>
<s
:
textarea
name=
"textarea"
key=
"Form.textarea"
cols=
"40"
rows=
"5"
/>
<s
:
select
name=
"select1"
list=
"select1Values"
size=
"1"
key=
"Form.select1"
headerValue=
"<--select 1 -->
<s:select name="
select2
" size="
3
" list="
select2Values
" key="
Form.select2
"/>
<s:select name="
select3
" size="
3
" list="
select3Values
" key="
Form.select3
" multiple="
true
"/>
<s:radio name="
radio
" list="
radioValues
" key="
Form.radio
"/>
<s:checkbox name="
checkbox
" key="
Form.checkbox
"/>
<s:checkboxlist name="
checkboxlist
" list="
checkboxlistValues
" key="
Form.checkboxlist
"/>
<s:hidden name="
hidden
" key="
Form.hidden
"/>
<s:submit key="
Form.submitText
" name="
submitText
"/>
</s:form>
<hr/>
<h2><s:text name="
Confirmation.message
"/></h2>
<table border="
1
">
<tr>
<th><s:text name="
Confirmation.champ
"/></th>
<th><s:text name="
Confirmation.valeur
"/></th>
</tr>
<tr>
<td><s:text name="
Form.textfield
"/></td>
<td><s:property value="
textfield
"/>
</tr>
<tr>
<td><s:text name="
Form.password
"/></td>
<td><s:property value="
password
"/>
</tr>
<tr>
<td><s:text name="
Form.textarea
"/></td>
<td><s:property value="
textarea
"/>
</tr>
<tr>
<td><s:text name="
Form.select1
"/></td>
<td><s:property value="
select1
"/>
</tr>
<tr>
<td><s:text name="
Form.select2
"/></td>
<td><s:property value="
select2
"/>
</tr>
<tr>
<td><s:text name="
Form.select3
"/></td>
<td><s:property value="
select3SelectedValues
"/>
</tr>
<tr>
<td><s:text name="
Form.radio
"/></td>
<td><s:property value="
radio
"/>
</tr>
<tr>
<td><s:text name="
Form.checkbox
"/></td>
<td><s:property value="
checkbox
"/>
</tr>
<tr>
<td><s:text name="
Form.checkboxlist
"/></td>
<td><s:property value="
checkboxlistSelectedValues
"/>
</tr>
<tr>
<td><s:text name="
Form.hidden
"/></td>
<td><s:property value="
hidden
"/>
</tr>
</table>
</body>
</html>
Ainsi la ligne 27 qui était précédemment :
<s
:
textfield
name=
"data.textfield"
key=
"Form.textfield"
/>
redevient :
<s
:
textfield
name=
"textfield"
key=
"Form.textfield"
/>
L'avantage de mettre le modèle en-dehors de la vue, est que cette architecture délimite plus clairement les fonctionnalités de chaque élément de l'architecture MVC. Le modèle M est clairement identifié par une classe. Par ailleurs, le modèle peut pré-exister aux actions. Ainsi, si une application web gère une base de personnes, il est probable que la classe Personne existe. Si une action propose un formulaire de saisie pour créer une nouvelle personne, le modèle de cette action est tout trouvé : c'est la classe Personne .
Nous avons vu comme il a été simple de passer de l'action [Form] à l'action [Form2] :
- le modèle M de la vue V a été encapsulé dans une classe à part
- l'action qui génère la vue utilisant ce modèle implémente l'interface ModelDriven avec la méthode getModel qui rend une instance du modèle M. L'action peut être alors vue comme une extension du contrôleur C de Struts 2, la classe FilterDispatcher.
Le lecteur pourra appliquer cette démarche à toute action décrite par la suite.