4. Korzystanie z formularzy dynamicznych
4.1. Deklaracja formularza dynamicznego
Widzieliśmy, że Struts wykorzystuje obiekty ActionForm do przechowywania wartości formularzy HTML przetwarzanych przez różne serwlety aplikacji. Dla każdego formularza w naszej aplikacji musimy utworzyć klasę pochodną od ActionForm. Może to szybko stać się dość uciążliwe, ponieważ dla każdego pola xx musimy napisać dwie metody: setXx i getXx. Struts oferuje nam możliwość korzystania z formularzy,
- , których struktura jest zadeklarowana w pliku struts-config.xml w sekcji <form-beans>
- i które są dynamicznie tworzone przez środowisko Struts zgodnie z zadeklarowaną strukturą
W ten sposób klasa służąca do przechowywania wartości „nazwisko” i „wiek” w aplikacji strutspersonne mogłaby zostać zdefiniowana w następujący sposób:
<form-beans>
<form-bean name="frmPersonne" type="org.apache.struts.actions.DynaActionForm">
<form-property name="nom" type="java.lang.String" initial=""/>
<form-property name="age" type="java.lang.String" initial=""/>
</form-bean>
</form-beans>
Dla każdego pola formularza definiuje się tag <form-property> z dwoma atrybutami:
- name: nazwa pola
- type: jego typ w Javie
Ponieważ wartości z formularza przesyłane przez klienta internetowego są ciągami znaków, najczęściej stosowanymi typami są java.lang.String dla pól o pojedynczej wartości oraz java.lang.String[] dla pól o wielu wartościach (pola wyboru o tej samej nazwie, listy z możliwością wielokrotnego wyboru itp.). Klasa DynactionForm, podobnie jak klasa ActionForm, posiada metodę validate, która nie wykonuje żadnej czynności. Aby sprawić, by klasa ta weryfikowała poprawność parametrów formularza, należy utworzyć jej podklasę i samodzielnie napisać metodę validate. Deklaracja formularza będzie zatem wyglądać następująco:
<form-beans>
<form-bean name="frmPersonne" type="istia.st.struts.personne.PersonneDynaForm">
<form-property name="nom" type="java.lang.String" initial=""/>
<form-property name="age" type="java.lang.String" initial=""/>
</form-bean>
</form-beans>
Klasę istia.st.struts.personne.PersonneDynaForm będziemy musieli stworzyć samodzielnie.
4.2. Tworzenie klasy DynaActionForm powiązanej z formularzem dynamicznym
Powyżej powiązaliśmy klasę istia.st.struts.personne.PersonneDynaForm z formularzem (imię, wiek). Teraz tworzymy tę klasę:
package istia.st.struts.personne;
import javax.servlet.http.*;
import org.apache.struts.action.*;
public class PersonneDynaForm extends DynaActionForm {
// weryfikacja
public ActionErrors validate(ActionMapping mapping, HttpServletRequest request) {
// obsługa błędów
ActionErrors erreurs = new ActionErrors();
// pole „imię” nie może być puste
String nom = (String)this.get("nom");
if (nom == null || nom.trim().equals("")) {
erreurs.add("nomvide", new ActionError("personne.formulaire.nom.vide"));
}
// wiek nie może być pusty
String age = (String)this.get("age");
if (age == null || age.trim().equals("")) {
erreurs.add("agevide", new ActionError("personne.formulaire.age.vide"));
}
else {
// wiek musi być dodatnią liczbą całkowitą
if (!age.matches("^\\s*\\d+\\s*$")) {
erreurs.add("ageincorrect", new ActionError("personne.formulaire.age.incorrect", age));
// zwracamy listę błędów
}
} //if
// zwracana jest lista błędów
return erreurs;
}
}//klasa
Należy zwrócić uwagę na następujące kwestie:
- klasa ta wywodzi się z klasy DynaActionForm
- metoda validate klasy DynaActionForm została przepisana. Podczas jej wykonania przez kontroler Struts utworzono obiekt PersonneDynaForm. Zawiera on słownik, którego kluczami są pola formularza „nazwa” i „wiek”, a wartościami – wartości tych pól. Aby uzyskać dostęp do pola w metodach klasy DynaActionForm, stosuje się metodę Object get(String nomDuChamp). Jeśli chcemy przypisać wartość do pola, możemy użyć metody void set(String nomDuChamp, Object wartość). Pełną definicję można znaleźć w definicji klasy DynaActionForm.
- Po pobraniu wartości pól „nazwa” i „wiek” z formularza metoda validate nie różni się od tej, która została napisana, gdy formularz był powiązany z obiektem ActionForm.
4.3. Nowa klasa FormulaireAction
Plik konfiguracyjny definiuje następującą akcję /main:
...
<form-beans>
<form-bean name="frmPersonne" type="istia.st.struts.personne.PersonneDynaForm">
<form-property name="nom" type="java.lang.String" initial=""/>
<form-property name="age" type="java.lang.String" initial=""/>
</form-bean>
</form-beans>
....
<action
path="/main"
name="frmPersonne"
scope="session"
validate="true"
input="/erreurs.do"
type="istia.st.struts.personne.FormulaireAction"
>
<forward name="reponse" path="/reponse.do"/>
</action>
Ta definicja jest taka sama jak w aplikacji strutspersonne. Akcja /main jest realizowana przez obiekt typu FormulaireAction. Obiekt ten otrzymywał wartości z formularza frmPersonne w obiekcie typu FormulaireBean. Obecnie otrzymuje je w obiekcie typu PersonneDynaForm. Należy zatem przepisać klasę:
package istia.st.struts.personne;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import javax.servlet.ServletException;
import istia.st.struts.personne.PersonneDynaForm;
public class FormulaireAction extends Action {
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response) throws IOException,ServletException {
// formularz jest poprawny, w przeciwnym razie nie dotarliśmy byśmy do tego miejsca
PersonneDynaForm formulaire=(PersonneDynaForm)form;
request.setAttribute("nom",formulaire.get("nom"));
request.setAttribute("age",formulaire.get("age"));
return mapping.findForward("reponse");
}//wykonaj
}
Należy zwrócić uwagę na następujące kwestie:
- musimy zaimportować klasę PersonneDynaForm, która zawiera dane formularza, aby uzyskać dostęp do jego definicji
- metoda execute pobiera wartości parametrów formularza za pomocą metody [DynaActionForm].get.
W przypadku klasy FormulaireAction w aplikacji strutspersonne zmienił się jedynie sposób uzyskiwania dostępu do wartości formularza.
4.4. Wdrożenie i testowanie aplikacji strutspersonne1
4.4.1. Tworzenie kontekstu
Tę nową aplikację nazwaliśmy strutspersonne1. Tworzymy nową definicję w pliku <tomcat>\conf\serveur.xml serwera Tomcat 4.x:
Po wykonaniu tej czynności należy ponownie uruchomić Tomcat. Prawidłowość działania kontekstu można sprawdzić, wywołując stronę URL:
http://localhost:8080/strutspersonne1/

