Skip to content

6. Formularze HTML

Do tej pory korzystaliśmy z jednego formularza zawierającego jedynie dwa pola wprowadzania danych. W niniejszym rozdziale zamierzamy stworzyć i przetworzyć formularz wykorzystujący typowe elementy graficzne (przyciski opcji, pola wyboru, pola wprowadzania danych, listy rozwijane, listy).

6.1. Widoki aplikacji

Aplikacja będzie miała tylko dwa widoki. Pierwszy z nich przedstawia pusty formularz:

QZXW2HTMLP000547ZQX 1 - formulaire

Image

Ten pierwszy widok, o nazwie formulaire.jsp, pozwoli nam na zastosowanie różnych tagów z biblioteki struts-html. Użytkownik wypełnia formularz:

Image

Przycisk [Envoyer] pozwala uzyskać potwierdzenie wprowadzonych wartości. Będzie to drugi widok:

Image

Ten drugi widok pozwoli nam na wykorzystanie dwóch kolejnych bibliotek tagów: struts-bean i struts-logic. Link [Retour au formulaire] pozwala nam powrócić do formularza w stanie, w jakim został wypełniony. W ten sposób powracamy do pierwszego widoku.

6.2. Architektura aplikacji

  • Formularz (widok 1) będzie reprezentowany przez dynamiczny obiekt Struts o nazwie dynaFormulaire, którego typ wywodzi się od typu DynaActionForm. Zostanie on wyświetlony przez widok formulaire.jsp.
  • Akcja Struts o nazwie InitFormulaireAction będzie miała na celu pozyskanie danych niezbędnych do wyświetlenia formularza
  • wypełniony formularz zostanie przetworzony przez akcję ForwardAction, która polegać będzie na przekierowaniu żądania do drugiego widoku confirmation.jsp. Widok ten zajmie się wyświetleniem wartości z formularza.

6.3. Konfiguracja aplikacji

6.3.1. Plik server.xml

Kontekst aplikacji będzie nosił nazwę /formulaire2. W związku z tym dodamy następujący wiersz do pliku server.xml serwera Tomcat:

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

Po wykonaniu tej czynności ewentualnie uruchomimy ponownie serwer Tomcat, aby uwzględnił nowy kontekst. Możemy sprawdzić poprawność tego kontekstu, wywołując stronę URL http://localhost:8080/formulaire2:

Image

6.3.2. Plik web.xml

Plik konfiguracyjny aplikacji web.xml będzie wyglądał następująco:

<?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>

W odniesieniu do plików konfiguracyjnych web.xml, z którymi mieliśmy już do czynienia, wprowadzamy kilka zmian:

  • wprowadzamy dwie nowe biblioteki tagów: struts-bean i struts-logic. Będą one wykorzystywane w widoku confirmation.jsp. Z kolei widok formulaire.jsp będzie korzystał z biblioteki struts-html.

6.3.3. Plik struts-config.xml

Plik struts-config.xml będzie wyglądał następująco:

<?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>

Znajdują się tu trzy główne sekcje:

  • deklarację formularzy w sekcji <form-beans>
  • deklaracja akcji w sekcji <action-mappings>
  • deklaracja pliku zasobów w sekcji <message-ressources>

6.3.4. Obiekty (beans) formularzy aplikacji

Obiekty używane do reprezentowania formularzy HTML w aplikacji to obiekty typu ActionForm lub pochodne (DynaActionForm, DynaValidatorForm, ...). Nazywane są one „beanami”, ponieważ ich konstrukcja jest zgodna z zasadami obiektów typu JavaBeans. W naszej aplikacji występuje tylko jeden obiekt formularza typu „bean”, o nazwie dynaFormulaire i będący pochodną typu DynaActionForm. Będzie on wykorzystywany w następujących sytuacjach:

  • przechowywanie danych niezbędnych do wyświetlenia widoku nr 1
  • pobieranie wartości z formularza w widoku nr 1 po jego zatwierdzeniu (submit) przez użytkownika
  • przechowywanie danych niezbędnych do wyświetlenia widoku nr 2

Struktura bean dynaFormulaire jest ściśle powiązana z formularzem widoku nr 1. Przyjrzyjmy się mu:

Nr
Typ HTML
Rola
1
<input name="opt" type="radio" value="tak">
<input name="opt" type="radio" value="nie">
grupa powiązanych ze sobą przycisków opcji (ta sama nazwa)
2
<input name="chk1" type="radio" value="on">
<input name="chk2" type="radio" value="włączone">
<input name="chk3" type="radio" value="on">
grupy pól wyboru
(nie mają tej samej nazwy)
3
<input type="text" name="champSaisie" >
pole wprowadzania danych
4
<input type="password" name="mdp" >
pole hasła
5
<textarea name="boiteSaisie">...</textarea>
pole wprowadzania tekstu wielowierszowego
6
<select name="combo" size="1">..</select>
listy rozwijanej
7
<select name="listeSimple" size="3">..</select>
lista z możliwością wyboru jednej pozycji
8
<select name="listeMultiple" size="3" multiple>..</select>
lista z możliwością wielokrotnego wyboru
9
<input type="button" value="Wyczyść"
onclick='effacerListe("listeSimple")'>
przycisk umożliwiający odznaczenie
wybranych elementów w listeSimple (7)
10
<input type="button" value="Usuń"
onclick='effacerListe("listeMultiple")'>
przycisk umożliwiający odznaczenie
wybranych elementów w listeMultiple (8)
11
<input type="submit" value="Wyślij">
przycisk przesyłania formularza
12
<input type="hidden" name="secret" value="...">
ukryte pole

Rozróżnijmy kilka przypadków:

  1. obiekt dynaFormulaire służy do przechowywania wartości z powyższego formularza HTML, który zostanie wysłany za pomocą przycisku [Envoyer]. Musi on zatem zawierać te same pola, co formularz HTML. Typ pola jest określany zgodnie z następującą regułą:
  • jeśli pole HTML zawiera tylko jedną wartość, wówczas pole w dynaFormulaire będzie typu java.lang.String
  • jeśli pole HTML zawiera wiele wartości, wówczas pole w dynaFormulaire będzie miało typ java.lang.String[]

W powyższym formularzu HTML jedynie pole listeMultiple może być powiązane z wieloma wartościami (wybranymi przez użytkownika). Zatem pierwsza definicja obiektu dynaFormulaire wyglądałaby następująco:

        <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>            

W jaki sposób formularz dynaFormulaire zostanie wypełniony wartościami z formularza HTML przesłanego przez klienta internetowego?

