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 行: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] – 数据录入部分

其视觉外观如下:

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=输入文本

该参数名称对应于操作 [Form] 中的一个字段。此处,输入字段 texte_saisi 的值将被注入到操作 [Form] 的字段 textfield 中。 反之,如果视图 [Form.jsp] 是通过操作 [Form] 显示的,则输入字段的值将是操作 [Form] 中字段 textfield 的值。 因此,该字段既用于读取也用于写入。为此,类 [Form] 必须具备方法 getTextFieldsetTextField

此逻辑同样适用于后续的所有输入标签,此处不再赘述。

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

password=输入的密码

在 [Form] 操作中必须存在一个 password 字段及其 get / set 方法。

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

textarea=输入

操作 [Form] 中必须存在一个 textarea 字段及其 get/set 方法。

  • 第 30 行:一个单选下拉列表(size=1)。列表中只能选择一个值。发送到操作 [Form] 的字符串格式如下:

select1=选定值

在操作 [Form] 中必须存在字段 select1 及其 get/set 方法。

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

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

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

...

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

select1=字符串2

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

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

将显示为下拉列表中已选中的选项。

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

  • 第31行:这里同样是一个包含3个项的列表(size=3):与前一个仅显示单个项的列表不同,此处将显示列表中的3个项。选择模式保持不变,仍只能选择一个项。
  • 第32行:又是列表,但支持多选(multiple=true)。在此情况下,提交至操作[Form]的值将采用以下形式:

select3=字符串2&select3=字符串5

(前提是已选中选项 chaine2chaine5)。 当为同一个参数(此处为 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

...

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

checkboxlist=字符串2&checkboxlist=字符串5

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

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

hidden=qqchose

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

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

textfield=文本&password=&textarea=行1%0D%0A行2%0D%0A&select1=零&select2=三&select3=零&select3=二&__multiselect_select3=&radio=蓝色&checkbox=true&__checkbox_checkbox=true&checkboxlist=自行车&checkboxlist=公交车&__multiselect_checkboxlist=&hidden=初始&submitText=提交

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);
  }

  // 复选框列表中选中的值
  public String getCheckboxlistSelectedValues() {
    return getValue(checkboxlist);
  }

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

// 字段的获取器和设置器
  ....
}
  • 第 1 行:与表单中 textfield 标签关联的字段。用于初始化该输入字段,并从中获取值。在 POST 执行时,接收该字段中输入的值。
  • 第 2 行:与表单中 password 标签关联的字段。在 POST 事件触发时,接收该字段中输入的值。
  • 第 13 行:与表单中的 textarea 标签相关联。在 POST 事件触发时,接收该字段中输入的文本。
  • 第 14 行:关联表单中的 select1 标签,这是一个单选列表。其初始值用于选择下拉列表中的一个项目。系统将选中 value 属性值与该初始值相等的项目。 在 POST 执行时,将接收下拉列表中选定选项的 value 属性。
  • 第 15 行:用于填充下列表的字符串。
  • 第 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] 包含一个尚未提及的“确认输入”部分:

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] 中:


  // 在 select3 字段中选定的值
  public String getSelect3SelectedValues() {
    return getValue(select3);
  }

  // 复选框列表字段中选中的值
  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] 将重新显示。 因此,这与前面的情况相同,只是初始值已被提交的值所替换。以下是一个示例:

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{

  // 无参构造函数
  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] 完全相同,只是其模板部分被移至一个附属类(即第 11 行中的类 [Data])中。 类 [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;

  // 下拉列表字段中的选定值
  public String getSelect3SelectedValues() {
    return getValue(select3);
  }

  // 复选框列表字段中选中的值
  public String getCheckboxlistSelectedValues() {
    return getValue(checkboxlist);
  }

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

  // 获取器和设置器
....

}

在操作 [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 {

  // 无参构造函数
  public Form2() {
  }
  // 操作模型
  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通过一个类被明确标识。 此外,模型可能在操作之前就已存在。因此,如果一个Web应用程序管理人员数据库,那么很可能已经存在Personne类。如果某个操作提供了一个用于创建新人员的输入表单,那么该操作的模型就显而易见了:它就是Personne类

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

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

读者可以将此方法应用于后面描述的任何操作。