9. Przykład 07 – Tagi formularza
Stworzymy i wykorzystamy następujący formularz:
![]() | ![]() |
Rozszerzymy koncepcję wstrzykiwania parametrów, omówioną w przypadku pola tekstowego, na inne elementy HTML formularza.
9.1. Projekt NetBeans
![]() |
- w [1], widoki projektu [Formx.JSP]
- w [2], plik konfiguracyjny Struts oraz komunikaty zinternacjonalizowane
- do [3], akcje z [Formx.java] oraz kolejny plik konfiguracyjny Struts.
9.2. Konfiguracja Struts
Plik [struts.xml] ma następującą treść:
<?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>
- wiersz 9: zawiera odwołanie do innego pliku konfiguracyjnego [example.xml]. To właśnie ten plik będzie konfigurował akcje.
- wiersze 11–19: pakiet default. Określa adres przekierowania dla URL /. Zostanie on przekierowany do URL /example/Form.action.
Plik [example.xml], który konfiguruje działania, wygląda następująco:
<?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>
- wiersze 7–17: definiują akcje /example/Form, /example/Form1, /example/Form2, gdzie /example to przestrzeń nazw (wiersz 7), a Formx to akcje.
- wiersze 8–10: wykonanie akcji Form spowoduje wyświetlenie widoku /example/Form.JSP.
- wiersze 11–13: wykonanie akcji Form1 spowoduje wyświetlenie widoku /example/Form1.JSP.
- wiersze 14–16: wykonanie akcji Form2 spowoduje wyświetlenie widoku /example/Form2.JSP.
9.3. Pliki komunikatów
Nie będziemy się tutaj zatrzymywać na kwestii internacjonalizacji projektu. Omówiliśmy ją już w przykładzie 01. Podajemy zawartość pliku [messages.properties], aby czytelnik mógł zrozumieć kolejne widoki.
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
9.4. Widok [Form.JSP] – część wprowadzania danych
Wygląda to następująco:
![]() |
Wygląda to 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: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>
</html>
- Formularz zaczyna się w wierszu 26, a kończy w wierszu 38. W wierszu 26 brakuje atrybutu action. Atrybut ten określa, do jakiej akcji zostaną przesłane dane z formularza. W przypadku braku atrybutu action dane zostaną przesłane do akcji, która doprowadziła do wyświetlenia widoku, w tym przypadku do akcji [Form].
- wiersz 27: pole wprowadzania danych. Atrybut key określa jego etykietę. Atrybut name to nazwa parametru, który zostanie przesłany. Ciąg znaków przesłany dla tego pola będzie miał postać:
Nazwa parametru odpowiada polu akcji [Form]. Wartość pola wprowadzania danych texte_saisi zostanie tutaj wstawiona do pola textfield akcji [Form]. I odwrotnie, jeśli widok [Form.JSP] zostanie wyświetlony w wyniku działania [Form], wartość pola wprowadzania danych będzie odpowiadać wartości pola textfield w działaniu [Form]. Pole to jest zatem wykorzystywane zarówno do odczytu, jak i zapisu. W tym celu klasa [Form] musi posiadać metody getTextField oraz setTextField.
To rozumowanie ma zastosowanie do wszystkich kolejnych tagów wprowadzania danych. Nie będziemy tego powtarzać.
- wiersz 28: wprowadzenie hasła. Ciąg znaków wysyłany do akcji [Form] będzie miał postać
W akcji [Form] musi istnieć pole password wraz z metodami get i set.
- wiersz 29: wprowadzenie tekstu wielowierszowego w obszarze o wymiarach 5 wierszy i 40 kolumn. Ciąg znaków wysyłany do akcji [Form] będzie miał postać
W akcji [Form] musi istnieć pole textarea wraz z metodami get i set.
- wiersz 30: lista rozwijana (size=1) z opcją pojedynczego wyboru. Można wybrać tylko jedną wartość z listy. Ciąg znaków wysyłany do akcji [Form] będzie miał postać:
W akcji [Form] musi istnieć pole select1 wraz z metodami get i set.
Atrybut key to nazwa listy. Atrybut list określa listę wartości, które mają zostać umieszczone w polu kombi. W tym przypadku wywołana zostanie metoda getSelect1Values akcji [Form] w celu wypełnienia pola kombi. Metoda ta może zwracać kolekcję, słownik lub tablicę. W tym przypadku zwróci ona tablicę ciągów znaków [chaine1, chaine2, ...], która wygeneruje następujący kod HTML:
<option value="chaine1">chaine1</option>
<option value="chaine2">chaine2</option>
...
Tekst pomiędzy znacznikami <option>...</option> zostanie wyświetlony w polu rozwijanym. Atrybut value określa wartość, która ma zostać przesłana, jeśli użytkownik wybierze tę opcję. W tym przypadku atrybut value i tekst wyświetlany w polu rozwijanym są identyczne. Najczęściej jednak tak nie jest. Jeśli wybrana zostanie druga opcja, następujący ciąg znaków zostanie przesłany do akcji [Form]:
Pole [Form].select1 otrzyma wówczas wartość chaine2. I odwrotnie, jeśli w momencie wyświetlenia formularza wartość tego pola wynosi chaine3, to wizualnie trzeci element
<option value="chaine3">chaine3</option>
będzie wyglądał na zaznaczony w polu rozwijanym.
Atrybut headerValue określa tekst, który ma być wyświetlany jako pierwsza opcja listy rozwijanej, a headerKey określa wartość, która ma zostać przesłana, jeśli użytkownik wybierze tę opcję.
- wiersz 31: ponownie lista o rozmiarze 3 (size=3): w przeciwieństwie do poprzedniej listy, na której widoczny jest tylko jeden element, tutaj widoczne będą 3 elementy listy. Sposób zaznaczania pozostaje taki sam. Można zaznaczyć tylko jeden element.
- wiersz 32: ponownie lista, ale z możliwością wielokrotnego wyboru (multiple=true). W tym przypadku wartość przesłana do akcji [Form] będzie miała postać:
jeśli wybrano opcje chaine2 i chaine5. Gdy dla tego samego parametru, w tym przypadku select3, księgowanych jest kilka wartości, akcja musi posiadać pole o nazwie parametru i mieć typ tableau. Na przykład akcja [Form] mogłaby mieć pole o następującej postaci:
oraz odpowiadające mu metody get / set. Tablica select3 otrzyma wówczas tablicę ciągów znaków [chaine2,chaine5]. I odwrotnie, podczas wyświetlania widoku [Form.JSP] wartości pola [Form].select3 określają, które opcje z listy select3 będą zaznaczone. Nadal mamy do czynienia z trybem odczytu/zapisu.
- wiersz 33: grupa przycisków opcji. Nazwa grupy jest podawana przez atrybut key. Nazwy poszczególnych przycisków są podawane przez atrybut list. Wartością tego atrybutu jest nazwa metody akcji [Form], która musi zwracać kolekcję, słownik lub tablicę. W tym przypadku metoda [Form].getRadioValues zwróci tablicę ciągów znaków [chaine1,chaine2,...], która wygeneruje następujące tagi HTML:
<input type="radio" name="radio" ... value="chaine1"/>chaine1
<input type="radio" name="radio" ... value="chaine2"/>chaine2
...
Jeśli przycisk opcji chaine2 jest zaznaczony, wysyłana wartość będzie wynosić
i ta wartość zostanie umieszczona w polu [Form].radio.
- wiersz 34: pole wyboru. Jeśli jest zaznaczone, następujący ciąg parametrów zostanie przesłany do akcji [Form]
Jeśli pole to nie jest zaznaczone, żaden ciąg parametrów nie zostanie przesłany. Struts 2 posiada wewnętrzny mechanizm, dzięki któremu wszystko przebiega tak, jakby ciąg
został przesłany.
- Wiersz 35: lista pól wyboru. Można zaznaczyć wiele z nich. Nazwa grupy jest podawana przez atrybut key. Nazwy poszczególnych pól wyboru są podawane przez atrybut list. Wartością tego atrybutu jest nazwa metody akcji [Form], która musi zwracać kolekcję, słownik lub tablicę. W tym przypadku metoda [Form].getCheckboxlistValues zwróci tablicę ciągów znaków [chaine1,chaine2,...], która wygeneruje następujące tagi HTML:
<input type="checkbox" name="checkboxlist" ... value="chaine1"/>chaine1
<input type="checkbox" name="checkboxlist" ... value="chaine2"/>chaine2
...
Jeśli użytkownik zaznaczy pola o nazwach chaine2 i chaine5, następujący ciąg znaków zostanie przesłany do akcji [Form]:
Tabela [chaine2, chaine5] zostanie przypisana do pola [Form].checkboxlist, które musi zatem być typu tabela ciągów znaków, tak jak w przypadku list z możliwością wielokrotnego wyboru. Z kolei podczas wyświetlania widoku [Form.JSP] wartości pola [Form].checkboxlist określają, które pola wyboru zostaną zaznaczone.
- wiersz 36: pole ukryte. Pole ukryte to pole, które jest przesyłane bez wprowadzania danych przez użytkownika. Jego wartość jest ustalana przez programistę. W tym przypadku pole ukryte przyjmie wartość z pola [Form].hidden. Ta sama wartość zostanie przesłana w postaci:
i ponownie przypisana do pola [Form].hidden.
- wiersz 37: przycisk typu submit. Kliknięcie tego przycisku spowoduje przesłanie wszystkich wartości formularza do akcji [Form]. Ciąg przesłanych parametrów będzie wyglądał następująco:
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
9.5. Akcja [Form] – część wprowadzania danych
Jej kod jest następujący:
package example;
import com.opensymphony.xwork2.ActionSupport;
public class Form extends ActionSupport {
// konstruktor bez parametrów
public Form() {
}
// pola formularza
private String textfield = "texte";
private String password = "secret";
private String textarea = "ligne1\nligne2\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;
// wartości wybrane w polu select3
public String getSelect3SelectedValues() {
return getValue(select3);
}
// wartości wybrane w polu checkboxlist
public String getCheckboxlistSelectedValues() {
return getValue(checkboxlist);
}
// metoda pomocnicza
public String getValue(String[] values) {
String result = "";
for (String value : values) {
result += " " + value;
}
return result;
}
// metody pobierające i ustawiające wartości pól
....
}
- wiersz 1: pole powiązane z tagiem textfield w formularzu. Służy zarówno do zainicjowania tego pola wprowadzania danych, jak i do pobrania z niego wartości. W momencie wykonania akcji POST otrzymuje wartość wprowadzoną w tym polu.
- wiersz 2: pole powiązane z tagiem password w formularzu. W momencie wywołania POST pobiera wartość wprowadzoną w tym polu.
- wiersz 13: powiązany z tagiem textarea formularza. W momencie wywołania POST pobiera tekst wprowadzony w tym polu.
- wiersz 14: powiązany z tagiem select1 formularza, lista z jednym elementem do wyboru. Jego wartość początkowa wybiera element z listy rozwijanej. Zostanie wybrany ten element, którego atrybut value jest równy tej wartości. W momencie wykonania POST otrzymuje atrybut value z opcji wybranej w polu rozwijanym.
- wiersz 15: ciągi znaków, które będą zasilać listę rozwijaną select1.
- wiersze 16 i 17: to samo dotyczy select1
- wiersz 18: powiązana z tagiem select1 formularza lista wielokrotnego wyboru. Wartości początkowe w tabeli wybierają odpowiadające im elementy listy. Wybrane zostaną te elementy, których atrybut value jest równy jednemu z tych elementów. W momencie wywołania POST otrzymuje atrybuty value różnych opcji wybranych z listy rozwijanej.
- wiersz 19: ciągi znaków, które zostaną umieszczone na liście select3
- wiersz 20: powiązany z tagiem radio formularza. Jego wartość służy do zaznaczenia jednego z przycisków opcji, tego, którego atrybut value jest równy tej wartości. W momencie wywołania POST otrzymuje wartość zaznaczonego przycisku opcji.
- wiersz 21: różne nazwy i wartości przycisków opcji.
- wiersz 22: powiązany z tagiem checkbox w formularzu. Jego wartość początkowa służy do zaznaczenia lub odznaczenia pola wyboru. W momencie wywołania POST przyjmuje wartość true, jeśli pole zostało zaznaczone, a w przeciwnym razie – false.
- wiersz 23: powiązany z tagiem checkboxlist w formularzu. Wartości początkowe w tabeli określają, które pola wyboru mają zostać zaznaczone. Zaznaczone zostaną te pola, których atrybut value jest równy jednej z tych wartości początkowych. W momencie POST otrzymuje atrybuty value z różnych zaznaczonych pól wyboru.
- wiersz 24: ciągi znaków, które będą stanowić treść etykiet pól w checkboxlist.
- wiersz 25: powiązany z ukrytym polem hidden. Jego wartość początkowa ustala wartość ukrytego pola. W momencie POST otrzyma tę samą wartość, ponieważ użytkownik nie ma możliwości jej zmiany.
- wiersz 26: powiązany z tagiem submit. W momencie wywołania POST przyjmuje etykietę przycisku. Można pominąć to pole.
- Metody w wierszach 29, 34 i 39 zostaną omówione nieco dalej.
9.6. Widok [Form.JSP] – część „Potwierdzenie”
Widok [Form.JSP] zawiera sekcję „Potwierdzenie wprowadzonych danych”, która nie została jeszcze omówiona:
![]() |
Jej kod jest następujący:
<%@ 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>
- wiersz 13: wyświetla wartość pola [Form].textfield
- wiersz 17: wyświetla wartość pola [Form].password
- wiersz 21: wyświetla wartość pola [Form].textarea
- wiersz 25: wyświetla wartość pola [Form].select1
- wiersz 29: wyświetla wartość pola [Form].select2
- wiersz 33: wyświetla wartości tablicy [Form].select3. W tym celu należy użyć metody [Form].getSelect3SelectedValues.
- wiersz 37: wyświetla wartość pola [Form].radio
- wiersz 41: wyświetla wartość pola [Form].checkbox
- wiersz 45: wyświetla wartości tablicy [Form].checkboxlist. W tym celu należy użyć metody [Form].getCheckboxlistSelectedValues.
- wiersz 49: wyświetla wartość pola [Form].hidden
9.7. Akcja [Form] – część potwierdzająca
Wymienione powyżej metody getSelect3SelectedValues i getCheckboxlistSelectedValues są zdefiniowane w klasie [Form]:
// wartości wybrane w polu select3
public String getSelect3SelectedValues() {
return getValue(select3);
}
// wartości wybrane w polu checkboxlist
public String getCheckboxlistSelectedValues() {
return getValue(checkboxlist);
}
// metoda pomocnicza
public String getValue(String[] values) {
String result = "";
for (String value : values) {
result += " " + value;
}
return result;
}
Ograniczają się one do łączenia elementów tablic, które mają wyświetlić.
9.8. Testy
Po uruchomieniu projektu w NetBeans wyświetla się następująca strona startowa:
![]() |
Wartości wyświetlane w [1] i [2] pochodzą z wartości początkowych pól akcji [Form]. Rozważmy kilka przykładów:
private String textarea = "ligne1\nligne2\n";
Wartość pola textarea wyjaśnia wyświetlane wartości w [1A] i [2A].
private String[] select3 = new String[]{"zéro", "deux"};
private String[] select3Values = new String[]{"zéro", "un", "deux", "trois", "quatre"};
Wartość pola select3Values wyjaśnia zawartość listy w [1B]. Wartość pola select3 wyjaśnia elementy wybrane w polu [1B] i wyświetlone w polu [2B].
Jeśli wprowadzimy inne wartości w formularzu i zatwierdzimy go, wprowadzone wartości zostaną zapisane w odpowiednich polach akcji [Form]. Następnie ponownie wyświetli się widok [Form.JSP]. Mamy więc do czynienia z tą samą sytuacją, co poprzednio, z tą różnicą, że wartości początkowe zostały zastąpione wartościami przesłanymi. Oto przykład:
![]() | ![]() |
9.9. Akcja [Form1] i widok [Form1.JSP]
Plik konfiguracyjny [example.xml] konfiguruje następującą akcję [Form1]:
<?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>
W wierszach 11–13 wywołanie akcji [Form1] generuje widok [Form1.JSP].
Akcja [Form1] wygląda następująco:
package example;
import com.opensymphony.xwork2.ActionSupport;
public class Form1 extends ActionSupport{
// konstruktor bez parametrów
public Form1() {
}
// pola formularza
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;
}
}
Akcja [Form1] jest identyczna z akcją [Form], z tą różnicą, że jej część szablonowa została przeniesiona do klasy pomocniczej, klasy [Data], wiersz 11. Klasa [Data] ma następującą postać:
package example;
public class Data {
// konstruktor bez parametrów
public Data() {
}
// pola formularza
private String textfield = "texte";
private String password = "secret";
private String textarea = "ligne1\nligne2\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;
// wartości wybrane w polu select3
public String getSelect3SelectedValues() {
return getValue(select3);
}
// wartości wybrane w polu checkboxlist
public String getCheckboxlistSelectedValues() {
return getValue(checkboxlist);
}
// metoda pomocnicza
public String getValue(String[] values) {
String result = "";
for (String value : values) {
result += " " + value;
}
return result;
}
// metody pobierające i ustawiające
....
}
W akcji [Form] odnajdujemy pola zadeklarowane wcześniej.
Co jest dość logiczne, widok [Form1.JSP] jest zbliżony do widoku [Form.JSP]:
<%@ 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>
Różnicę między widokami [Form] i [Form1] można zilustrować na przykładzie wiersza 27 przedstawionego poniżej:
<s:textfield name="data.textfield" key="Form.textfield" />
Pole wprowadzania danych jest powiązane w trybie odczytu i zapisu z polem data.textfield akcji [Form1]. Przypominamy, że po wykonaniu akcji właściwości akcji [Form1] są dostępne dla widoku. W związku z tym poprzedni atrybut name zostanie obliczony jako [Form1].getData().getTextField(). W związku z tym muszą istnieć dwie poprzednie metody o nazwie get.
9.10. Akcja [Form2] i widok [Form2.JSP]
Plik konfiguracyjny [example.xml] konfiguruje następującą akcję [Form2]:
<?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>
W wierszach 14–16 wywołanie akcji [Form2] generuje widok [Form2.JSP].
Akcja [Form2] wygląda następująco:
package example;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;
public class Form2 extends ActionSupport implements ModelDriven {
// konstruktor bez parametrów
public Form2() {
}
// szablon akcji
public Object getModel() {
return new Data();
}
}
- wiersz 6: akcja [Form2] implementuje interfejs [ModelDriven]. Należy zaimplementować tylko jedną metodę – metodę getModel z wiersza 12. Metoda ta zwraca instancję klasy [Data], którą omówiono wcześniej.
Fakt, że akcja [Form2] implementuje interfejs [ModelDriven], ma następującą konsekwencję: widok wyświetlany w wyniku akcji [Form2] ma bezpośredni dostęp do właściwości modelu akcji [Form2], który jest zwracany przez metodę getModel(). W związku z tym widok [Form2.JSP] powraca do stanu, w jakim znajdował się przy początkowej akcji [Form]:
<%@ 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/>
<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>
W ten sposób wiersz 27, który wcześniej brzmiał:
<s:textfield name="data.textfield" key="Form.textfield" />
ponownie przybiera postać:
<s:textfield name="textfield" key="Form.textfield" />
Zaletą umieszczenia modelu poza widokiem jest to, że taka architektura wyraźniej wyznacza funkcje każdego elementu architektury MVC. Model M jest jednoznacznie identyfikowany przez klasę. Ponadto model może istnieć przed akcjami. Jeśli więc aplikacja internetowa zarządza bazą osób, prawdopodobnie istnieje klasa Personne. Jeśli akcja udostępnia formularz do wprowadzenia danych w celu utworzenia nowej osoby, model tej akcji jest oczywisty: jest to klasa Personne.
Widzieliśmy, jak łatwo było przejść od akcji [Form] do akcji [Form2]:
- szablon M widoku V został zamknięty w osobnej klasie
- akcja generująca widok wykorzystujący ten model implementuje interfejs ModelDriven wraz z metodą getModel, która zwraca instancję modelu M. Akcję tę można zatem postrzegać jako rozszerzenie kontrolera C w Struts 2, czyli klasy FilterDispatcher.
Czytelnik będzie mógł zastosować to podejście do każdej akcji opisanej poniżej.