opt
Pole „opt” otrzyma wartość „tak”, jeśli zaznaczono pole HTML <input type="radio" name="opt" value="tak">, a wartość „nie”, jeśli zaznaczono pole <input type="radio" name="opt" value="nie">.
chk1
pole chk1 otrzyma wartość „włączone”, jeśli zaznaczono pole HTML <input name="chk1" type="radio" value="1">, w przeciwnym razie nie otrzyma żadnej wartości. W tym ostatnim przypadku pole chk1 zachowa swoją poprzednią wartość.
chk2
to samo
chk3
to samo
champSaisie
Pole champSaisie otrzyma tekst wprowadzony przez użytkownika w polu HTML <input type="text" name="champSaisie">. Tekst ten może ewentualnie stanowić pusty ciąg znaków.
mdp
Pole „mdp” otrzyma tekst wprowadzony przez użytkownika w polu HTML <input type="password" name="mdp">. Tekst ten może ewentualnie być pusty.
boiteSaisie
Pole boiteSaisie otrzyma tekst wprowadzony przez użytkownika w polu HTML <textarea name="boiteSaisie">...</textarea>. Tekst ten stanowi jeden ciąg znaków, złożony z wierszy wpisanych przez użytkownika, oddzielonych od siebie sekwencją znaków „\r\n”. Uzyskany tekst może ewentualnie być pustym ciągiem znaków.
combo
pole kombi otrzyma opcję wybraną przez użytkownika w polu HTML <select name="combo" size="1">..</select>. Wybrana opcja to ta, która pojawia się w polu kombi. Jeśli wybrana opcja HTML jest typu <option value="XX">YY</option>, pole kombi otrzyma wartość „XX”. Jeśli wybrana opcja HTML jest typu <option>YY</option>, pole listy rozwijanej otrzyma wartość „YY”.
listeSimple
pole listeSimple otrzyma opcję wybraną przez użytkownika w polu HTML <select name="listeSimple" size="..">..</select>, o ile taka istnieje. Jeśli nie ma takiej opcji, pole listeSimple nie otrzyma żadnej wartości i zachowa swoją poprzednią wartość. Wartość faktycznie przypisana do pola listeSimple jest zgodna z zasadami określonymi dla listy rozwijanej.
listeMultiple
pole listeMultiple typu String[] otrzyma opcje wybrane przez użytkownika w polu HTML <select name="listeMultiple" size=".." multiple>..</select>, jeśli takie istnieją. Jeśli nie ma żadnych opcji, tablica listeMultiple nie otrzyma żadnych wartości, a jej zawartość pozostanie niezmieniona. Wartości faktycznie przypisane do tablicy listeMultiple są zgodne z zasadami określonymi dla pola kombi.
secret
pole „secret” otrzyma wartość XX z pola HTML <input type="hidden" name="secret" value="XX">. Tekst ten może ewentualnie stanowić pusty ciąg znaków.
  1. Obiekt dynaFormulaire służy do nadania początkowej zawartości widoku nr 1. Wartości poprzednich pól zostaną wykorzystane w następujących celach:
opt
musi mieć wartość „tak” lub „nie”, aby przeglądarka wiedziała, który przycisk opcji zaznaczyć
chk1
jeśli chk1 ma wartość „on”, pole wyboru zostanie zaznaczone, w przeciwnym razie nie zostanie zaznaczone
chk2
to samo
chk3
to samo
champSaisie
wartość pola zostanie wyświetlona w polu wprowadzania danych champSaisie
mdp
wartość pola zostanie wyświetlona w polu wprowadzania hasła
boiteSaisie
wartość pola zostanie wyświetlona w polu wprowadzania danych boiteSaisie
combo
wartość tego pola określa, który element listy rozwijanej ma zostać zaznaczony po wyświetleniu formularza
listeSimple
to samo
listeMultiple
wartości tabeli listeMultiple wskazują, które elementy listy wielokrotnego wyboru mają być zaznaczone podczas wyświetlania formularza
secret
wartość pola zostanie przypisana do atrybutu „value” pola HTML secret.

Widok nr 1 wymaga dodatkowych informacji:

  • lista wartości do wyświetlenia w liście rozwijanej
  • lista wartości do wyświetlenia w liście listeSimple
  • lista wartości, które mają być wyświetlone na liście listeMultiple

Istnieje kilka sposobów przekazania tych informacji do widoku. Na przykład wystarczą tabele umieszczone w zapytaniu przekazywanym do widoku. W tym przypadku umieszczamy te tabele w 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>            

Formularz dynaFormulaire zostanie zainicjowany przez akcję /init, która wywoła obiekt pochodny od klasy Action o nazwie InitFormulaireAction. To właśnie ten obiekt będzie odpowiedzialny za utworzenie trzech tablic niezbędnych do wyświetlenia trzech list oraz umieszczenie ich w bean'ie dynaFormulaire. Plik konfiguracyjny nadaje temu beanowi zakres równy sesji. Oznacza to, że kontroler Struts umieści ten bean w sesji. Dzięki temu nie będziemy musieli go odtwarzać między kolejnymi cyklami żądanie-odpowiedź. Akcja /init zostanie zatem wywołana tylko raz.

  1. Obiekt dynaFormulaire jest również wykorzystywany do dostarczania treści widoku nr 2. Widok ten ogranicza się do wyświetlania wartości tego obiektu.

6.3.5. Akcje aplikacji

Akcje są realizowane przez obiekty typu Action lub pochodne. Konfiguracja akcji odbywa się wewnątrz tagów <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>

Należy zauważyć, że nie zawsze z akcją powiązany jest formularz. Tak jest w przypadku powyższej akcji /affiche. Zanim omówimy szczegółowo każdą akcję, przypomnijmy sobie, jak działa para „akcja-formularz” powiązana w tagu <action>:

  • akcja rozpoczyna się od żądania wysłanego przez klienta internetowego, a kończy się wysłaniem strony odpowiedzi. Jest to cykl żądanie-odpowiedź między klientem a serwerem internetowym. Żądanie jest odbierane przez kontroler Struts typu ActionServlet lub jego pochodną. To właśnie ten kontroler wysyła odpowiedź.
  • Bean formularza typu ActionForm lub pochodnego jest tworzony, jeśli jeszcze nie istnieje. Kontroler sprawdza, czy może znaleźć obiekt o nazwie name w zakresie określonym przez scope. Jeśli tak, to go wykorzystuje. Jeśli nie, to tworzy go i umieszcza w zakresie określonym przez scope, powiązając go z atrybutem wskazanym przez name.

