Skip to content

9. Voorbeeld 07 – Formuliertags

We gaan het volgende formulier bouwen en gebruiken:

We gaan het concept van parameterinjectie, dat we eerder hebben gezien voor een tekstinvoerveld, uitbreiden naar de andere HTML-elementen van een formulier.

9.1. Het NetBeans-project

  • in [1], de weergaven van het project [Formx.JSP]
  • in [2], het Struts-configuratiebestand en de geïnternationaliseerde berichten
  • in [3], de acties [Formx.java] en nog een Struts-configuratiebestand.

9.2. Struts-configuratie

Het bestand [struts.xml] ziet er als volgt uit:


<?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>
  • regel 9: bevat een ander configuratiebestand, [example.xml]. Dit bestand configureert de acties.
  • regels 11-19: het pakket default. Hierin wordt een omleidingsadres gedefinieerd voor de map / van URL. Deze wordt omgeleid naar de map /example/Form.action van URL.

Het bestand [example.xml] waarin de acties worden geconfigureerd, ziet er als volgt uit:


<?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>
  • regels 7-17: definiëren de acties /example/Form, /example/Form1 en /example/Form2, waarbij /example de naamruimte is (regel 7) en Formx de acties.
  • regels 8-10: wanneer de actie Form wordt uitgevoerd, wordt de weergave /example/Form.JSP weergegeven.
  • regels 11-13: het uitvoeren van de actie Form1 zorgt ervoor dat de weergave /example/Form1.JSP wordt weergegeven.
  • regels 14-16: het uitvoeren van de actie Form2 zorgt ervoor dat de weergave van de view /example/Form2.JSP wordt weergegeven.

9.3. De berichtbestanden

We zullen niet verder ingaan op de internationalisering van het project. Dit hebben we al behandeld in voorbeeld 01. We geven de inhoud van het bestand [messages.properties] weer, zodat de lezer de volgende weergaven beter kan begrijpen.


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. De weergave [Form.JSP] – invoergedeelte

Het ziet er als volgt uit:

 

Dit is het volgende:


<%@ 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>
  • Het formulier begint op regel 26 en eindigt op regel 38. Op regel 26 ontbreekt het attribuut action. Dit attribuut geeft aan naar welke actie de gegevens van het formulier worden verzonden. Bij afwezigheid van het attribuut action worden de gegevens verzonden naar de actie die heeft geleid tot de weergave van de weergave, in dit geval de actie [Form].
  • regel 27: een invoerveld. Het attribuut key geeft de tekst van het veld aan. Het attribuut name is de naam van de parameter die wordt verzonden. De verzonden tekenreeks voor dit veld heeft de volgende vorm:
textfield=texte_saisi

De naam van de parameter komt overeen met een veld van de actie [Form]. De waarde van het invoerveld texte_saisi wordt hier ingevoerd in het veld textfield van de actie [Form]. Omgekeerd geldt dat, als de weergave [Form.JSP] wordt weergegeven na de actie [Form], de waarde van het invoerveld gelijk is aan die van het veld textfield van de actie [Form]. Dit veld wordt dus zowel voor lezen als voor schrijven gebruikt. De klasse [Form] moet hiervoor de methoden getTextField en setTextField bevatten.

Deze redenering geldt voor alle invoertags die hierna volgen. We zullen dit niet herhalen.

  • regel 28: invoer van een wachtwoord. De tekenreeks die naar de actie [Form] wordt verzonden, heeft de vorm
password=mot_de_passe_saisi

Er moet een veld password met bijbehorende get/set-methoden bestaan in de actie [Form].

  • regel 29: invoer van een tekst met meerdere regels in een gebied van 5 regels en 40 kolommen. De tekenreeks die naar de actie [Form] wordt verzonden, heeft de vorm
textarea=saisie

Er moet een veld textarea met bijbehorende get/set-methoden aanwezig zijn in de actie [Form].

  • regel 30: een dropdownlijst (size=1) met enkele selectie. Er kan slechts één waarde uit de lijst worden geselecteerd. De string die naar de actie [Form] wordt verzonden, heeft de volgende vorm:
select1=valeur_sélectionnée

Er moet een veld select1 met bijbehorende get/set-methoden aanwezig zijn in de actie [Form].