4.4.2. Widoki
Skopiujemy folder „vues” z aplikacji strutspersonne do folderu aplikacji strutspersonne1. Widoki bowiem nie uległy zmianie.

4.4.3. Kompilacja klas
Musimy utworzyć dwie klasy: PersonneDynaForm i FormulaireAction, przy czym ta druga wykorzystuje pierwszą. Możemy je utworzyć i skompilować za pomocą projektu JBuilder:

4.4.4. Folder WEB-INF
Skopiujemy folder WEB-INF z aplikacji strutspersonne do folderu aplikacji strutspersonne1. Zmieniają się niektóre pliki:

Plik konfiguracyjny struts-config.xml przyjmuje następującą postać:
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE struts-config PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 1.1//EN"
"http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd">
<struts-config>
<form-beans>
<form-bean name="frmPersonne" type="istia.st.struts.personne.PersonneDynaForm">
<form-property name="nom" type="java.lang.String" initial=""/>
<form-property name="age" type="java.lang.String" initial=""/>
</form-bean>
</form-beans>
<action-mappings>
<action
path="/main"
name="frmPersonne"
scope="session"
validate="true"
input="/erreurs.do"
type="istia.st.struts.personne.FormulaireAction"
>
<forward name="reponse" path="/reponse.do"/>
</action>
<action
path="/erreurs"
parameter="/vues/erreurs.personne.jsp"
type="org.apache.struts.actions.ForwardAction"
/>
<action
path="/reponse"
parameter="/vues/reponse.personne.jsp"
type="org.apache.struts.actions.ForwardAction"
/>
<action
path="/formulaire"
parameter="/vues/formulaire.personne.jsp"
type="org.apache.struts.actions.ForwardAction"
/>
</action-mappings>
<message-resources parameter="ressources.personneressources"/>
</struts-config>
Ten plik jest identyczny z plikiem aplikacji strutspersonne, z wyjątkiem dynamicznej definicji formularza (część w ramce).
W folderze WEB-INF/classes umieścimy klasy skompilowane przez JBuilder:

W folderze WEB-INF\classes\ressources należy umieścić plik komunikatów. Nie uległ on zmianie.
personne.formulaire.nom.vide=<li>Vous devez indiquer un nom</li>
personne.formulaire.age.vide=<li>Vous devez indiquer un age</li>
personne.formulaire.age.incorrect=<li>L'âge [{0}] est incorrect</li>
errors.header=<ul>
errors.footer=</ul>

4.5. Testy
Jesteśmy gotowi do przeprowadzenia testów. Poniżej zamieszczamy kilka zrzutów ekranu, które czytelnik powinien spróbować odtworzyć.
Wymagane są: URLhttp://localhost:8080/strutspersonne1/formulaire.do:

Należy użyć przycisku [Envoyer] bez wypełniania pól:

Próbujemy ponownie, ale pojawia się błąd w polu „age”:

Otrzymujemy następującą odpowiedź:

Próbujemy ponownie, tym razem podając prawidłowe wartości:

Otrzymujemy następującą odpowiedź:

4.6. Wniosek
Korzystanie z formularzy dynamicznych ułatwia pisanie klas typu ActionForm, których zadaniem jest przechowywanie wartości z formularzy. Można pójść o krok dalej w upraszczaniu zarządzania formularzami. W aplikacji strutspersonne1 utworzyliśmy klasę PersonneDynaForm w celu sprawdzania poprawności wartości (imię, wiek) w formularzu. W praktyce niektóre weryfikacje powtarzają się często: pole niepuste, pole sprawdzające określony wyrażenie regularne, pole typu całkowitego, pole daty itp. Tego typu standardowe weryfikacje można wówczas zdefiniować w pliku konfiguracyjnym struts-config.html. Jeśli wszystkie wymagane weryfikacje są „standardowe”, nie ma już potrzeby tworzenia klasy dla formularza. Właśnie to teraz omówimy.