Na przykład w przypadku akcji /init kontroler wykona operację request.getSession().getAttribute("dynaFormulaire"), aby sprawdzić, czy obiekt dynaFormulaire został już utworzony. Jeśli nie, utworzy go i umieści w sesji za pomocą instrukcji typu request.getSession().setAttribute("dynaFormulaire", new DynaFormulaire(...)).

  • Kontroler wyszuka również obiekt Action typu wskazanego przez atrybut type. Jeśli go nie znajdzie, utworzy go, w przeciwnym razie wykorzysta istniejący.
  • Zostanie wywołana metoda reset fasoli formularza. Fasola ta, z wyjątkiem sytuacji podczas jej pierwotnego utworzenia, jest ponownie wykorzystywana. Zawiera wówczas dane, które może zaistnieć potrzeba „wyczyścić”. Czynność tę wykonuje się w metodzie reset fasoli ActionForm lub jej pochodnej.
  • jeśli akcja jest celem formularza wysyłanego metodą POST, wówczas wartości z formularza zawarte w żądaniu klienta są kopiowane do pól o tej samej nazwie w beanie formularza. Należy zauważyć, że przed tym kopiowaniem wywołano metodę reset.
  • Jeśli w konfiguracji określono atrybut `validate="true"`, wywołana zostanie metoda `validate` obiektu formularza. Metoda ta musi wówczas zweryfikować dane zawarte w obiekcie formularza. Weryfikacja ta ma miejsce najczęściej tylko wtedy, gdy formularz właśnie otrzymał nowe dane za pośrednictwem wysłanego formularza i chce się sprawdzić poprawność tych danych. Metoda ta zwraca kontrolerowi ewentualną listę błędów w obiekcie ActionErrors.
  • Jeśli obiekt ActionErrors nie jest pusty, kontroler wyświetla widok określony przez atrybut input akcji.
  • Jeśli walidacja danych nie jest wymagana lub przebiegła pomyślnie, kontroler uruchamia metodę `execute` obiektu typu `Action` lub jego pochodnej, powiązanego z bieżącą akcją. To właśnie w tej metodzie przetwarzane jest żądanie klienta internetowego. Metoda `execute` zwraca obiekt typu `ActionForward`, indeksowany za pomocą kluczy typu ciąg znaków. Klucze te są zadeklarowane przez tagi forward skonfigurowanej akcji. W naszym przykładzie akcja /init ma tylko jeden tag forward. Przypisuje on klucz „afficherFormulaire” do widoku formulaire.jsp.
  • Kontroler wyświetla widok, z którym powiązany jest otrzymany klucz. Widok ten może w rzeczywistości być akcją, w którym to przypadku powyższy proces się powtarza.

Akcja /init

        <action
          path="/init"
            name="dynaFormulaire"
            validate="false" 
            scope="session"
          type="istia.st.struts.formulaire.InitFormulaireAction"
      >
            <forward name="afficherFormulaire" path="/vues/formulaire.jsp"/>
        </action>
  • Akcja /init odbywa się zazwyczaj raz podczas pierwszego cyklu żądanie-odpowiedź, gdy użytkownik żąda obiektu URL http://localhost:8080/formulaire2/init.do
  • obiekt dynaFormulaire jest tworzony lub ponownie wykorzystywany. Jest on pobierany (ponowne wykorzystanie) lub umieszczany (tworzenie) w sesji zgodnie z atrybutem scope.
  • Wywoływana jest jego metoda reset. Co powinna ona wykonać? Zazwyczaj pola obiektu ActionForm są resetowane do wartości domyślnych. Jednak w tym przypadku nie zrobimy tego, ponieważ obiekt dynaFormulaire jest umieszczany w sesji (scope="session"). Pola obiektu dynaFormulaire muszą zatem zachować swoje wartości. Jakie są te wartości podczas początkowego utworzenia obiektu dynaFormulaire? Istnieją dwa przypadki:
  • pole ma wartość początkową określoną w pliku konfiguracyjnym:
            <form-property name="opt" type="java.lang.String" initial="non"/>

W tym przypadku kontroler Struts utworzy to pole z tą wartością początkową.

  • pole nie ma wartości początkowej określonej w konfiguracji: zastosowanie mają reguły inicjalizacji języka Java. Zasadniczo pola liczbowe będą miały wartość zero, ciągi znaków będą miały wartość pustą, a pozostałe obiekty będą miały wartość null.

Przyjrzyjmy się początkowej konfiguracji 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>            

Wartości początkowe pól w rekordzie dynaFormulaire po jego utworzeniu będą następujące:

Pole
Wartość początkowa
opt
„nie”
chk1, chk2, chk3
pusty ciąg znaków
champSaisie
pusty ciąg znaków
mdp
pusty ciąg znaków
boiteSaisie
pusty ciąg znaków
combo
pusty ciąg znaków
listeSimple
pusty ciąg znaków
listeMultiple
tablica pustych ciągów znaków
secret
„xxx”
valeursCombo, listeSimple, listeMultiple
tablica pustych ciągów znaków
  • można by sobie wyobrazić, że metoda reset w dynaFormulaire nadaje wartości trzem tablicom, które mają zasilać trzy listy w widoku formulaire.jsp. Byłoby to tutaj możliwe, ponieważ dane w tych trzech tablicach są generowane w sposób arbitralny. Niemniej jednak najczęściej dane te pochodzą z modelu aplikacji, czyli z M w MVC. W tym przypadku, aby nie komplikować przykładu, przyjmiemy rozwiązanie pośrednie, polegające na wygenerowaniu tych wartości przez akcję InitFormulaireAction, a więc przez element C z MVC.
  • Nie ma konieczności pisania metody reset w klasie dynaFormulaire, ponieważ klasa ActionForm, od której ona pochodzi, posiada taką metodę, która nic nie robi (brak inicjalizacji).
  • Po wywołaniu metody reset klasy dynaFormulaire kontroler sprawdza atrybut validate akcji. W tym przypadku ma on wartość „false”. Metoda validate klasy dynaFormulaire nie zostanie wywołana.
  • Obiekt InitFormulaireAction zostaje utworzony lub odnowiony, jeśli już istniał, a następnie wywoływana jest jego metoda `execute`. To właśnie ta metoda przypisze dowolne wartości do trzech tablic obiektu dynaFormulaire: valeursCombo, valeursListeSimple i valeursListeMultiple. Metoda zwraca obiekt ActionForward o kluczu „afficherFormulaire”.
  • Kontroler wyświetla widok /vues/formulaire.jsp, który został powiązany z kluczem „afficherFormulaire” za pomocą znacznika forward akcji /init.

