Skip to content

9. Приклад 07 — Теги форми

Ми створимо та використаємо таку форму:

Ми розширимо поняття введення параметрів, розглянуте для поля введення тексту, на інші HTML-елементи форми.

9.1. Проєкт NetBeans

  • у [1], представлення проекту [Formx.JSP]
  • у [2], файл конфігурації Struts та інтернаціоналізовані повідомлення
  • у [3], дії — у [Formx.java] та ще один файл конфігурації Struts.

9.2. Налаштування Struts

Файл [struts.xml] має такий вигляд:


<?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>
  • рядок 9: включає інший файл конфігурації [example.xml]. Саме він буде налаштовувати дії.
  • рядки 11–19: пакет default. Визначає адресу перенаправлення для URL /. Вона буде перенаправлена на URL /example/Form.action.

Файл [example.xml], що налаштовує дії, має такий вигляд:


<?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>
  • рядки 7–17: визначають дії /example/Form, /example/Form1, /example/Form2, де /example — це простір імен (рядок 7), а Formx — дії.
  • рядки 8–10: виконання дії Form призведе до відображення подання /example/Form.JSP.
  • рядки 11–13: виконання дії Form1 призведе до відображення подання /example/Form1.JSP.
  • рядки 14–16: виконання дії Form2 призведе до відображення подання /example/Form2.JSP.

9.3. Файли повідомлень