Het attribuut key is de tekst van de lijst. Het attribuut list definieert de lijst met waarden die in de keuzelijst moeten worden geplaatst. Hier wordt de methode getSelect1Values van de actie [Form] aangeroepen om de keuzelijst te vullen. Deze methode kan een verzameling, een woordenboek of een array retourneren. In dit geval retourneert deze methode een array met tekenreeksen [chaine1, chaine2, ...], die de volgende HTML-code genereert:


<option value="chaine1">chaine1</option>
<option value="chaine2">chaine2</option>
...

De tekst tussen de tags <option>...</option> wordt weergegeven in de keuzelijst. Het attribuut value geeft de waarde aan die moet worden verzonden als de optie door de gebruiker wordt geselecteerd. Hier zijn het attribuut value en de tekst die in de keuzelijst wordt weergegeven identiek. Meestal is dat niet het geval. Als de tweede optie wordt geselecteerd, wordt de volgende tekenreeks verzonden naar de actie [Form]:

select1=chaine2

Het veld [Form].select1 krijgt dan de waarde chaine2. Omgekeerd, als dit veld op het moment dat het formulier wordt weergegeven de waarde chaine3 heeft, dan is visueel gezien het derde element


<option value="chaine3">chaine3</option>

visueel geselecteerd in de keuzelijst.

Het attribuut headerValue bepaalt de tekst die als eerste optie in de keuzelijst moet worden weergegeven en headerKey de waarde die moet worden verzonden als deze optie door de gebruiker wordt gekozen.

  • regel 31: ook hier weer een lijst met 3 items (size=3): in tegenstelling tot de vorige lijst, waar slechts één item zichtbaar is, zijn hier 3 items uit de lijst zichtbaar. De selectiemodus blijft hetzelfde. Er kan slechts één item worden geselecteerd.
  • regel 32: opnieuw een lijst, maar nu met meervoudige selectie (multiple=true). In dit geval zal de waarde die naar de actie [Form] wordt verzonden, de volgende vorm hebben:
select3=chaine2&select3=chaine5

als de opties chaine2 en chaine5 zijn geselecteerd. Wanneer er meerdere waarden worden geboekt voor dezelfde parameter, in dit geval select3, moet de actie een veld hebben met de naam van de parameter en van het type tableau zijn. Zo zou de actie [Form] in dit geval bijvoorbeeld een veld kunnen hebben van het type:

String[] select3 ;

en de bijbehorende get-/set-methoden. De array select3 ontvangt dan de array met tekenreeksen [chaine2,chaine5]. Omgekeerd bepalen bij het weergeven van de weergave [Form.JSP] de waarden van het veld [Form].select3 welke opties in de lijst select3 geselecteerd zullen worden weergegeven. Dit is nog steeds een lees-/schrijfbewerking.

  • regel 33: een groep keuzerondjes. De tekst van de groep wordt opgegeven door het attribuut key. De teksten van de afzonderlijke keuzerondjes worden opgegeven door het attribuut list. De waarde van dit attribuut is de naam van een methode van de actie [Form] die een verzameling, een woordenboek of een array moet retourneren. Hier zal de methode [Form].getRadioValues een array van tekenreeksen [chaine1,chaine2,...] retourneren, die de volgende HTML-tags genereert:

<input type="radio" name="radio" ... value="chaine1"/>chaine1
<input type="radio" name="radio" ... value="chaine2"/>chaine2
...

Als het keuzerondje chaine2 is aangevinkt, wordt de volgende waarde verzonden:

radio=chaine2

en deze waarde wordt ingevuld in het veld [Form].radio.

  • regel 34: een selectievakje. Als dit is aangevinkt, wordt de volgende parameterreeks verzonden naar de actie [Form]
checkbox=true

Als het vakje niet is aangevinkt, wordt er geen parameterreeks verzonden. Struts 2 heeft een intern mechanisme waardoor alles verloopt alsof de parameterreeks

checkbox=false

was verzonden.

  • regel 35: een lijst met selectievakjes. Er kunnen meerdere worden aangevinkt. De groepstitel wordt geleverd door het attribuut key. De labels van de afzonderlijke selectievakjes worden geleverd door het attribuut list. De waarde van dit attribuut is de naam van een methode van de actie [Form] die een verzameling, een woordenboek of een array moet retourneren. Hier zal de methode [Form].getCheckboxlistValues een array van tekenreeksen [chaine1,chaine2,...] retourneren, die de volgende HTML-tags genereert:

<input type="checkbox" name="checkboxlist" ... value="chaine1"/>chaine1
<input type="checkbox" name="checkboxlist" ... value="chaine2"/>chaine2
...

Als de gebruiker de selectievakjes met de labels chaine2 en chaine5 selecteert, wordt de volgende tekenreeks verzonden naar de actie [Form]:

checkboxlist=chaine2&checkboxlist=chaine5

De tabel [chaine2, chaine5] wordt toegewezen aan het veld [Form].checkboxlist, dat daarom van het type tekenreeks-tabel moet zijn, net als bij lijsten met meerdere selecties. Omgekeerd bepalen bij de weergave van de weergave [Form.JSP] de waarden van het veld [Form].checkboxlist welke selectievakjes zullen worden aangevinkt.

  • regel 36: een verborgen veld. Een verborgen veld is een veld dat wordt verzonden zonder dat de gebruiker het invult. De waarde ervan wordt door de programmeur vastgesteld. Hier haalt het verborgen veld zijn waarde uit het veld [Form].hidden. Dezelfde waarde wordt verzonden in de vorm:
hidden=qqchose

en opnieuw toegewezen aan het veld [Form].hidden.

  • regel 37: een knop van het type submit. Als er op deze knop wordt geklikt, worden alle waarden van het formulier verzonden naar de actie [Form]. De reeks verzonden parameters ziet er als volgt uit:
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. De actie [Form] – invoergedeelte

De code hiervoor is:


package example;

import com.opensymphony.xwork2.ActionSupport;

public class Form extends ActionSupport {

  // constructor zonder parameters
  public Form() {
  }
  // formuliervelden
  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;

  // geselecteerde waarden in het select3-veld
  public String getSelect3SelectedValues() {
    return getValue(select3);
  }

  // geselecteerde waarden in het veld 'checkboxlist'
  public String getCheckboxlistSelectedValues() {
    return getValue(checkboxlist);
  }

  // hulpprogramma
  public String getValue(String[] values) {
    String result = "";
    for (String value : values) {
      result += " " + value;
    }
    return result;
  }

// getters en setters van de velden
  ....
}
  • regel 1: het veld dat gekoppeld is aan de tag textfield van het formulier. Wordt gebruikt om dit invoerveld te initialiseren en om de waarde ervan op te halen. Ontvangt op het moment van de actie POST de waarde die in dit veld is ingevoerd.
  • regel 2: het veld dat gekoppeld is aan de tag password van het formulier. Ontvangt op het moment van de POST de waarde die in dit veld is ingevoerd.
  • regel 13: gekoppeld aan de tag textarea van het formulier. Ontvangt op het moment van POST de tekst die in dit veld is ingevoerd.
  • regel 14: gekoppeld aan de tag select1 van het formulier, een keuzelijst met één selectie. De beginwaarde selecteert een item uit de keuzelijst. Het item waarvan het attribuut value gelijk is aan deze waarde, wordt geselecteerd. Op het moment van de POST krijgt het het attribuut value van de geselecteerde optie in de keuzelijst.
  • regel 15: de tekenreeksen die de keuzelijst select1 zullen vullen.
  • regels 16 en 17: idem select1
  • regel 18: gekoppeld aan de tag select1 van het formulier, een keuzelijst. De beginwaarden in de tabel selecteren de overeenkomstige elementen uit de lijst. De elementen waarvan het attribuut value gelijk is aan een van deze elementen, worden geselecteerd. Op het moment van de POST ontvangt deze de value-attributen van de verschillende opties die in de keuzelijst zijn geselecteerd.
  • regel 19: de tekenreeksen die de lijst select3 zullen vullen
  • regel 20: gekoppeld aan de tag radio van het formulier. De waarde ervan wordt gebruikt om een van de keuzerondjes aan te vinken, namelijk degene waarvan het attribuut value gelijk is aan deze waarde. Ontvangt op het moment van POST de waarde van de aangevinkte keuzeknop.
  • regel 21: de verschillende labels en waarden van de keuzerondjes.
  • regel 22: gekoppeld aan de tag checkbox van het formulier. De beginwaarde wordt gebruikt om het selectievakje aan of uit te vinken. Krijgt op het moment van POST de waarde true als het selectievakje is aangevinkt, anders false.
  • regel 23: gekoppeld aan de tag checkboxlist van het formulier. De beginwaarden in de tabel selecteren de bijbehorende selectievakjes. De selectievakjes waarvan het attribuut value gelijk is aan een van deze beginwaarden, worden aangevinkt. Op het moment van POST ontvangt het de value-attributen van de verschillende aangevinkte vakjes.
  • regel 24: de tekenreeksen die de labels van de selectievakjes in checkboxlist zullen vullen.
  • regel 25: gekoppeld aan het verborgen veld hidden. De beginwaarde bepaalt de waarde van het verborgen veld. Bij de POST krijgt het dezelfde waarde, omdat de gebruiker deze niet kan wijzigen.
  • regel 26: gekoppeld aan de tag submit. Bij de uitvoering van POST krijgt deze de tekst van de knop. Dit veld is niet noodzakelijk.
  • De methoden op de regels 29, 34 en 39 worden verderop toegelicht.