Akcja /confirmation

      <action
          path="/confirmation"
            name="dynaFormulaire"
            validate="false" 
            scope="session"
          parameter="/vues/confirmation.jsp"
          type="org.apache.struts.actions.ForwardAction"
      />
  • Akcja /confirmation ma miejsce, gdy użytkownik naciśnie przycisk [Envoyer] w widoku nr 1. Przeglądarka „wysyła” wówczas do kontrolera Struts formularz wypełniony przez użytkownika.
  • Obiekt dynaFormulaire jest pobierany z sesji
  • wywoływana jest jego metoda reset. Po jej wywołaniu kontroler Struts skopiuje wartości pól formularza przesłanego przez klienta do pól o tej samej nazwie w obiekcie dynaFormulaire. Przyjrzyjmy się ponownie liście pól tego obiektu i zobaczmy, jak przebiega to kopiowanie:
Pole
Powiązany kod HTML
Wartość pola
po skopiowaniu wartości z formularza
opt
<input type="radio" name="opt" value="tak">Tak
<input type="radio" name="opt" value="nie" checked="checked">Nie
– „tak” lub „nie” w zależności od zaznaczonego przycisku opcji
chk1
<input type="checkbox" name="chk1" value="on">
- „on”, jeśli pole wyboru chk1 zostało zaznaczone
- zachowuje poprzednią wartość, jeśli pole wyboru chk1 nie zostało zaznaczone
chk2
<input type="checkbox" name="chk2" value="on">
- „on”, jeśli pole wyboru chk2 zostało zaznaczone
- zachowuje poprzednią wartość, jeśli pole wyboru chk2 nie zostało zaznaczone
chk3
<input type="checkbox" name="chk2" value="on">
- „on”, jeśli pole wyboru chk3 zostało zaznaczone
- zachowuje poprzednią wartość, jeśli pole wyboru chk3 nie zostało zaznaczone
champSaisie
<input type="text" name="champSaisie" value="">
- wartość wpisana przez użytkownika w polu champSaisie
mdp
<input type="password" name="mdp" value="">
- wartość wprowadzona przez użytkownika w polu „mdp”
boiteSaisie
<textarea name="boiteSaisie"></textarea>
- wartość wprowadzona przez użytkownika w polu boiteSaisie
combo
<select name="combo">...</select>
- wartość wybrana przez użytkownika w combo
listeSimple
<select name="listeSimple" size="3">...</select>
- wartość wybrana przez użytkownika w polu listeSimple
listeMultiple
<select name="listeMultiple" multiple="multiple" size="5">
- tablica ciągów znaków zawierająca wartości wybrane przez użytkownika w listeMultiple
secret
<input type="hidden" name="secret" value="xxx">
- „xxx”.

Mamy problem z polami, które niekoniecznie otrzymują wartość w żądaniu wysyłanym przez przeglądarkę. Dotyczy to pól wyboru od chk1 do chk3 oraz dwóch list: listeSimple i listeMultiple. W takim przypadku pola te zachowują swoją poprzednią wartość, czyli tę uzyskaną podczas poprzedniego cyklu żądania-odpowiedzi.

Rozważmy na przykład pole wyboru chk1 i załóżmy, że w poprzednim cyklu żądania-odpowiedzi użytkownik zaznaczył to pole. Przeglądarka wysłała wówczas w łańcuchu parametrów swojego żądania informację chk1="on". Twórca strony przypisał zatem wartość „on” do pola chk1 w dynaFormulaire. Załóżmy teraz, że w bieżącym cyklu użytkownik nie zaznacza pola wyboru chk1. W takim przypadku w łańcuchu parametrów nowego żądania przeglądarka nie wysyła czegoś w rodzaju chk1="off", lecz nie wysyła nic. W rezultacie pole chk1 w dynaFormulaire zachowa wartość „on”, a zatem będzie miało wartość, która nie odzwierciedla wartości z formularza zatwierdzonego przez użytkownika. Aby rozwiązać ten problem, wykorzystamy metodę reset w dynaFormulaire. W tej metodzie ustawimy trzy pola chk1, chk2, chk3 na „off”. W naszym przykładzie dotyczącym chk1, jeśli użytkownik:

  • zaznaczy pole wyboru chk1. Wówczas przeglądarka wyśle informację chk1="on", a pole chk1 w dynaFormulaire zmieni się na „on”
  • nie zaznaczy pola wyboru chk1. Wówczas przeglądarka nie wyśle wartości dla pola chk1, które zachowa swoją poprzednią wartość „off”. W obu przypadkach wartość zapisana w polu chk1 w dynaFormulaire jest poprawna.

Problem ten dotyczy obu list: listeSimple i listeMultiple. Jeśli na tych listach nie wybrano żadnej opcji, nie pojawią się one w parametrach zapytania i w związku z tym zachowają swoje poprzednie wartości. W metodzie reset dynaFormulaire zresetujemy listeSimple za pomocą pustego ciągu znaków, a listeMultiple za pomocą tablicy ciągów o długości 0.

  • Po wywołaniu metody reset obiektu dynaFormulaire kontroler kopiuje do pól obiektu dynaFormulaire informacje, które zostały mu przesłane w żądaniu klienta
  • tworzony lub ponownie wykorzystywany jest obiekt ForwardAction, a następnie wywoływana jest jego metoda `execute`. ForwardAction jest predefiniowaną klasą, która zwraca obiekt ActionForward wskazujący na widok zdefiniowany przez atrybut „parameter” akcji, w tym przypadku /vues/confirmation.jsp.
  • Kontroler wysyła ten widok. Cykl zostaje zakończony.

Akcja /wyświetla

      <action
          path="/affiche"
          parameter="/vues/formulaire.jsp"
          type="org.apache.struts.actions.ForwardAction"
      />
  • Akcja /affiche jest uruchamiana poprzez kliknięcie linku [Retour vers le formulaire] w widoku nr 2.
  • W tym przypadku nie ma formularza powiązanego z tą akcją. Przechodzimy więc od razu do wykonania metody `execute` obiektu `ForwardAction`, która zwróci obiekt `ActionForward` wskazujący na widok `/vues/formulaire.jsp`.

6.3.6. Plik komunikatów aplikacji

Trzecia sekcja pliku struts-config.xml to sekcja zawierająca komunikaty:

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

Plik ApplicationResources.properties znajduje się w katalogu WEB-INF/classes. Będzie on pusty. Nawet jeśli jest pusty, musi zostać zadeklarowany w pliku konfiguracyjnym, w przeciwnym razie biblioteka tagów struts-bean, którą omówimy nieco później, wygeneruje błąd. Biblioteka ta jest wykorzystywana przez widok confirmation.jsp.

6.4. Kod widoków

6.4.1. Widok formulaire.jsp

Przypomnijmy, że ten widok jest wyświetlany w dwóch przypadkach:

  • przy wywołaniu akcji /init podczas pierwszego cyklu żądanie-odpowiedź
  • przy wywołaniu akcji /affiche w kolejnych cyklach

Kod widoku formulaire.jsp wygląda następująco:

<%@ 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>

