Skip to content

9. 示例 07 – 表单标签

我们将构建并使用以下表单:

Image

Image

我们将把文本输入字段中看到的参数注入概念扩展到表单中的其他 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 行:默认包。定义了 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] 视图 – 输入部分

其外观如下:

Image

具体如下:


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

该参数名称对应于 [Form] 操作中的一个字段。text_saisi 输入字段的值将被注入到 [Form] 操作的 textfield 字段中。 反之,如果 [Form.jsp] 视图在 [Form] 操作之后显示,该输入字段的值将取自 [Form] 操作中的 textfield 字段。因此,该字段既用于读取也用于写入。为此,[Form] 类必须提供 getTextFieldsetTextField 方法。

这一原理适用于后续的所有 input 标签,此处不再赘述。

  • 第 28 行:密码输入。提交至 [Form] 操作的字符串将采用以下形式

password=entered_password

[Form] 操作中必须存在一个带有 get/set 方法的密码字段。

  • 第 29 行:在 5 行 40 列的区域中输入多行文本。提交到 [Form] 操作的字符串将采用以下形式

textarea=输入内容

[Form] 动作中必须存在一个带有 get/set 方法的 textarea 字段。

  • 第 30 行:单选下拉列表(size=1)。列表中只能选择一个值。提交到 [Form] 操作的字符串将采用以下形式:

select1=selected_value

[Form] 动作中必须存在一个带有 get/set 方法的 select1 字段。

key 属性是列表的标签。list 属性定义了要放入下拉列表框中的值列表。在此,将调用 [Form] 操作的 getSelect1Values 方法来填充下拉列表框。该方法可以返回集合、字典或数组。在此,它将返回一个字符串数组 [string1, string2, ...],这将生成以下 HTML 代码:

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

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

...

位于 <option>...</option> 标签之间的文本将显示在下拉列表中。value 属性指定了当用户选中该选项时要提交的值。在此示例中,value 属性与下拉列表中显示的文本相同。但通常情况下并非如此。如果选中了第二个选项,则会向 [Form] 操作提交以下字符串:

select1=string2

此时 [Form].select1 字段将接收值 string2。反之,如果表单显示时该字段被设置为 string3,那么从视觉上看,第三个元素

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

将显示为已选中状态。

headerValue 属性用于设置下拉列表中作为第一个选项显示的文本,而 headerKey 属性则用于设置当用户选中该选项时提交的值。

  • 第 31 行:同样是一个大小为 3 的列表(size=3):与之前仅显示一个项目的列表不同,这里将显示列表中的 3 个项目。选择模式保持不变。只能选择一个项目。
  • 第 32 行:另一个列表,但这次支持多选(multiple=true)。在这种情况下,提交到 [Form] 操作的值将采用以下形式:

select3=string2&select3=string5

(前提是选项 string2string5 已被选中)。当同一参数(此处为 select3)收到多个值时,该操作必须包含一个与参数同名的字段,且该字段类型为数组。因此,例如,此处的 [Form] 操作可以包含如下字段:

String[] select3;

以及相应的 get/set 方法。此时,select3 数组将接收字符串数组 [string2, string5]。反之,当显示 [Form.jsp] 视图时,[Form].select3 字段的值将决定 select3 下拉列表中哪些选项显示为已选中。这始终是一项读写操作。

  • 第 33 行:一组单选按钮。组标签由 key 属性提供。各个按钮的标签由 list 属性提供。 该属性的值是 [Form] 动作中某个方法的名称,该方法必须返回一个集合、字典或数组。在此,[Form].getRadioValues 方法将返回一个字符串数组 [string1, string2, ...],该数组将生成以下 HTML 标签:

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

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

...

如果选中了 radio 按钮 string2,提交的值将是

radio=string2

,该值将被插入到 [Form].radio 字段中。

  • 第 34 行:一个复选框。如果选中,则会将以下参数字符串提交到 [Form] 操作

checkbox=true

如果未勾选此选项,则不会提交任何参数字符串。Struts 2 具有一种内部机制,使其运行效果如同该字符串