9.6. Het scherm [Form.JSP] – gedeelte Bevestiging

Het scherm [Form.JSP] heeft een gedeelte „Bevestiging van de invoer“ dat nog niet is besproken:

 

De code hiervan is als volgt:


<%@ 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>
  • regel 13: toont de waarde van het veld [Form].textfield
  • regel 17: geeft de waarde weer van het veld [Form].password
  • regel 21: geeft de waarde weer van het veld [Form].textarea
  • regel 25: geeft de waarde weer van het veld [Form].select1
  • regel 29: geeft de waarde van het veld [Form].select2 weer
  • regel 33: geeft de waarden van de tabel [Form].select3 weer. Gebruik hiervoor de methode [Form].getSelect3SelectedValues.
  • regel 37: geeft de waarde van het veld [Form].radio weer
  • regel 41: geeft de waarde van het veld [Form].checkbox weer
  • regel 45: geeft de waarden van de tabel [Form].checkboxlist weer. Gebruik hiervoor de methode [Form].getCheckboxlistSelectedValues.
  • regel 49: geeft de waarde van het veld [Form].hidden weer

9.7. De actie [Form] – bevestigingsgedeelte

De eerder genoemde methoden getSelect3SelectedValues en getCheckboxlistSelectedValues zijn gedefinieerd in de klasse [Form]:


  // geselecteerde waarden in het select3-veld
  public String getSelect3SelectedValues() {
    return getValue(select3);
  }

  // geselecteerde waarden in het checkboxlist-veld
  public String getCheckboxlistSelectedValues() {
    return getValue(checkboxlist);
  }

  // hulpprogramma-methode
  public String getValue(String[] values) {
    String result = "";
    for (String value : values) {
      result += " " + value;
    }
    return result;
}

Ze voegen gewoon de elementen van de tabellen die ze moeten weergeven aan elkaar toe.

9.8. De tests

Bij het uitvoeren van het NetBeans-project verschijnt de volgende startpagina:

De waarden die worden weergegeven in [1] en [2] zijn afkomstig van de beginwaarden van de velden van de actie [Form]. Laten we enkele voorbeelden bekijken:


private String textarea = "ligne1\nligne2\n";

De waarde van het veld textarea verklaart de weergaven [1A] en [2A].


private String[] select3 = new String[]{"zéro", "deux"};
private String[] select3Values = new String[]{"zéro", "un", "deux", "trois", "quatre"};

De waarde van het veld select3Values verklaart de inhoud van de lijst in [1B]. De waarde van het veld select3 verklaart de geselecteerde elementen in [1B] die worden weergegeven in [2B].

Als we andere waarden in het formulier invoeren en dit valideren, worden de ingevoerde waarden naar de overeenkomstige velden van de actie [Form] verzonden. Vervolgens wordt de weergave [Form.JSP] opnieuw weergegeven. We bevinden ons dus in hetzelfde geval als eerder, behalve dat de oorspronkelijke waarden zijn vervangen door de verzonden waarden. Hier volgt een voorbeeld:

9.9. De actie [Form1] en de weergave [Form1.JSP]