Ta strona JSP wykorzystuje tagi pochodzące z biblioteki struts-html. Należy pamiętać, że aby korzystać z biblioteki tagów, należy:

  • zadeklarować ją w pliku web.xml aplikacji za pomocą tagu <tag-lib>
  <taglib>
      <taglib-uri>/WEB-INF/struts-html.tld</taglib-uri>
    <taglib-location>/WEB-INF/struts-html.tld</taglib-location>
  </taglib>
  • umieścić kod tej biblioteki w dowolnym miejscu w drzewie katalogów aplikacji, w tym przypadku WEB-INF/struts-html.tld
  • zadeklarować użycie tej biblioteki na początku stron JSP, które z niej korzystają:
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>

Widok formulaire.jsp wykorzystuje tagi, które teraz wyjaśnimy:

balise
<body background="<html:rewrite page="/images/standard.jpg"/>">
traduction HTML
<body background="/formulaire2/images/standard.jpg">
explication
tag html:rewrite pozwala pominąć nazwę aplikacji w plikach URL. Posiada on jeden atrybut:
page
URL – do przepisania
Tak więc, jak widać powyżej, jeśli zdecydujemy się nazwać aplikację „formularz3”, kod atrybutu background nie musi być przepisywany. Tag html:rewrite wygeneruje nowy kod 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
Tag html:form pozwala wygenerować formularz o nazwie HTML. Posiada on kilka atrybutów:
action
akcja Struts, do której ma zostać wysłany formularz – musi odpowiadać jednej z akcji zdefiniowanych w struts-config.xml
name
opcjonalny – nazwa bean’a ActionForm lub pochodnego, w którym mają zostać umieszczone lub odczytane wartości przesłanego formularza. W przypadku braku tego atrybutu wykorzystywany jest formularz powiązany z akcją zdefiniowaną przez atrybut action. Informacja ta znajduje się w pliku konfiguracyjnym (atrybut name akcji).
type
opcjonalny – klasa Java, której instancja ma zostać utworzona dla formularza. W przypadku braku tego atrybutu wykorzystywana jest klasa powiązana z akcją zdefiniowaną przez atrybut action. Informacja ta znajduje się w pliku konfiguracyjnym (atrybut type akcji).
Widać, że domyślnie wygenerowany kod HTML wykorzystuje metodę POST. W tym samym kodzie HTML nazwa akcji URL została przepisana tak, aby na początku znajdowała się nazwa aplikacji, a na końcu rozszerzenie .do.
balise
<html:radio name="dynaFormulaire" property="opt" value="tak">Tak</html:radio>
traduction HTML
<input type="radio" name="opt" value="oui">
explication
tag html:radio służy do generowania tagu HTML <input type="radio" ...>. Obsługuje on różne atrybuty:
name
opcjonalny – nazwa bean’a ActionForm lub pochodnego, w którym ma zostać umieszczona lub odczytana wartość pola. W przypadku braku tego atrybutu wykorzystywany jest formularz powiązany z tagiem <html:form>.
property
nazwa pola w beanie formularza powiązanego z przyciskiem opcji, do odczytu i zapisu.
value
opcjonalne – wartość, jaką należy nadać polu HTML
Tekst między tagiem początkowym a końcowym to tekst, który zostanie wyświetlony obok przycisku opcji.
balise
<html:checkbox name="dynaFormulaire" property="chk1">1</html:checkbox>
traduction HTML
<input type="checkbox" name="chk1" value="on">
explication
tag html:checkbox służy do wygenerowania tagu HTML <input type="checkbox" ...>. Obsługuje on różne atrybuty:
name
opcjonalny – nazwa bean’a ActionForm lub pochodnego, w którym ma zostać umieszczona lub odczytana wartość pola. W przypadku braku tego atrybutu wykorzystywany jest formularz powiązany z tagiem <html:form>.
property
nazwa pola w beanie formularza powiązanego z polem wyboru, z możliwością odczytu i zapisu.
value
opcjonalne – wartość, jaką należy nadać polu HTML
Tekst między tagiem początkowym a końcowym to tekst, który zostanie wyświetlony obok pola wyboru.
balise
<html:text name="dynaFormulaire" property="champSaisie" />
traduction HTML
<input type="text" name="champSaisie" value="">
explication
tag html:text służy do generowania tagu HTML <input type="text" ...>. Obsługuje on różne atrybuty:
name
opcjonalny – nazwa bean’a ActionForm lub pochodnego, w którym ma zostać umieszczona lub odczytana wartość pola. W przypadku braku tego atrybutu wykorzystywany jest formularz powiązany z tagiem <html:form>.
property
nazwa pola w beanie formularza powiązanego z polem wprowadzania danych do odczytu i zapisu.
value
opcjonalne – wartość, którą należy wpisać w polu HTML
balise
<html:password name="dynaFormulaire" property="mdp" />
traduction HTML
<input type="password" name="mdp" value="">
explication
tag html:password służy do generowania tagu HTML <input type="password" ...>. Obsługuje on różne atrybuty:
balise
<html:textarea name="dynaFormulaire" property="boiteSaisie" />
traduction HTML
<textarea name="boiteSaisie"></textarea>
explication
Tag html:textarea służy do wygenerowania tagu HTML <textarea>...</textarea>. Obsługuje on różne atrybuty:
name
opcjonalny – nazwa komponentu typu bean ActionForm lub pochodnego, w którym ma zostać umieszczona lub odczytana wartość pola. W przypadku braku tego atrybutu wykorzystywany jest formularz powiązany z tagiem <html:form>.
property
nazwa pola w beanie formularza powiązanego z polem hasła, z prawem do odczytu i zapisu.
value
opcjonalne – wartość, jaką należy nadać polu HTML
balise
<html:select name="dynaFormulaire" property="combo">....</html:select>
traduction HTML
<select name="combo">...</select>
explication
Tag html:select służy do wygenerowania tagu HTML <select>...</select>. Obsługuje on różne atrybuty:
name
opcjonalny – nazwa bean’a ActionForm lub pochodnego, w którym ma zostać umieszczona lub odczytana wartość pola. W przypadku braku tego atrybutu wykorzystywany jest formularz powiązany z tagiem <html:form>.
property
nazwa pola w beanie formularza powiązanego z polem wprowadzania danych do odczytu i zapisu.
value
opcjonalne – wartość, jaką należy nadać polu HTML. Zostanie wybrana opcja z listy rozwijanej o tej wartości.
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
Tag HTML:options służy do generowania tagów HTML <option>...</option> wewnątrz tagu HTML <select>. Istnieje wiele sposobów określenia, w jaki sposób znaleźć wartości wypełniające listę wyboru. W tym przypadku użyliśmy atrybutów name i property:
name
opcjonalny – nazwa bean’a ActionForm lub pochodnego, w którym ma zostać umieszczona lub odczytana wartość pola. W przypadku braku tego atrybutu wykorzystywany jest formularz powiązany z tagiem <html:form>.
property
nazwa pola w bean formularza, które zawiera wartości z listy rozwijanej. Musi to być tablica ciągów znaków.