checkbox=false

已提交。

  • 第 35 行:一组复选框。可以同时选中多个复选框。组标签由 key 属性提供。各个复选框的标签由 list 属性提供。 该属性的值是 [Form] 动作中某个方法的名称,该方法必须返回一个集合、字典或数组。在此,[Form].getCheckboxlistValues 方法将返回一个字符串数组 [string1, string2, ...],该数组将生成以下 HTML 标签:

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

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

...

如果用户选中了标有 string2string5 的复选框,则会向 [Form] 操作提交以下字符串:

checkboxlist=string2&checkboxlist=string5

数组 [string2, string5] 将被赋值给 [Form].checkboxlist 字段,因此该字段必须是字符串数组类型,这与多选列表的情况相同。反之,当显示 [Form.jsp] 视图时,[Form].checkboxlist 字段的值将决定哪些复选框会被选中。

  • 第 36 行:一个隐藏字段。隐藏字段是指无需用户输入即可提交的字段。其值由程序员设定。在此,该隐藏字段将从 [Form].hidden 字段获取其值。该值将以以下形式提交:

hidden=something

并重新赋值给 [Form].hidden 字段。

  • 第 37 行:一个提交按钮。点击此按钮将把所有表单值提交至 [Form] 操作。提交的参数字符串将如下所示:

textfield=text&password=&textarea=line1%0D%0Aline2%0A&select1=zero&select2=three&select3=zero&select3=two&__multiselect_select3=&radio=blue&checkbox=true&__checkbox_checkbox=true&checkboxlist=v%C3%A9lo&checkboxlist=bus&__multiselect_checkboxlist=&hidden=initial&submitText=Validate

9.5. [表单] 操作 – 输入部分

其代码如下:


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

  • 第 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 行:与隐藏字段相关联。其初始值用于设置隐藏字段的值。在 POST 请求时,它将接收该相同值,因为用户无法修改它。
  • 第 26 行:与 submit 标签相关联。在 POST 请求期间,它将接收按钮的标签。该字段是可选的。
  • 第 29、34 和 39 行中的方法将在下文进一步讨论。

9.6. [Form.jsp] 视图 – 确认部分

[Form.jsp] 视图中有一个尚未介绍的“输入确认”部分:

Image

其代码如下:


<%@ 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] 操作 – 确认部分

上述的 getSelect3SelectedValuesgetCheckboxlistSelectedValues 方法定义在 [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;
}

它们只是将需要显示的数组元素进行拼接。

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] 视图将重新显示。因此,我们处于与之前相同的情况,只是初始值已被发布的值所替换。以下是一个示例:

Image

Image

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{
 
  // 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;
  }
}

[Form1] 的实现与 [Form] 完全相同,唯一的区别在于其模型部分已被移至一个独立的类——[Data] 类(第 11 行)。[Data] 类的定义如下:


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

我们找到了之前在 [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" />

该输入字段与 [Form1] 操作的 data.textfield 字段建立了读写绑定。请注意,执行完成后,视图可以访问 [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 {
 
  // constructor without parameters
  public Form2() {
  }
  // action model
  public Object getModel() {
    return new Data();
  }
}
  • 第 6 行:[Form2] 操作实现了 [ModelDriven] 接口。只需实现一个方法:第 12 行中的 getModel 方法。该方法返回一个前面讨论过的 [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(模型)通过一个类被明确标识。此外,模型可能在操作(Action)之前就已存在。因此,如果一个Web应用程序管理一个人员数据库,那么Person类很可能已经存在。如果一个操作提供了一个用于创建新人员的输入表单,那么该操作的模型显而易见:它就是Person类。

我们已经看到,从 [Form] 操作切换到 [Form2] 操作是多么简单

  • 视图 V 的模型 M 已被封装在单独的类中
  • 使用该模型生成视图的操作实现了带有 getModel 方法的 ModelDriven 接口,该方法返回模型 M 的实例。因此,该操作可视为 Struts 2 控制器 C(即 FilterDispatcher 类)的扩展。

读者可以将此方法应用于下文所述的任何操作。