Skip to content

9. Esempio 07 – Tag dei moduli

Creeremo e utilizzeremo il seguente modulo:

Image

Image

Estenderemo il concetto di iniezione dei parametri visto per un campo di immissione testo ad altri elementi HTML in un modulo.

9.1. Il progetto NetBeans

  • in [1], le viste del progetto [Formx.jsp]
  • in [2], il file di configurazione Struts e i messaggi internazionalizzati
  • in [3], le azioni [Formx.java] e un altro file di configurazione Struts.

9.2. Configurazione di Struts

Il file [struts.xml] è il seguente:


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

  • Riga 9: include un altro file di configurazione [example.xml]. Questo file configura le azioni.
  • Righe 11–19: il pacchetto predefinito. Definisce un indirizzo di reindirizzamento per l'URL /. Verrà reindirizzato all'URL /example/Form.action.

Il file [example.xml] che configura le azioni è il seguente:


<?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>
  • Righe 7–17: definiscono le azioni /example/Form, /example/Form1, /example/Form2, dove /example è lo spazio dei nomi (riga 7) e Formx sono le azioni.
  • Righe 8-10: l'esecuzione dell'azione Form visualizzerà la vista /example/Form.jsp.
  • Righe 11-13: l'esecuzione dell'azione Form1 visualizzerà la vista /example/Form1.jsp.
  • Righe 14–16: l'esecuzione dell'azione Form2 visualizzerà la vista /example/Form2.jsp.

9.3. File dei messaggi

Non ci soffermeremo sull'internazionalizzazione del progetto. Ne abbiamo già parlato nell'Esempio 01. Forniamo il contenuto del file [messages.properties] in modo che il lettore possa comprendere le viste che seguono.


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. La vista [Form.jsp] – sezione di immissione dati

Ha questo aspetto:

Image

È la seguente:


<%@ 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>
  • Il modulo inizia alla riga 26 e termina alla riga 38. Alla riga 26 manca l'attributo `action`. Questo attributo specifica l'azione a cui verranno inviati i dati del modulo. Se l'attributo `action` manca, i dati verranno inviati all'azione che ha causato la visualizzazione della vista, in questo caso l'azione `[Form]`.
  • Riga 27: un campo di immissione. L'attributo `key` specifica la sua etichetta. L'attributo `name` è il nome del parametro che verrà inviato. La stringa inviata per questo campo avrà la forma:

textfield=text_entered

Il nome del parametro corrisponde a un campo nell'azione [Form]. Il valore del campo di immissione text_saisi verrà inserito nel campo textfield dell'azione [Form]. Al contrario, se la vista [Form.jsp] viene visualizzata a seguito dell'azione [Form], il valore del campo di immissione sarà quello del campo textfield nell'azione [Form]. Questo campo viene quindi utilizzato sia per la lettura che per la scrittura. La classe [Form] deve disporre dei metodi getTextField e setTextField a questo scopo.

Questo ragionamento si applica a tutti i tag di input successivi. Non lo ripeteremo.

  • Riga 28: inserimento della password. La stringa inviata all'azione [Form] avrà la forma

password=password_inserita

Nell'azione [Form] deve esistere un campo password con i relativi metodi get/set.

  • Riga 29: inserimento di testo su più righe in un'area di 5 righe e 40 colonne. La stringa inviata all'azione [Form] avrà il formato

textarea=input

Nell'azione [Form] deve essere presente un campo textarea con i relativi metodi get/set.

  • Riga 30: un elenco a discesa a selezione singola (size=1). È possibile selezionare un solo valore dall'elenco. La stringa inviata all'azione [Form] avrà la forma:

select1=valore_selezionato

Nell'azione [Form] deve essere presente un campo select1 con i relativi metodi get e set.

L'attributo key è l'etichetta dell'elenco. L'attributo list definisce l'elenco dei valori da inserire nella casella combinata. In questo caso, verrà chiamato il metodo getSelect1Values dell'azione [Form] per popolare la casella combinata. Questo metodo può restituire una collezione, un dizionario o un array. In questo caso, restituirà un array di stringhe [string1, string2, ...] che genererà il seguente codice HTML:

<option value="string1">string1</option>

<option value="string2">string2</option>

...

Il testo tra i tag <option>...</option> verrà visualizzato nella casella combinata. L'attributo value specifica il valore da inviare se l'opzione viene selezionata dall'utente. In questo caso, l'attributo value e il testo visualizzato nella casella combinata sono identici. Molto spesso, però, non è così. Se viene selezionata la seconda opzione, la seguente stringa verrà inviata all'azione [Form]:

select1=string2

Il campo [Form].select1 riceverà quindi il valore string2. Al contrario, se al momento della visualizzazione del modulo questo campo è impostato su string3, visivamente il terzo elemento

<option value="string3">string3</option>