Dwie pozostałe listy są generowane analogicznie do poprzedniej:

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

W powyższym przykładzie podano atrybut size o wartości innej niż 1, aby uzyskać listę zamiast pola kombi.

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

Powyżej określono atrybut multiple="true", aby uzyskać listę z możliwością wielokrotnego wyboru.

balise
<html:hidden name="dynaFormulaire" property="secret"/>
traduction HTML
<input type="hidden" name="secret" value="xxx">
explication
tag html:hidden służy do wygenerowania tagu HTML <input type="hidden" ...>.
name
opcjonalne – nazwa bean’a ActionForm lub jego pochodnej, do którego ma zostać umieszczona lub z którego ma zostać odczytana wartość pola. W przypadku braku tego atrybutu wykorzystywany jest formularz powiązany z tagiem <html:form>.
property
nazwa pola w beanie formularza powiązanego z polem ukrytym. Wartość „xxx” przypisana do pola HTML secret pochodzi z definicji formularza w pliku konfiguracyjnym. Zdefiniowano tam wartość początkową „xxx” dla pola secret.

Aby dobrze zrozumieć związek między widokiem formulaire.jsp a beanem dynaFormulaire, który reprezentuje go w pamięci, należy pamiętać, że bean dynaFormulaire służy zarówno do odczytu, jak i zapisu:

Żądanie jest wysyłane, gdy użytkownik kliknie przycisk [Envoyer] w formularzu. Przeglądarka „wysyła” wówczas formularz HTML do akcji /confirmation. Wyjaśniliśmy już, co się wtedy dzieje, a w szczególności, że pola w dynaFormulaire otrzymają wartości z pól o tej samej nazwie w formularzu HTML.

Co się dzieje, gdy kontroler żąda wyświetlenia widoku formulaire.jsp w odpowiedzi na żądanie? Przyjrzyjmy się ponownie tagom jeden po drugim:

balise
<body background="<html:rewrite page="/images/standard.jpg"/>">
fonction
generuje kod HTML
<body background="/formulaire2/images/standard.jpg">
balise
<html:form action="/confirmation" name="dynaFormulaire" type="istia.st.struts.formulaire.DynaFormulaire">
...
</html:form>
fonction
generuje kod HTML
<form name="dynaFormulaire" method="post" action="/formulaire2/confirmation.do">
balise
<html:radio name="dynaFormulaire" property="opt" value="tak">Tak</html:radio>
<html:radio name="dynaFormulaire" property="opt" value="nie">Nie</html:radio>
fonction
Jeśli pole opt w dynaFormulaire ma wartość „tak”, wygeneruj kod 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
Jeśli pola chk1 i chk3 w dynaFormulaire mają wartość „on”, a pole chk2 ma wartość „off”, wygeneruj kod 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
jeśli pole champSaisie ma wartość „to jest test”, generuje kod HTML
<input type="text" name="champSaisie" value="ceci est un essai">
balise
<html:password name="dynaFormulaire" property="mdp" />
fonction
jeśli wartość pola mdp wynosi „azerty”, generuje kod HTML
<input type="password" name="mdp" value="azerty">
balise
<html:password name="dynaFormulaire" property="mdp" />
fonction
jeśli wartość pola mdp wynosi „azerty”, generuje kod HTML
<input type="password" name="mdp" value="azerty">
balise
<html:password name="dynaFormulaire" property="mdp" />
fonction
jeśli wartość pola mdp wynosi „azerty”, generuje kod HTML
<input type="password" name="mdp" value="azerty">
balise
<html:select name="dynaFormulaire" property="combo">
<html:options name="dynaFormulaire" property="valeursCombo"/>
</html:select>
fonction
jeśli wartość pola combo wynosi „combo2”, generuje kod 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
jeśli wartość pola listeSimple wynosi „simple1”, generuje kod 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
jeśli pole listeMultiple jest tablicą {"multiple0", "multiple2"}, generuje kod 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
jeśli pole secret ma wartość „xxx”, generuje kod HTML
<input type="hidden" name="secret" value="xxx">
balise
<html:submit>Wyślij</html:submit>
fonction
generuje kod HTML
<input type="submit" value="Envoyer">

Ostatnią rzeczą, którą należy wyjaśnić, jest kod JavaScript zawarty na stronie JSP i powiązany z dwoma przyciskami [Effacer], które odznaczają elementy zaznaczone na listach listeSimple i listeMultiple:

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

Tag

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

generuje następujący kod HTML:

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

Aby zrozumieć kod JavaScript powiązany z przyciskami [Effacer], przypomnijmy, w jaki sposób różne elementy dokumentu internetowego są oznaczane w kodzie JavaScript wykorzystującym ten dokument:

Dane
Znaczenie
document
oznacza cały dokument internetowy
document.forms
oznacza zbiór formularzy zdefiniowanych w dokumencie
document.forms[i]
oznacza formularz nr i w dokumencie
document.forms["nomFormulaire"]
oznacza formularz <form> o atrybucie name równym „nomFormulaire”
document.nomFormulaire
oznacza formularz <form> z atrybutem name równym „nomFormulaire”
document.[formulaire].elements
oznacza zbiór elementów należących do formularza określonego wyrażeniem [formulaire]. Zbiór ten obejmuje wszystkie tagi <input>, <textarea>, <select> z określonego formularza.
document.[formulaire].elements[i]
oznacza element nr i z [formulaire]
document.[formulaire].elements["nomComposant"]
oznacza element z [formulaire], którego atrybut „name” ma wartość nomComposant
document.[formulaire]. nomComposant
oznacza element [formulaire], którego atrybut name ma wartość nomComposant
document.[formulaire].[composant].value
oznacza wartość komponentu [composant] formularza [formulaire], gdy kod HTML tego formularza może posiadać atrybut value (<input>, <textarea>)
document.[formulaire].[select].selectedIndex
oznacza indeks opcji wybranej z listy. Służy zarówno do odczytu, jak i zapisu. Ustawienie tej właściwości na -1 powoduje odznaczenie wszystkich elementów listy.
document.[formulaire].[select].options
oznacza tablicę opcji powiązanych z tagiem <select>
document.[formulaire].[select].options[i]
oznacza opcję nr i w wskazanym tagu <select>
document.[formulaire].[select].options[i].selected
wartość logiczna wskazująca, czy opcja nr i podanego tagu [select] jest zaznaczona (true), czy nie. Można ją wykorzystywać zarówno do odczytu, jak i zapisu