Het configuratiebestand [example.xml] configureert de volgende actie [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>

Regels 11-13: de aanroep van de actie [Form1] genereert de weergave [Form1.JSP].

De actie [Form1] is als volgt:


package example;

import com.opensymphony.xwork2.ActionSupport;

public class Form1 extends ActionSupport{

  // constructor zonder parameters
  public Form1() {
  }
  // formuliervelden
  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;
  }
}

De actie [Form1] is identiek aan de actie [Form], behalve dat het sjabloongedeelte ervan is ondergebracht in een bijbehorende klasse, de klasse [Data], regel 11. De klasse [Data] ziet er als volgt uit:


package example;

public class Data {

  // constructor zonder parameters
  public Data() {
  }
  
// formuliervelden
  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;

  // geselecteerde waarden in het select3-veld
  public String getSelect3SelectedValues() {
    return getValue(select3);
  }

  // geselecteerde waarden in het checkboxlist-veld
  public String getCheckboxlistSelectedValues() {
    return getValue(checkboxlist);
  }

  // hulpprogramma
  public String getValue(String[] values) {
    String result = "";
    for (String value : values) {
      result += " " + value;
    }
    return result;
  }

  // getters en setters
....

}

We vinden de eerder gedeclareerde velden terug in de actie [Form].

Het is logisch dat de weergave [Form1.JSP] veel lijkt op de weergave [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>

Het verschil tussen de weergaven [Form] en [Form1] kan worden geïllustreerd aan de hand van regel 27, die hieronder is weergegeven:


<s:textfield name="data.textfield" key="Form.textfield" />

Het invoerveld is zowel voor lezen als schrijven gekoppeld aan het veld data.textfield van de actie [Form1]. Ter herinnering: na uitvoering zijn de eigenschappen van de actie [Form1] toegankelijk voor de weergave. Het voorgaande attribuut name wordt dus geëvalueerd als [Form1].getData().getTextField(). De twee voorgaande methoden get moeten dus bestaan.

9.10. De actie [Form2] en de weergave [Form2.JSP]

Het configuratiebestand [example.xml] configureert de volgende actie [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>

Regels 14-16: de aanroep van de actie [Form2] genereert de weergave [Form2.JSP].

De actie [Form2] is als volgt:


package example;

import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;

public class Form2 extends ActionSupport implements ModelDriven {

  // constructor zonder parameters
  public Form2() {
  }
  // actiemodel
  public Object getModel() {
    return new Data();
  }
}
  • regel 6: de actie [Form2] implementeert de interface [ModelDriven]. Er hoeft slechts één methode te worden geïmplementeerd, namelijk de methode getModel op regel 12. Deze methode retourneert een instantie van de eerder besproken klasse [Data].

Het feit dat de actie [Form2] de interface [ModelDriven] implementeert, heeft het volgende gevolg: de weergave die wordt getoond na de actie [Form2] heeft directe toegang tot de eigenschappen van het model van de actie [Form2], aangezien dit wordt gerenderd door de methode getModel(). Daardoor wordt de weergave [Form2.JSP] weer zoals deze was bij de oorspronkelijke actie [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>

Zo was regel 27 voorheen:


<s:textfield name="data.textfield" key="Form.textfield" />

weer:


<s:textfield name="textfield" key="Form.textfield" />

Het voordeel van het buiten de weergave plaatsen van het model is dat deze architectuur de functionaliteiten van elk element van de MVC-architectuur duidelijker afbakent. Het M-model wordt duidelijk geïdentificeerd door een klasse. Bovendien kan het model al bestaan voordat de acties er zijn. Als een webapplicatie bijvoorbeeld een database met personen beheert, is de kans groot dat de klasse Personne al bestaat. Als een actie een invoerformulier aanbiedt om een nieuwe persoon aan te maken, ligt het model voor deze actie voor de hand: dat is de klasse Personne.

We hebben gezien hoe eenvoudig het was om van de actie [Form] over te stappen naar de actie [Form2]:

  • het model M van de weergave V is ingekapseld in een aparte klasse
  • de actie die de weergave genereert die gebruikmaakt van dit model, implementeert de interface ModelDriven met de methode getModel die een instantie van het model M retourneert. De actie kan dan worden gezien als een uitbreiding van de Struts 2-controller C, de klasse FilterDispatcher.

De lezer kan deze aanpak toepassen op elke actie die hierna wordt beschreven.