apparirà selezionato nella casella combinata.

L'attributo headerValue imposta il testo da visualizzare come prima opzione nella casella combinata, mentre headerKey imposta il valore da inviare se questa opzione viene selezionata dall'utente.

  • Riga 31: ancora una volta, un elenco di dimensione 3 (size=3): a differenza dell'elenco precedente in cui è visibile un solo elemento, qui saranno visibili 3 elementi dell'elenco. La modalità di selezione rimane la stessa. È possibile selezionare un solo elemento.
  • Riga 32: un altro elenco, ma questa volta con selezione multipla (multiple=true). In questo caso, il valore inviato all'azione [Form] avrà la forma:

select3=string2&select3=string5

se sono state selezionate le opzioni string2 e string5. Quando vengono inviati più valori per lo stesso parametro — in questo caso, select3 — l'azione deve avere un campo con il nome del parametro ed essere di tipo array. Quindi, ad esempio, qui l'azione [Form] potrebbe avere un campo come:

String[] select3;

e i corrispondenti metodi get/set. L'array select3 riceverà quindi l'array di stringhe di caratteri [string2, string5]. Al contrario, quando viene visualizzata la vista [Form.jsp], i valori del campo [Form].select3 determinano quali opzioni nell'elenco select3 appariranno selezionate. Si tratta sempre di un'operazione di lettura/scrittura.

  • Riga 33: un gruppo di pulsanti di opzione. L'etichetta del gruppo è fornita dall'attributo key. Le etichette dei singoli pulsanti sono fornite dall'attributo list. Il valore di questo attributo è il nome di un metodo nell'azione [Form] che deve restituire una collezione, un dizionario o un array. Qui, il metodo [Form].getRadioValues restituirà un array di stringhe [string1, string2, ...] che genererà i seguenti tag HTML:

<input type="radio" name="radio" ... value="string1"/>string1

<input type="radio" name="radio" ... value="string2"/>string2

...

Se viene selezionato il pulsante di opzione string2, il valore inviato sarà

radio=string2

e questo valore verrà inserito nel campo [Form].radio.

  • Riga 34: una casella di controllo. Se selezionata, la seguente stringa di parametri verrà inviata all'azione [Form]

checkbox=true

Se non è selezionata, non viene inviata alcuna stringa di parametri. Struts 2 dispone di un meccanismo interno che fa sì che tutto avvenga come se la casella di controllo

checkbox=false

fosse stata inviata.

  • Riga 35: un elenco di caselle di controllo. È possibile selezionare più caselle di controllo. L'etichetta del gruppo è fornita dall'attributo key. Le etichette delle singole caselle di controllo sono fornite dall'attributo list. Il valore di questo attributo è il nome di un metodo nell'azione [Form] che deve restituire una collezione, un dizionario o un array. Qui, il metodo [Form].getCheckboxlistValues restituirà un array di stringhe [string1, string2, ...] che genererà i seguenti tag HTML:

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

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

...

Se l'utente seleziona le caselle di controllo denominate string2 e string5, la seguente stringa verrà inviata all'azione [Form]:

checkboxlist=string2&checkboxlist=string5

L'array [string2, string5] verrà assegnato al campo [Form].checkboxlist, che deve quindi essere di tipo array di stringhe, come nel caso degli elenchi a selezione multipla. Al contrario, quando viene visualizzata la vista [Form.jsp], i valori del campo [Form].checkboxlist determinano quali caselle di controllo saranno selezionate.

  • Riga 36: un campo nascosto. Un campo nascosto è un campo che viene inviato senza essere inserito dall'utente. Il suo valore è impostato dal programmatore. In questo caso, il campo nascosto prenderà il suo valore dal campo [Form].hidden. Questo stesso valore verrà inviato nel modulo:

hidden=qualcosa

e riassegnato al campo [Form].hidden.

  • Riga 37: un pulsante di invio. Facendo clic su questo pulsante, tutti i valori del modulo verranno inviati all'azione [Form]. La stringa dei parametri inviati avrà questo aspetto:

textfield=text&password=&textarea=line1%0D%0Aline2%0D%0A&select1=zero&select2=tre&select3=zero&select3=due&__multiselect_select3=&radio=blu&checkbox=vero&__checkbox_checkbox=vero&checkboxlist=v%C3%A9lo&checkboxlist=autobus&__multiselect_checkboxlist=&hidden=iniziale&submitText=Convalida

9.5. L'azione [Form] – sezione input

Il codice è il seguente:


package example;
 
import com.opensymphony.xwork2.ActionSupport;
 
public class Form extends ActionSupport {
 
  // constructor without parameters
  public Form() {
  }
  // form fields
  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;
 
  // values selected in select3 field
  public String getSelect3SelectedValues() {
    return getValue(select3);
  }
 
