10. Przykład 08 – Elementy związane z listami
Poniższy przykład dotyczy tagów służących do wyświetlania list. Spotkaliśmy się z nimi już w poprzednim przykładzie: lista rozwijana, pola wyboru i przyciski opcji. Formularz testowy będzie wyglądał następująco:
![]() | ![]() |
10.1. Projekt NetBeans
Projekt NetBeans wygląda następująco:
![]() |
- w pliku [1] znajduje się akcja, a w pliku [2] – powiązany widok.
Plik [example/example.xml] konfiguruje aplikację:
<?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>
</package>
</struts>
Wiersze 8–9 pokazują, że wywołanie akcji URL generuje widok [/example/Form.JSP]. Akcja [Form] będzie zatem odpowiedzialna za zbudowanie szablonu widoku [Form.JSP].
10.2. Plik z komunikatami zinternacjonalizowanymi
Plik [messages.properties] zawierający komunikaty z lokalizacją wygląda następująco:
Form.francais=Fran\u00E7ais
Form.anglais=Anglais
Form.titre=Struts 2 - les tags \u00E0 valeurs multiples
Form.message=Struts 2 - les tags \u00E0 valeurs multiples
Form.langues=langues
Form.select1=1-select1 (multiple=false, size=1)
Form.select2=2-select2 (multiple=true, size=3)
Form.select3=3-select3 (multiple=true, size=3)
Form.select4=4-select4 (multiple=true, size=3)
Form.select5=5-select5 (multiple=true, size=3)
Form.checkboxlist1=6-checkboxlist1
Form.checkboxlist2=7-checkboxlist2
Form.checkboxlist3=8-checkboxlist3
Form.radio1=9-radio1
Form.radio2=10-radio2
Form.radio3=11-radio3
Form.submitText=Valider
Confirmation.message=Confirmation des valeurs saisies
Confirmation.champ=champ
Confirmation.valeur=valeur
Confirmation.select1=1-select1 (multiple=false, size=1)
Confirmation.select2=2-select2 (multiple=false, size=3)
Confirmation.select3=3-select3 (multiple=true, size=3)
Confirmation.select4=4-select4 (multiple=true, size=3)
Confirmation.select5=5-select5 (multiple=true, size=3)
Confirmation.checkboxlist1=6-checkboxlist1
Confirmation.checkboxlist2=7-checkboxlist2
Confirmation.checkboxlist3=8-checkboxlist3
Confirmation.radio1=9-radio1
Confirmation.radio2=10-radio2
Confirmation.radio3=11-radio3
Widok [Form.JSP] wykorzystuje te komunikaty.
10.3. Widok [Form.JSP]
Widok [Form.JSP] wygląda następująco:
<%@ 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:select name="select1" list="{'vert','jaune','rouge'}" size="1" key="Form.select1"/>
<s:select name="select2" list="{'vert','jaune','rouge'}" size="3" key="Form.select2" multiple="true"/>
<s:select name="select3" list="#{'01':'vert(01)','02':'jaune(02)','03':'rouge(03)','04':'blanc(04)','05':'noir(05)'}" size="3" key="Form.select3" multiple="true"/>
<s:select name="select4" list="dico" size="3" key="Form.select4" multiple="true"/>
<s:select name="select5" list="couleurs" listKey="id" listValue="nom" size="3" key="Form.select5" multiple="true" />
<s:checkboxlist name="checkboxlist1" list="#{'01':'vert(01)','02':'jaune(02)','03':'rouge(03)','04':'blanc(04)','05':'noir(05)'}" key="Form.checkboxlist1"/>
<s:checkboxlist name="checkboxlist2" list="dico" key="Form.checkboxlist2"/>
<s:checkboxlist name="checkboxlist3" list="couleurs" listKey="id" listValue="nom" key="Form.checkboxlist3" />
<s:radio name="radio1" list="#{'01':'vert(01)','02':'jaune(02)','03':'rouge(03)','04':'blanc(04)','05':'noir(05)'}" key="Form.radio1"/>
<s:radio name="radio2" list="dico" key="Form.radio2"/>
<s:radio name="radio3" list="couleurs" listKey="id" listValue="nom" key="Form.radio3" />
<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.select1"/></td>
<td><s:property value="select1"/>
</tr>
<tr>
<td><s:text name="Form.select2"/></td>
<td><s:property value="select2SelectedValues"/>
</tr>
<tr>
<td><s:text name="Form.select3"/></td>
<td><s:property value="select3SelectedValues"/>
</tr>
<tr>
<td><s:text name="Form.select4"/></td>
<td><s:property value="select4SelectedValues"/>
</tr>
<tr>
<td><s:text name="Form.select5"/></td>
<td><s:property value="select5SelectedValues"/>
</tr>
<tr>
<td><s:text name="Form.checkboxlist1"/></td>
<td><s:property value="checkboxlist1SelectedValues"/>
</tr>
<tr>
<td><s:text name="Form.checkboxlist2"/></td>
<td><s:property value="checkboxlist2SelectedValues"/>
</tr>
<tr>
<td><s:text name="Form.checkboxlist3"/></td>
<td><s:property value="checkboxlist3SelectedValues"/>
</tr>
<tr>
<td><s:text name="Form.radio1"/></td>
<td><s:property value="radio1"/>
</tr>
<tr>
<td><s:text name="Form.radio2"/></td>
<td><s:property value="radio2"/>
</tr>
<tr>
<td><s:text name="Form.radio3"/></td>
<td><s:property value="radio3"/>
</tr>
</table>
</body>
</html>
Przyjrzyjmy się poszczególnym tagom formularza:
- wiersz 27: tag <s:select>:
<s:select name="select1" list="{'vert','jaune','rouge'}" size="1" key="Form.select1"/>
Ten element wygeneruje następujący kod HTML:
Wartość atrybutu list to tablica ciągów znaków wykorzystywana do generowania wartości i etykiet opcji. Powiązanym szablonem jest pole select1 w następującej akcji [Form]:
private String select1 = "jaune";
Pole to jest powiązane w trybie odczytu i zapisu z tagiem o nazwie select1. W trybie zapisu wartość wybranej opcji zostanie umieszczona w polu select1. W trybie odczytu wartość pola select1 służy do określenia wybranej opcji. W tym przypadku wartość jaune pola select1 służy do wybrania opcji znacznika select1, która ma wartość „żółty”. Dlatego w wierszu 3 opcja jest zaznaczona.
- wiersz 28
<s:select name="select2" list="{'vert','jaune','rouge'}" size="3" key="Form.select2" multiple="true"/>
Nie ma zasadniczej różnicy w stosunku do znacznika z wiersza 27, z wyjątkiem tego, że w tym przypadku dokonano wyboru wielu opcji. W związku z tym wysyłana wartość jest tablicą wartości, odpowiadającą wybranym opcjom. Pole select2 akcji powiązanej z formularzem wygląda następująco:
private String[] select2 = new String[]{"vert", "rouge"};
Jest to rzeczywiście tablica. Jej wartość początkowa służy do wyboru opcji, których wartości znajdują się w tablicy.
- wiersz 29
<s:select name="select3" list="#{'01':'vert(01)','02':'jaune(02)','03':'rouge(03)','04':'blanc(04)','05':'noir(05)'}" size="3" key="Form.select3" multiple="true"/>
W tym przypadku wartość atrybutu list to słownik, którego klucze służą jako wartości opcji, a powiązane z nimi wartości służą jako etykiety opcji. Wygenerowany kod HTML wygląda następująco:
Powiązane pole w akcji wygląda następująco:
private String[] select3 = new String[]{"01", "03"};
Ponownie mamy tablicę wartości, ponieważ powiązany element jest typu wielokrotnego wyboru. Początkowe wartości tablicy wyjaśniają, dlaczego opcje w wierszach 2 i 4 są początkowo zaznaczone.
- wiersz 30
<s:select name="select4" list="dico" size="3" key="Form.select4" multiple="true"/>
W tym przypadku opcje z listy będą dostarczane przez szablon widoku, który w tym przypadku stanowi część akcji [Form]:
private HashMap<String, String> dico = new HashMap<String, String>();
Klucze słownika dico będą stanowić wartości opcji. Wartości słownika dico będą stanowić nazwy opcji. Pole select4 akcji wygląda następująco:
private String[] select4 = new String[]{"1", "3"};
Jest to tablica wartości, do której trafią wartości przesłane przez tag select4.
- wiersz 31
<s:select name="select5" list="couleurs" listKey="id" listValue="nom" size="3" key="Form.select5" multiple="true" />
Lista opcji jest generowana przez pole couleurs zdefiniowane przez atrybut list. Pole couleurs akcji [Form] jest zdefiniowane w następujący sposób:
private Couleur[] couleurs;
Klasa [Couleur] jest zdefiniowana w następujący sposób:
package example;
public class Couleur {
// pola
private String id;
private String nom;
// konstruktor
public Couleur(){
}
// metody pobierające i ustawiające
...
}
Zawiera dwa pola o nazwach id i nom. W tagu select5 mamy: listKey="id" oraz listValue="nom".. Oznacza to, że pole id posłuży jako wartość opcji, a pole nom jako nazwa. Przypominamy, że to właśnie wartości opcji są księgowane. Zostaną one zaksięgowane w następującym polu select5:
private String[] select5 = new String[]{"1", "3"};
- wiersz 32
<s:checkboxlist name="checkboxlist1" list="#{'01':'vert(01)','02':'jaune(02)','03':'rouge(03)','04':'blanc(04)','05':'noir(05)'}" key="Form.checkboxlist1"/>
Ten tag wygeneruje następujący kod HTML:
Wszystkie pola wyboru noszą nazwę checkboxlist1. Ponieważ każde z nich może zostać zaznaczone, może zostać przesłanych wiele parametrów checkboxlist1. Dlatego pole checkboxlist1 w modelu musi być typu tablicowego. Atrybut list w wierszu 22 jest słownikiem, którego klucze generują atrybuty value pól wyboru, a wartości – etykiety tych pól. Pole checkboxlist1 akcji [Form] ma następującą postać:
private String[] checkboxlist1 = new String[]{"01", "03"};
- wiersz 33
<s:checkboxlist name="checkboxlist2" list="dico" key="Form.checkboxlist2"/>
Ten tag działa tak samo jak omówiony wcześniej tag select4. Powiązane pole w akcji wygląda następująco:
private String[] checkboxlist2 = new String[]{"1", "3"};
- wiersz 34
<s:checkboxlist name="checkboxlist3" list="couleurs" listKey="id" listValue="nom" key="Form.checkboxlist3" />
Ten tag działa tak samo jak omówiony wcześniej tag select5. Powiązane pole checkboxlist3 w akcji jest następujące:
private String[] checkboxlist3 = new String[]{"1", "3"};
- wiersz 35
<s:radio name="radio1" list="#{'01':'vert(01)','02':'jaune(02)','03':'rouge(03)','04':'blanc(04)','05':'noir(05)'}" key="Form.radio1"/>
Ten tag działa zasadniczo tak samo jak tag select3, który widzieliśmy wcześniej. Wygenerowany kod HTML wygląda następująco:
Wszystkie tagi mają tę samą nazwę: radio1. Można zaznaczyć tylko jedno pole wyboru. W związku z tym wysyłana jest tylko jedna wartość – ta z zaznaczonego pola wyboru. Klucze słownika list służą do generowania atrybutów value przycisków opcji, natomiast wartości słownika służą do generowania etykiet powiązanych z tymi przyciskami. Pole radio1 powiązane z tą akcją wygląda następująco:
private String radio1="01";
- wiersz 36
<s:radio name="radio2" list="dico" key="Form.radio2"/>
Ten tag działa zasadniczo tak samo jak tag select4. Pole radio2 powiązane z tą akcją jest następujące:
private String radio2="1";
- wiersz 37
<s:radio name="radio3" list="couleurs" listKey="id" listValue="nom" key="Form.radio3" />
Ten znacznik działa na tej samej zasadzie co znacznik select5. Pole radio3 powiązane z tą akcją jest następujące:
private String radio3="1";
To tyle, jeśli chodzi o część saisie formularza. Istnieje również część confirmation, która jest analogiczna do tej z formularza z poprzedniego przykładu: potwierdza ona przesłane wartości. Omówimy jedynie następujące wiersze:
<tr>
<td><s:text name="Form.select2"/></td>
<td><s:property value="select2SelectedValues"/>
</tr>
co wizualnie odpowiada poniższemu:
Tekst w lewej komórce pochodzi z pliku [messages.properties]:
Form.select2=2-select2 (multiple=true, size=3)
Tekst w komórce po prawej stronie został wygenerowany przez metodę getSelect2SelectedValues w ramach akcji [Form]:
// wartości wybrane z list rozwijanych
public String getSelect2SelectedValues() {
return getArrayValue(select2);
}
// metody pomocnicze
public String getArrayValue(String[] values) {
String result = "";
for (String value : values) {
result += " " + value;
}
return result;
}
Metoda getSelect2SelectedValues przekształca wartości tablicy select2 w ciąg znaków.
10.4. Akcja [Form]
W komentarzu do widoku [Form.JSP] przedstawiliśmy pola powiązanej akcji [Form]. Kod akcji [Form] jest następujący:
package example;
import com.opensymphony.xwork2.ActionSupport;
import java.util.HashMap;
public class Form extends ActionSupport {
// pola formularza
private String select1 = "jaune";
private String[] select2 = new String[]{"vert", "rouge"};
private String[] select3 = new String[]{"01", "03"};
private String[] select4 = new String[]{"1", "3"};
private String[] select5 = new String[]{"1", "3"};
private HashMap<String, String> dico = new HashMap<String, String>();
private Couleur[] couleurs;
private String[] checkboxlist1 = new String[]{"01", "03"};
private String[] checkboxlist2 = new String[]{"1", "3"};
private String[] checkboxlist3 = new String[]{"1", "3"};
private String radio1="01";
private String radio2="1";
private String radio3="1";
private String submitText;
// konstruktor bez parametrów
public Form() {
// inicjowanie słownika kolorów
dico.put("1", "vert(1)");
dico.put("2", "jaune(2)");
dico.put("3", "bleu(3)");
dico.put("4", "rouge(4)");
dico.put("5", "blanc(5)");
// inicjowanie tablicy obiektów typu Kolor
couleurs = new Couleur[dico.size()];
int i = 0;
for (String key : dico.keySet()) {
couleurs[i] = new Couleur();
couleurs[i].setId(key);
couleurs[i].setNom(dico.get(key));
i++;
}
}
// wartości wybrane z list rozwijanych
public String getSelect2SelectedValues() {
return getArrayValue(select2);
}
public String getSelect3SelectedValues() {
return getArrayValue(select3);
}
public String getSelect4SelectedValues() {
return getArrayValue(select4);
}
public String getSelect5SelectedValues() {
return getArrayValue(select5);
}
public String getCheckboxlist1SelectedValues() {
return getArrayValue(checkboxlist1);
}
public String getCheckboxlist2SelectedValues() {
return getArrayValue(checkboxlist2);
}
public String getCheckboxlist3SelectedValues() {
return getArrayValue(checkboxlist3);
}
// metody pomocnicze
public String getArrayValue(String[] values) {
String result = "";
for (String value : values) {
result += " " + value;
}
return result;
}
// metody pobierające i ustawiające
...
}
W tej akcji nie ma metody o nazwie execute. W związku z tym wykonywana jest metoda execute z klasy nadrzędnej ActionSupport. Metoda ta nie wykonuje żadnych działań, a jedynie zwraca klucz nawigacyjny SUCCESS.
10.5. Testy
Zachęcamy czytelnika do przetestowania aplikacji [exemple-08].