Wróćmy do kodu JavaScript obu przycisków:

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

Po kliknięciu przycisku wykonywany jest kod powiązany z atrybutem „onclick”. W tym przypadku jest to kod wbudowany. Najczęściej zapisuje się to w postaci onclick="funkcja(...)", gdzie funkcja jest funkcją zdefiniowaną w tagu <script language="javascript">...</script>. Co robi powyższy kod? Omówmy kod pierwszego przycisku:

this
oznacza dokument internetowy, w którym znajduje się przycisk
this.form
oznacza formularz, w którym znajduje się przycisk
this.form.listeSimple
oznacza komponent listeSimple formularza
this.form.listeSimple.selectedIndex
oznacza indeks wybranej opcji w listeSimple. Ustawienie tej właściwości na -1 powoduje odznaczenie wszystkich opcji.

6.4.2. Widok confirmation.jsp

Przypomnijmy, że ten widok jest wyświetlany po zakończeniu akcji /potwierdzenia, c.a.d, po tym, jak formularz zawarty w widoku formulaire.jsp został przesłany przez klienta internetowego. Jego jedynym celem jest wyświetlenie wartości wprowadzonych przez użytkownika. Jego kod jest następujący:

<%@ 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>

W tym miejscu wprowadzamy dwie nowe biblioteki tagów: struts-bean i struts-logic. Biblioteka struts-bean umożliwia dostęp do obiektów umieszczonych w żądaniu, sesji lub kontekście aplikacji. Biblioteka struts-logic umożliwia wprowadzanie logiki wykonawczej za pomocą tagów. Obie te biblioteki nie są w żadnym wypadku niezbędne. Jak widzieliśmy, strona JSP może:

  • pobierać obiekty z żądania (request.getAttribute(...)), sesji (session.getAttribute(...)) lub kontekstu aplikacji
  • zawierać dynamiczne fragmenty w kodzie HTML za pomocą zmiennych <%= zmienna %>
  • zawierać kod Java <% kod Java %>

Włączenie kodu Java do stron JSP przeszkadza wszystkim, którzy pragną ścisłego rozdzielenia logiki aplikacji (kodu Java) od prezentacji (użycia znaczników). Dlatego właśnie stworzono dla nich biblioteki znaczników.

Postąpimy tak samo jak w przypadku widoku formulaire.jsp i wyjaśnimy każdy z tagów obecnych w kodzie confirmation.jsp, o ile nie pojawiły się one już wcześniej w widoku formulaire.jsp. Na początek zauważmy, że strona rozpoczyna się od zadeklarowania trzech bibliotek znaczników, z których będzie korzystać:

<%@ 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" %>

Przypomnijmy również, że te trzy biblioteki muszą być zadeklarowane w pliku web.xml aplikacji. Teraz skomentujemy znaczniki w dokumencie formulaire.jsp:

balise
<bean:write name="dynaFormulaire" scope="session" property="opt"/>
fonction
zapisuje wartość w bieżącym strumieniu HTML. Tag bean:write akceptuje następujące atrybuty:
name: nazwa obiektu, który ma zostać użyty
scope: zakres (request, session, context), w którym należy szukać tego obiektu
property: pole obiektu oznaczonego przez name, którego właściwość ma zostać zapisana. Pole to może być obiektem dowolnego typu. Zastosowana zostanie metoda toString tego obiektu.
W tym przypadku zapisywana jest wartość pola opt obiektu dynaFormulaire. Wynikiem będzie „tak”, jeśli użytkownik zaznaczył przycisk opcji o atrybucie value="tak", lub „nie”, jeśli zaznaczył przycisk opcji o atrybucie value="nie"
balise
<bean:write name="dynaFormulaire" scope="session" property="chk1"/>
fonction
zapisuje wartość pola chk1 z dynaFormulaire. Wynikiem będzie „on”, jeśli użytkownik zaznaczył pole wyboru, lub „off” w przeciwnym razie. To samo dotyczy pól chk2 i chk3.
balise
<bean:write name="dynaFormulaire" scope="session" property="champSaisie"/>
fonction
zapisuje wartość pola champSaisie z pola dynaFormulaire, czyli tekst wpisany przez użytkownika w tym polu. To samo dotyczy pola mdp, boiteSaisie.
balise
<bean:write name="dynaFormulaire" scope="session" property="combo"/>
fonction
zapisuje wartość pola rozwijanego dynaFormulaire. Otrzymamy atrybut „value” elementu <option> wybranego przez użytkownika.
balise
<bean:write name="dynaFormulaire" scope="session" property="listeSimple"/>
fonction
zapisuje wartość pola listeSimple z dynaFormulaire. Otrzymamy atrybut „value” elementu <option> wybranego przez użytkownika, jeśli taki istniał. W przeciwnym razie otrzymamy pusty ciąg znaków.
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
W tym miejscu wprowadzamy znaczniki logiczne. Mamy do czynienia z listą wielokrotnego wyboru. Wartość pola listeMultiple obiektu dynaFormulaire jest tablicą typu String. W języku Java napisalibyśmy pętlę. Znacznik logic:iterate pozwala nam wykonać tę samą pętlę bez pisania kodu w Javie. W tym przykładzie znacznik logic:iterate ma następujące atrybuty:
name="dynaFormulaire": nazwa obiektu, który ma zostać użyty
property="listeMultiple": nazwa właściwości, która w obiekcie wskazanym przez atrybut name zawiera kolekcję, która będzie przeglądana w pętli. W tym przypadku kolekcją tą jest tablica wartości wybranych w obiekcie listeMultiple. Tablica ta może być pusta.
id="choix": identyfikator wskazujący bieżący element tablicy w każdej iteracji pętli. Podczas pierwszej iteracji „choix” będzie reprezentować listeMultiple[0], podczas drugiej – listeMultiple[1] i tak dalej.
indexID="index": identyfikator wskazujący indeks bieżącego elementu tablicy w każdej iteracji pętli. W pierwszej iteracji index będzie miał wartość 0, w drugiej – 1 i tak dalej.
Kod HTML zawarty między tagami <logic:iterate ...> i </logic:iterate> jest powtarzany dla każdego elementu kolekcji wskazanej przez parę (name,property). Dynamiczna część tego kodu wygląda następująco:
          <td>liste multiple[<bean:write name="index"/>]</td>
          <td><bean:write name="choix"/></td>
Z powyższego wynika, że w iteracji nr i (i>=0) wygenerowany kod HTML jest równoważny następującemu kodowi:
          <td>liste multiple[<%=i%>]</td>
          <td><%=listeMultiple[i]%></td>