Ми не будемо зупинятися на інтернаціоналізації проекту. Ми вже розглядали це в прикладі 01. Наводимо вміст файлу [messages.properties], щоб читач міг зрозуміти наступні види.


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. Вигляд [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="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>
  • Форма починається з 26-го рядка і закінчується 38-м рядком. У 26-му рядку відсутній атрибут action. Цей атрибут вказує, до якої дії будуть відправлені дані форми. За відсутності атрибута action дані будуть відправлені до дії, яка призвела до відображення цього виду, у даному випадку — до дії [Form].
  • рядок 27: поле введення. Атрибут key визначає його назву. Атрибут name — це ім’я параметра, який буде передано. Рядок, що передається для цього поля, матиме такий вигляд:
textfield=texte_saisi

Назва параметра відповідає полю дії [Form]. Значення поля введення texte_saisi буде вставлено в поле textfield дії [Form]. І навпаки, якщо вікно [Form.JSP] відображається в результаті дії [Form], значення поля введення буде відповідати значенню поля textfield дії [Form]. Отже, це поле використовується як для читання, так і для запису. Для цього клас [Form] повинен мати методи getTextField та setTextField.

Цей висновок справедливий для всіх наступних тегів введення даних. Ми не будемо його повторювати.

  • рядок 28: введення пароля. Рядок, що надсилається до дії [Form], матиме вигляд
password=mot_de_passe_saisi

У дії [Form] має існувати поле password з відповідними методами get та set.

  • рядок 29: введення багаторядкового тексту в поле розміром 5 рядків і 40 стовпців. Рядок, що надсилається до дії [Form], матиме вигляд
textarea=saisie

У дії [Form] має бути поле textarea з відповідними методами get та set.

  • рядок 30: випадаючий список (size=1) з можливістю вибору одного елемента. Можна вибрати лише одне значення зі списку. Рядок, що надсилається до дії [Form], матиме такий вигляд:
select1=valeur_sélectionnée

У дії [Form] має бути присутнє поле select1 з відповідними методами get / set.

Атрибут key — це назва списку. Атрибут list визначає список значень, які потрібно помістити в комбінований список. Тут для заповнення комбінованого списку буде викликано метод getSelect1Values дії [Form]. Цей метод може повертати колекцію, словник або масив. У цьому випадку він поверне масив рядків [chaine1, chaine2, ...], який згенерує такий HTML-код:


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

Текст між тегами <option>...</option> відобразиться у комбінованому списку. Атрибут value визначає значення, яке слід відправити, якщо користувач вибере цю опцію. У цьому випадку атрибут value та текст, що відображається у комбінованому списку, є однаковими. Найчастіше це не так. Якщо вибрано другий варіант, до дії [Form] буде відправлено такий рядок:

select1=chaine2

Тоді поле [Form].select1 отримає значення chaine2. І навпаки, якщо під час відображення форми це поле має значення chaine3, то візуально третій елемент


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

буде відображатися як вибраний у комбінованому списку.

Атрибут headerValue визначає текст, який має відображатися як перший варіант списку, а headerKey — значення, яке має бути відправлене, якщо користувач обере цей варіант.

  • рядок 31: тут знову список розміром 3 (size=3): на відміну від попереднього списку, де видно лише один елемент, тут будуть видні 3 елементи списку. Режим вибору залишається тим самим. Можна вибрати лише один елемент.
  • рядок 32: знову список, але з можливістю множинного вибору (multiple=true). У цьому випадку значення, що передається до дії [Form], матиме такий вигляд:
select3=chaine2&select3=chaine5

якщо було вибрано параметри chaine2 та chaine5. Коли для одного й того самого параметра, у даному випадку select3, заноситься кілька значень, дія повинна мати поле з назвою параметра та бути типу tableau. Так, наприклад, у даному випадку дія [Form] може мати поле такого вигляду:

String[] select3 ;

та відповідні методи get / set. Масив select3 отримає масив рядків [chaine2,chaine5]. І навпаки, під час відображення подання [Form.JSP] значення поля [Form].select3 визначають, які опції зі списку select3 будуть позначені як вибрані. Тут також використовується режим читання/запису.

  • рядок 33: група перемикачів. Назва групи задається атрибутом key. Назви окремих перемикачів задаються атрибутом list. Значенням цього атрибута є назва методу дії [Form], який повинен повертати колекцію, словник або масив. У цьому випадку метод [Form].getRadioValues поверне масив рядків [chaine1,chaine2,...], який згенерує такі HTML-теги:

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

Якщо встановлено перемикач chaine2, відправлене значення буде

radio=chaine2

, і це значення буде вставлено в поле [Form].radio.

  • рядок 34: прапорець. Якщо він встановлений, до дії буде передано такий рядок параметрів: [Form]
checkbox=true

Якщо його не позначено, ланцюжок параметрів не надсилається. Struts 2 має внутрішній механізм, завдяки якому все відбувається так, ніби ланцюжок

checkbox=false

був відправлений.

  • рядок 35: список прапорців. Можна встановити кілька прапорців. Назва групи задається атрибутом key. Назви окремих прапорців задаються атрибутом list. Значенням цього атрибута є ім’я методу дії [Form], який повинен повертати колекцію, словник або масив. У цьому випадку метод [Form].getCheckboxlistValues поверне масив рядків [chaine1,chaine2,...], який згенерує такі HTML-теги:

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

Якщо користувач позначить поля з назвами chaine2 та chaine5, наступний рядок символів буде відправлений до дії [Form]:

checkboxlist=chaine2&checkboxlist=chaine5

Таблиця [chaine2, chaine5] буде прив’язана до поля [Form].checkboxlist, яке, отже, має бути типу «масив символьних рядків», як і у випадку списків із можливістю множинного вибору. І навпаки, під час відображення подання [Form.JSP] значення поля [Form].checkboxlist визначають, які прапорці будуть встановлені.

  • рядок 36: приховане поле. Приховане поле — це поле, яке надсилається без введення даних користувачем. Його значення встановлюється програмістом. У цьому випадку приховане поле отримає значення з поля [Form].hidden. Це саме значення буде надіслано у вигляді:
hidden=qqchose

і перепризначено полю [Form].hidden.

  • рядок 37: кнопка типу submit. Натискання на цю кнопку відправить усі значення форми до дії [Form]. Рядок відправлених параметрів матиме такий вигляд:
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. Акція [Form] — частина введення даних

Її код такий:


package example;

import com.opensymphony.xwork2.ActionSupport;

public class Form extends ActionSupport {

  // конструктор без параметрів
  public Form() {
  }
  // поля форми
  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;

  // значення, вибрані в полі select3
  public String getSelect3SelectedValues() {
    return getValue(select3);
  }

  // значення, вибрані в полі checkboxlist
  public String getCheckboxlistSelectedValues() {
    return getValue(checkboxlist);
  }

  // допоміжний метод
  public String getValue(String[] values) {
    String result = "";
    for (String value : values) {
      result += " " + value;
    }
    return result;
  }

// методи getter та setter для полів
  ....
}
  • рядок 1: поле, пов’язане з тегом textfield у формі. Використовується як для ініціалізації цього поля введення, так і для отримання його значення. Під час виконання дії POST отримує значення, введене в це поле.
  • рядок 2: поле, пов’язане з тегом password у формі. Під час виконання POST отримує значення, введене в це поле.
  • рядок 13: пов’язаний із тегом textarea форми. Під час виконання POST отримує текст, введений у це поле.
  • рядок 14: пов'язаний з тегом select1 форми, список з одним варіантом вибору. Його початкове значення вибирає елемент зі списку. Буде вибрано той елемент, у якого атрибут value дорівнює цьому значенню. Під час виконання POST отримує атрибут value від опції, обраної у комбінованому списку.
  • рядок 15: символьні рядки, які будуть заповнювати комбінований список select1.
  • рядки 16 і 17: те саме, що й select1
  • рядок 18: пов'язаний з тегом select1 форми, список з можливістю вибору декількох елементів. Початкові значення в таблиці вибирають відповідні елементи зі списку. Будуть вибрані ті елементи, у яких атрибут value дорівнює одному з цих елементів. Під час виконання POST отримує атрибути value різних опцій, вибраних у комбінованому списку.
  • рядок 19: рядки символів, які будуть заповнювати список select3
  • рядок 20: пов’язаний із тегом radio у формі. Його значення використовується для позначення одного з перемикачів — того, у якого атрибут value дорівнює цьому значенню. Під час виконання POST отримує значення вибраної кнопки радіо.
  • рядок 21: різні назви та значення перемикачів.
  • рядок 22: пов’язаний з тегом checkbox у формі. Його початкове значення використовується для встановлення/зняття галочки. Під час виконання POST отримує значення true, якщо поле було позначено, і false — у протилежному випадку.
  • рядок 23: пов'язаний з тегом checkboxlist у формі. Початкові значення в таблиці визначають відповідні прапорці, які слід встановити. Будуть встановлені ті прапорці, у яких атрибут value дорівнює одному з цих початкових значень. Під час виконання POST отримує атрибути value різних позначених полів.
  • рядок 24: символьні рядки, які будуть використовуватися як назви полів у checkboxlist.
  • рядок 25: пов'язаний із прихованим полем hidden. Його початкове значення визначатиме значення прихованого поля. Під час виконання POST отримає те саме значення, оскільки користувач не має можливості його змінити.
  • рядок 26: пов’язаний із тегом submit. Під час виконання POST отримує напис кнопки. Без цього поля можна обійтися.
  • Методи в рядках 29, 34 та 39 будуть прокоментовані трохи далі.

9.6. Екран [Form.JSP] — розділ «Підтвердження»

Погляд [Form.JSP] має розділ «Підтвердження введених даних», який ще не розглядався:

 

Її код такий:


<%@ 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>
  • рядок 13: відображає значення поля [Form].textfield
  • рядок 17: відображає значення поля [Form].password
  • рядок 21: відображає значення поля [Form].textarea
  • рядок 25: відображає значення поля [Form].select1
  • рядок 29: відображає значення поля [Form].select2
  • рядок 33: відображає значення масиву [Form].select3. Для цього використовується метод [Form].getSelect3SelectedValues.
  • рядок 37: виводить значення поля [Form].radio
  • рядок 41: виводить значення поля [Form].checkbox
  • рядок 45: відображає значення масиву [Form].checkboxlist. Для цього використовується метод [Form].getCheckboxlistSelectedValues.
  • рядок 49: відображає значення поля [Form].hidden

9.7. Дія [Form] — частина підтвердження

Згадані вище методи getSelect3SelectedValues та getCheckboxlistSelectedValues визначені у класі [Form]:


  // значення, вибрані в полі select3
  public String getSelect3SelectedValues() {
    return getValue(select3);
  }

  // значення, вибрані у полі checkboxlist
  public String getCheckboxlistSelectedValues() {
    return getValue(checkboxlist);
  }

  // допоміжний метод
  public String getValue(String[] values) {
    String result = "";
    for (String value : values) {
      result += " " + value;
    }
    return result;
}

Вони просто об'єднують елементи масивів, які потрібно відобразити.

9.8. Тестування

Під час виконання проекту в NetBeans з’являється така початкова сторінка:

Значення, що відображаються в [1] та [2], походять від початкових значень полів дії [Form]. Розглянемо кілька прикладів:


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

Значення поля textarea пояснює відображення [1A] та [2A].


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

Значення поля select3Values пояснює вміст списку в [1B]. Значення поля select3 пояснює елементи, вибрані в [1B] та відображені в [2B].

Якщо ми введемо інші значення у форму та підтвердимо її, введені значення будуть відправлені у відповідні поля дії [Form]. Потім знову відобразиться вікно [Form.JSP]. Отже, ми маємо ту саму ситуацію, що й раніше, за винятком того, що початкові значення були замінені на відправлені. Ось приклад:

9.9. Дія [Form1] та представлення [Form1.JSP]

Файл конфігурації [example.xml] налаштовує таку дію [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>

У рядках 11–13 виклик дії [Form1] генерує представлення [Form1.JSP].

Дія [Form1] виглядає наступним чином:


package example;

import com.opensymphony.xwork2.ActionSupport;

public class Form1 extends ActionSupport{

  // конструктор без параметрів
  public Form1() {
  }
  // поля форми
  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;
  }
}

Дія [Form1] ідентична дії [Form], за винятком того, що її модельна частина була винесена в дочірній клас — клас [Data], рядок 11. Клас [Data] має такий вигляд:


package example;

public class Data {

  // конструктор без параметрів
  public Data() {
  }
  
// поля форми
  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;

  // значення, вибрані у полі select3
  public String getSelect3SelectedValues() {
    return getValue(select3);
  }

  // значення, вибрані у полі checkboxlist
  public String getCheckboxlistSelectedValues() {
    return getValue(checkboxlist);
  }

  // допоміжний метод
  public String getValue(String[] values) {
    String result = "";
    for (String value : values) {
      result += " " + value;
    }
    return result;
  }

  // методи getter та setter
....

}

У дії [Form] ми бачимо ті самі поля, що були оголошені раніше.

Цілком логічно, що представлення [Form1.JSP] схоже на представлення [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>

Різницю між поданнями [Form] та [Form1] можна проілюструвати на прикладі рядка 27, наведеного нижче:


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

Поле введення пов’язане для читання та запису з полем data.textfield дії [Form1]. Нагадаємо, що після виконання властивості дії [Form1] стають доступними для подання. Таким чином, попередній атрибут name буде обчислено як [Form1].getData().getTextField(). Отже, обидва попередні методи get повинні існувати.

9.10. Дія [Form2] та представлення [Form2.JSP]

Файл конфігурації [example.xml] налаштовує таку дію [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>

У рядках 14–16 виклик дії [Form2] генерує представлення [Form2.JSP].

Дія [Form2] виглядає наступним чином:


package example;

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

public class Form2 extends ActionSupport implements ModelDriven {

  // конструктор без параметрів
  public Form2() {
  }
  // модель дії
  public Object getModel() {
    return new Data();
  }
}
  • рядок 6: дія [Form2] реалізує інтерфейс [ModelDriven]. Потрібно реалізувати лише один метод — метод getModel у рядку 12. Він повертає екземпляр класу [Data], який ми розглядали раніше.

Те, що дія [Form2] реалізує інтерфейс [ModelDriven], має такий наслідок: вид, що відображається після дії [Form2], має прямий доступ до властивостей моделі дії [Form2], яка повертається методом getModel(). Таким чином, представлення [Form2.JSP] повертається до того стану, в якому воно перебувало під час виконання початкової дії [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>

Таким чином, рядок 27, який раніше мав вигляд:


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

знову стає:


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

Перевага розміщення моделі поза межами подання полягає в тому, що така архітектура чіткіше розмежовує функціональні можливості кожного елемента архітектури MVC. Модель M чітко ідентифікується класом. Крім того, модель може існувати ще до появи дій. Так, якщо веб-додаток управляє базою даних осіб, ймовірно, що клас Personne вже існує. Якщо дія пропонує форму введення даних для створення нової особи, модель цієї дії вже готова: це клас Personne.

Ми побачили, як просто було перейти від дії [Form] до дії [Form2]:

  • модель M подання V була інкапсульована в окремий клас
  • дія, яка генерує вигляд, що використовує цю модель, реалізує інтерфейс ModelDriven із методом getModel, який повертає екземпляр моделі M. Отже, цю дію можна розглядати як розширення контролера C у Struts 2 — класу FilterDispatcher.

Читач зможе застосувати цей підхід до будь-якої дії, описаної далі.