  // values selected in checkboxlist field
  public String getCheckboxlistSelectedValues() {
    return getValue(checkboxlist);
  }
 
  // utilitarian method
  public String getValue(String[] values) {
    String result = "";
    for (String value : values) {
      result += " " + value;
    }
    return result;
  }
 
// field getters and setters
  ....
}

  • riga 1: il campo associato al tag textfield del modulo. Utilizzato per inizializzare questo campo di input e per recuperarne il valore. Riceve il valore inserito in questo campo al momento della richiesta POST.
  • riga 2: il campo associato al tag password del modulo. Riceve il valore inserito in questo campo durante la richiesta POST.
  • riga 13: associata al tag textarea del modulo. Riceve il testo inserito in questo campo durante la richiesta POST.
  • Riga 14: associata al tag `select1` del modulo, un elenco a selezione singola. Il suo valore iniziale seleziona una voce dal menu a tendina. Verrà selezionata la voce con un attributo `value` corrispondente a questo valore. Al momento del POST, riceve l'attributo `value` dell'opzione selezionata nel menu a tendina.
  • Riga 15: le stringhe di caratteri che popoleranno il menu a tendina select1.
  • Righe 16 e 17: Uguale a select1
  • Riga 18: associata al tag select1 del modulo, un elenco a selezione multipla. I valori iniziali nell'array selezionano gli elementi corrispondenti nell'elenco. Verranno selezionati gli elementi il cui attributo value corrisponde a uno di questi valori. Al momento del POST, riceve gli attributi value delle varie opzioni selezionate nel menu a tendina.
  • Riga 19: le stringhe di caratteri che popoleranno l'elenco select3
  • Riga 20: associata al tag radio del modulo. Il suo valore viene utilizzato per selezionare uno dei pulsanti di opzione, ovvero quello il cui attributo value corrisponde a questo valore. Riceve il valore del pulsante di opzione selezionato durante la richiesta POST.
  • Riga 21: le varie etichette e i valori dei pulsanti radio.
  • Riga 22: associata al tag checkbox del modulo. Il suo valore iniziale viene utilizzato per selezionare o deselezionare la casella. Al momento del POST, riceve il valore true se la casella era selezionata, false in caso contrario.
  • Riga 23: Associato al tag checkboxlist del modulo. I valori iniziali nell'array selezionano le caselle di controllo corrispondenti. Le caselle di controllo il cui attributo value corrisponde a uno di questi valori iniziali saranno selezionate. Al momento del POST, riceve gli attributi value delle varie caselle di controllo selezionate.
  • Riga 24: Le stringhe che popoleranno le etichette delle caselle di controllo dell'elenco.
  • Riga 25: associata al campo nascosto. Il suo valore iniziale imposta il valore del campo nascosto. Al momento del POST, riceverà lo stesso valore poiché l'utente non può modificarlo.
  • Riga 26: Associato al tag submit. Durante la richiesta POST, riceve l'etichetta del pulsante. Questo campo è facoltativo.
  • I metodi nelle righe 29, 34 e 39 saranno discussi più avanti.

9.6. La vista [Form.jsp] – Sezione di conferma

La vista [Form.jsp] presenta una sezione denominata "Conferma dei dati inseriti" che non è stata ancora trattata:

Image

Il codice è il seguente:


<%@ 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>
  • Riga 13: visualizza il valore del campo [Form].textfield
  • riga 17: visualizza il valore del campo [Form].password
  • riga 21: visualizza il valore del campo [Form].textarea
  • riga 25: visualizza il valore del campo [Form].select1
  • riga 29: visualizza il valore del campo [Form].select2
  • Riga 33: visualizza i valori dell'array [Form].select3. Per farlo, utilizza il metodo [Form].getSelect3SelectedValues.
  • riga 37: visualizza il valore del campo [Form].radio
  • riga 41: visualizza il valore del campo [Form].checkbox
  • Riga 45: visualizza i valori dell'array [Form].checkboxlist. Per farlo, utilizza il metodo [Form].getCheckboxlistSelectedValues.
  • Riga 49: visualizza il valore del campo [Form].hidden

9.7. L'azione [Form] – sezione di conferma

I metodi getSelect3SelectedValues e getCheckboxlistSelectedValues menzionati sopra sono definiti nella classe [Form]:


  // valeurs sélectionnées dans champ select3
  public String getSelect3SelectedValues() {
    return getValue(select3);
  }
 
  // valeurs sélectionnées dans champ checkboxlist
  public String getCheckboxlistSelectedValues() {
    return getValue(checkboxlist);
  }
 
  // méthode utilitaire
  public String getValue(String[] values) {
    String result = "";
    for (String value : values) {
      result += " " + value;
    }
    return result;
}

Essi semplicemente concatenano gli elementi degli array che devono visualizzare.

9.8. Test

Quando si esegue il progetto NetBeans, viene visualizzata la seguente pagina iniziale:

I valori visualizzati in [1] e [2] derivano dai valori iniziali dei campi nell'azione [Form]. Vediamo alcuni esempi:


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

Il valore del campo textarea spiega le visualizzazioni [1A] e [2A].


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

Il valore del campo select3Values spiega il contenuto dell'elenco in [1B]. Il valore del campo select3 spiega gli elementi selezionati in [1B] e visualizzati in [2B].

Se inseriamo altri valori nel modulo e lo inviamo, i valori inseriti verranno inviati ai campi corrispondenti dell'azione [Form]. Quindi, la vista [Form.jsp] verrà visualizzata nuovamente. Ci troviamo quindi nella stessa situazione di prima, tranne per il fatto che i valori iniziali sono stati sostituiti dai valori inviati. Ecco un esempio:

Image

Image

9.9. L'azione [Form1] e la vista [Form1.jsp]

Il file di configurazione [example.xml] configura la seguente azione [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>

Righe 11–13: La chiamata all'azione [Form1] genera la vista [Form1.jsp].

L'azione [Form1] è la seguente:


package example;
 
import com.opensymphony.xwork2.ActionSupport;
 
public class Form1 extends ActionSupport{
 
  // constructor without parameters
  public Form1() {
  }
  // form fields
  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;
  }
}

L'azione [Form1] è identica all'azione [Form], tranne per il fatto che la sua parte relativa al modello è stata spostata in una classe separata, la classe [Data], alla riga 11. La classe [Data] è la seguente:


package example;
 
public class Data {
 
  // constructor without parameters
  public Data() {
  }
  
// form fields
  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;
 
  // values selected in select3 field
  public String getSelect3SelectedValues() {
    return getValue(select3);
  }
 
  // values selected in checkboxlist field
  public String getCheckboxlistSelectedValues() {
    return getValue(checkboxlist);
  }
 
  // utilitarian method
  public String getValue(String[] values) {
    String result = "";
    for (String value : values) {
      result += " " + value;
    }
    return result;
  }
 
  // getters and setters
....
 
}

Troviamo i campi precedentemente dichiarati nell'azione [Form].

Logicamente, la vista [Form1.jsp] è simile alla vista [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>

La differenza tra le viste [Form] e [Form1] può essere illustrata dalla riga 27 riportata di seguito:


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

Il campo di immissione è associato, sia in lettura che in scrittura, al campo data.textfield dell'azione [Form1]. Ricordiamo che, dopo l'esecuzione, le proprietà dell'azione [Form1] sono accessibili alla vista. Pertanto, l'attributo name precedente verrà valutato come [Form1].getData().getTextField(). Di conseguenza, i due metodi get precedenti devono esistere.

9.10. L'azione [Form2] e la vista [Form2.jsp]

Il file di configurazione [example.xml] configura la seguente azione [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>

Righe 14–16: La chiamata all'azione [Form2] genera la vista [Form2.jsp].

L'azione [Form2] è la seguente:


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

public class Form2 extends ActionSupport implements ModelDriven {
 
  // constructor without parameters
  public Form2() {
  }
  // action model
  public Object getModel() {
    return new Data();
  }
}
  • Riga 6: L'azione [Form2] implementa l'interfaccia [ModelDriven]. C'è un solo metodo da implementare: il metodo getModel alla riga 12. Questo metodo restituisce un'istanza della classe [Data] discussa in precedenza.

Il fatto che l'azione [Form2] implementi l'interfaccia [ModelDriven] comporta quanto segue: la vista visualizzata a seguito dell'azione [Form2] ha accesso diretto alle proprietà del modello dell'azione [Form2], restituito dal metodo getModel(). Pertanto, la vista [Form2.jsp] torna a essere quella che era con l'azione [Form] iniziale:


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

Quindi la riga 27, che prima era:


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

diventa:


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

Il vantaggio di collocare il modello al di fuori della vista è che questa architettura delinea più chiaramente le funzionalità di ciascun elemento dell'architettura MVC. Il modello M è chiaramente identificato da una classe. Inoltre, il modello può esistere prima delle azioni. Pertanto, se un'applicazione web gestisce un database di persone, è probabile che esista la classe Person. Se un'azione fornisce un modulo di input per creare una nuova persona, il modello per questa azione è ovvio: è la classe Person.

Abbiamo visto quanto fosse semplice passare dall'azione [Form] all'azione [Form2]:

  • il modello M della vista V è stato incapsulato in una classe separata
  • l'azione che genera la vista utilizzando questo modello implementa l'interfaccia ModelDriven con il metodo getModel, che restituisce un'istanza del modello M. L'azione può quindi essere vista come un'estensione del controller C di Struts 2, la classe FilterDispatcher.

Il lettore può applicare questo approccio a qualsiasi azione descritta di seguito.