balise
    <html:link page="/affiche.do">
            Retour au formulaire
        </html:link>    
fonction
generuje link powiązany z kontekstem aplikacji, dzięki czemu nie trzeba go znać. Kod HTML wygenerowany przez ten tag wygląda następująco:
<a href="/formulaire2/affiche.do">Retour au formulaire</a>

6.5. Klasy Java

Plik konfiguracyjny struts-config.xml odwołuje się do dwóch klas 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"
      >

Klasa DynaFormulaire to klasa, która będzie zawierać wartości z widoku nr 1 formulaire.jsp. Klasa InitFormulaireAction jest klasą, która będzie przetwarzać wartości formularza przesłanego za pomocą przycisku [Envoyer] z formulaire.jsp.

6.5.1. Klasa DynaFormulaire

Aby przechowywać wartości formularza, wystarczy obiekt typu DynaActionForm, chyba że konieczne jest przedefiniowanie jednej z metod reset lub validate tej klasy. W tym przypadku metoda validate nie wymaga przedefiniowania, ponieważ nie przeprowadzamy żadnej walidacji danych. Należy jednak zdefiniować na nowo metodę reset. Pola obiektu DynaFormulaire otrzymają bowiem swoje wartości z formularza przesłanego przez klienta internetowego. Niektóre pola mogą jednak nie otrzymać wartości, jeśli nie występują w żądaniu. Dzieje się tak w następujących przypadkach:

  • pole wyboru, które nie zostało zaznaczone przez użytkownika
  • lista zawierająca więcej niż jedną pozycję lub nie wybrano żadnej opcji

W przypadku formularzy zawierających ten typ komponentu metoda reset musi

  • przypisać wartość „off” do pola powiązanego z pola wyboru
  • przypisać pusty ciąg znaków do pola powiązanego z listą z jednym wyborem
  • przypisać tablicę o zerowej długości zawierającą ciągi znaków do pola powiązanego z listą wielokrotnego wyboru

W ten sposób, jeśli pola te nie otrzymają wartości z zapytania, zachowują wartość przypisaną przez metodę reset, która odpowiada stanowi komponentu w formularzu zatwierdzonym przez użytkownika (pole niezaznaczone, lista bez żadnego wybranego elementu).

Kod klasy DynaFormulaire, klasy pochodnej od DynaActionForm, jest następujący:

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){
    // resetowanie pól wyboru – wartość „off”
    set("chk1","off");
    set("chk2","off");
    set("chk2","off");
    // reset listeSimple – pusty ciąg znaków
    set("listeSimple","");
     // resetowanie listeMultiple – pusta tablica
    set("listeMultiple",new String[]{});
  }
}

6.5.2. Klasa InitFormulaireAction

Klasa InitFormulaireAction jest w pliku struts-config.xml powiązana z akcją /init:

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

Akcja /init jest wykorzystywana tylko raz podczas początkowego tworzenia obiektu DynaFormulaire. Jej celem jest dostarczenie treści do trzech list w formularzu rozwijanym: listeSimple, listeMultiple. Treść ta jest dostarczana w postaci trzech tablic, będących właściwościami obiektu 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>            

Gdy tabele valeursCombo, valeursListeSimple, valeursListeMultiple zostaną zainicjowane przez InitFormulaireAction, nie będzie już konieczne ich ponowne inicjowanie. Obiekt dynaFormulaire jest bowiem umieszczany w sesji i dzięki temu zachowuje swoją wartość w kolejnych cyklach żądanie-odpowiedź. Dlatego akcja /init jest wykonywana tylko raz. Kod InitFormulaireAction wygląda następująco:

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 {

     // przygotowuje formularz do wyświetlenia

     // umieszczamy niezbędne informacje dotyczące formularza w jego bean
    DynaFormulaire formulaire = (DynaFormulaire) form;
    formulaire.set("valeursCombo", getValeurs(5, "combo"));
    formulaire.set("valeursListeSimple", getValeurs(7, "simple"));
    formulaire.set("valeursListeMultiple", getValeurs(10, "multiple"));
     // zwracamy kontrolę
    return mapping.findForward("afficherFormulaire");
  } //wykonuje

   // lista wartości z listy rozwijanej
  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;
  }
}
  • Klasa ta dziedziczy po klasie Action. Jest to obowiązkowe.
  • Kontroler Struts wykorzystuje obiekt Action za pośrednictwem metody execute. Należy więc zdefiniować właśnie tę metodę. Metoda ta przyjmuje następujące parametry:
    • ActionMapping mapping: obiekt reprezentujący konfigurację aplikacji w struts-config.xml
    • ActionForm form: formularz powiązany z akcją, o ile został zdefiniowany w konfiguracji akcji (atrybut name akcji).
    • HttpServletRequest request: żądanie klienta
    • HttpServletResponse: odpowiedź dla klienta
  • klasa InitFormulaireAction musi zainicjować formularz dynaFormulaire. Formularz ten trafia do metody execute w postaci parametru ActionForm form. Przypomnijmy, że dynaFormulaire jest typu DynaFormulaire, klasy pochodnej od klasy DynaActionForm, która z kolei jest pochodną klasy ActionForm.
  • W metodzie `execute` wartości są przypisywane do trzech pól: `valeursCombo`, `valeursListeSimple` i `valeursListeMultiple` za pomocą metody `set` klasy `DynaActionForm`. Wartości te są dowolnymi tablicami, co ma na celu uproszczenie. Należy zauważyć, że metoda set przypisuje wartość do istniejącego pola. Nie można jej używać do tworzenia nowych pól. Dlatego konieczne jest zdefiniowanie trzech pól valeursCombo, valeursListeSimple, valeursListeMultiple w definicji obiektu dynaFormulaire w struts-config.xml.
  • Metoda `execute` kończy działanie, zwracając do kontrolera klucz widoku, który ma zostać wyświetlony jako odpowiedź dla klienta. W tym przypadku jest to klucz afficherFormulaire, który w pliku struts-config.xml został powiązany z widokiem /vues/formulaire.jsp.

6.6. Wdrożenie

Struktura drzewa aplikacji wygląda następująco:

  

Image

Image

Przypomnijmy, że powyższy plik ApplicationResources.properties jest niezbędny dla biblioteki tagów struts-bean. Wiadomo, że plik ten zawiera komunikaty aplikacji. Są one dostępne dla biblioteki struts-bean. W tym przypadku nasza aplikacja nie definiuje żadnych komunikatów. Plik ApplicationResources.properties istnieje, ale jest pusty.

6.7. Wnioski

W tej lekcji szczegółowo omówiliśmy sposób zarządzania różnymi komponentami formularza HTML. Teraz możemy wykorzystywać złożone formularze w naszych aplikacjach Struts.