Skip to content

6. HTML 表单

到目前为止,我们使用了一个仅包含两个输入字段的单一表单。在此,我们建议使用标准的图形组件(单选按钮、复选框、输入字段、下拉列表、列表)来创建和处理表单。

6.1. 应用程序视图

该应用程序仅包含两个视图。第一个视图显示一个空白表单:

视图 1 - 表单

Image

这个名为 form.jsp 的第一个视图,将让我们能够实现 struts-html 库中的各种标签。用户填写表单:

Image

[提交]按钮用于确认已输入的值。这将作为第二个视图:

Image

在第二个视图中,我们将使用另外两个标签库:struts-beanstruts-logic。[返回表单] 链接可让我们返回填写过表单的页面。随后,系统将带我们回到第一个视图。

6.2. 应用程序架构

  • 该表单(视图 1)将由一个名为 dynaFormulaire 的动态 Struts 对象表示,该对象是 DynaActionForm 的子类。它将通过 form.jsp 视图进行显示。
  • Struts 动作 InitFormulaireAction 将负责检索显示表单所需的数据
  • 填写完成的表单将由一个 ForwardAction 进行处理该操作仅将请求重定向至第二个视图 confirmation.jsp。该视图将负责显示表单的值。

6.3. 应用程序配置

6.3.1. server.xml 文件

应用程序上下文将命名为 /formulaire2。因此,我们将向 Tomcat 的 server.xml 文件中添加以下行:

    <Context path="/formulaire2" docBase="e:/data/serge/web/struts/formulaire2" />

完成此操作后,可能需要重启 Tomcat 以便其识别新的上下文。我们可以通过访问 URL http://localhost:8080/formulaire2 来验证其有效性:

Image

6.3.2. web.xml 文件

应用程序的 web.xml 配置文件如下所示:

<?xml version="1.0" encoding="ISO-8859-1"?>

<!DOCTYPE web-app
    PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    "http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>
    <servlet>
      <servlet-name>action</servlet-name>
    <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
    <init-param>
        <param-name>config</param-name>
      <param-value>/WEB-INF/struts-config.xml</param-value>
    </init-param>
  </servlet>

  <servlet-mapping>
      <servlet-name>action</servlet-name>
    <url-pattern>*.do</url-pattern>
  </servlet-mapping>

  <taglib>
      <taglib-uri>/WEB-INF/struts-html.tld</taglib-uri>
    <taglib-location>/WEB-INF/struts-html.tld</taglib-location>
  </taglib>
  <taglib>
      <taglib-uri>/WEB-INF/struts-bean.tld</taglib-uri>
    <taglib-location>/WEB-INF/struts-bean.tld</taglib-location>
  </taglib>
  <taglib>
      <taglib-uri>/WEB-INF/struts-logic.tld</taglib-uri>
    <taglib-location>/WEB-INF/struts-logic.tld</taglib-location>
  </taglib>

</web-app>

与我们之前看到的 web.xml 配置文件相比,我们做了一些改动:

  • 我们将引入两个新的标签库:struts-bean struts-logic。它们将在 confirmation.jsp 视图中使用。另一方面,formulaire.jsp 视图将使用 struts-html 库。

6.3.3. struts-config.xml 文件

struts-config.xml 文件内容如下:

<?xml version="1.0" encoding="ISO-8859-1" ?>

<!DOCTYPE struts-config PUBLIC
          "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN"
          "http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd">

<struts-config>
    <form-beans>
        <form-bean name="dynaFormulaire" type="istia.st.struts.formulaire.DynaFormulaire">
            <form-property name="opt" type="java.lang.String" initial="non"/>
            <form-property name="chk1" type="java.lang.String"/>
            <form-property name="chk2" type="java.lang.String"/>
            <form-property name="chk3" type="java.lang.String"/>            
            <form-property name="champSaisie" type="java.lang.String" initial=""/>
            <form-property name="mdp" type="java.lang.String" initial=""/>        
            <form-property name="boiteSaisie" type="java.lang.String" initial=""/>        
            <form-property name="combo" type="java.lang.String"/>
            <form-property name="listeSimple" type="java.lang.String"/>
            <form-property name="listeMultiple" type="java.lang.String[]"/>                
            <form-property name="secret" type="java.lang.String" initial="xxx"/>
            <form-property name="valeursCombo" type="java.lang.String[]" />
            <form-property name="valeursListeSimple" type="java.lang.String[]" />
            <form-property name="valeursListeMultiple" type="java.lang.String[]"/>                  
        </form-bean>            
    </form-beans>

    <action-mappings>
      <action
          path="/confirmation"
            name="dynaFormulaire"
            validate="false" 
            scope="session"
          parameter="/vues/confirmation.jsp"
          type="org.apache.struts.actions.ForwardAction"
      />

      <action
          path="/init"
            name="dynaFormulaire"
            validate="false" 
            scope="session"
          type="istia.st.struts.formulaire.InitFormulaireAction"
      >
            <forward name="afficherFormulaire" path="/vues/formulaire.jsp"/>
        </action>

      <action
          path="/affiche"
          parameter="/vues/formulaire.jsp"
          type="org.apache.struts.actions.ForwardAction"
      />

    </action-mappings>

        <message-resources 
      parameter="ApplicationResources"
        null="false"/>    

</struts-config>

它包含三个主要部分:

  • <form-beans> 部分中的表单声明
  • <action-mappings> 部分中的操作声明
  • <message-resources> 部分中的资源文件声明

6.3.4. 应用程序的表单对象(Beans)

用于表示应用程序 HTML 表单的对象属于 ActionForm 类型或其派生类型(如 DynaActionFormDynaValidatorForm 等)。它们被称为 Bean,因为其构造遵循 JavaBeans 的规则。我们的应用程序中仅有一个表单 Bean,名为 dynaFormulaire,它继承自 DynaActionForm。它将在以下情况下被使用:

  • 用于存储显示视图 #1 所需的数据
  • 在用户提交表单时,从视图 #1 的表单中提取值
  • 用于存储显示视图 #2 所需的数据

dynaFormulaire Bean 的结构与视图 #1 中的表单紧密相关。让我们来分析一下:

编号
HTML 类型
角色
1
<input name="opt" type="radio" value="yes">
<input name="opt" type="radio" value="no">
一组相互关联的单选按钮(名称相同)
2
<input name="chk1" type="radio" value="on">
<input name="chk2" type="radio" value="on">
<input name="chk3" type="radio" value="on">
复选框组
(名字不同)
3
<input type="text" name="inputField">
一个文本输入框
4
<input type="password" name="password">
密码输入框
5
<textarea name="inputBox">...</textarea>
多行输入框
6
<select name="combo" size="1">..</select>
下拉菜单
7
<select name="simpleList" size="3">..</select>
单选列表
8
<select name="listeMultiple" size="3" multiple>..</select>
多选列表
9
<input type="button" value="清除"
onclick='clearList("singleList")'>
用于取消选中的按钮
simpleList(7)中选中的项目
10
<input type="button" value="清除"
onclick='clearList("multipleList")'>
取消选中的按钮
multipleList 中选中的项目 (8)
11
<input type="submit" value="提交">
表单提交按钮
12
<input type="hidden" name="secret" value="...">
一个隐藏字段

让我们考虑几种情况:

  1. dynaFormulaire 对象用于存储上方 HTML 表单中的值,这些值将通过 [提交] 按钮提交。因此,它必须包含与 HTML 表单相同的字段。字段类型由以下规则决定:
  • 如果 HTML 字段仅提供一个值,则 dynaFormulaire 字段的类型为 java.lang.String
  • 如果 HTML 字段提供多个值,则 dynaFormulaire 字段的类型为 java.lang.String[]

在上方的 HTML 表单中,只有 listeMultiple 字段可以关联多个值(即用户所选的值)。因此,**dynaFormulaire** 对象的初始定义如下:

        <form-bean name="dynaFormulaire" type="istia.st.struts.formulaire.DynaFormulaire">
            <form-property name="opt" type="java.lang.String" initial="non"/>
            <form-property name="chk1" type="java.lang.String"/>
            <form-property name="chk2" type="java.lang.String"/>
            <form-property name="chk3" type="java.lang.String"/>            
            <form-property name="champSaisie" type="java.lang.String" initial=""/>
            <form-property name="mdp" type="java.lang.String" initial=""/>        
            <form-property name="boiteSaisie" type="java.lang.String" initial=""/>        
            <form-property name="combo" type="java.lang.String"/>
            <form-property name="listeSimple" type="java.lang.String"/>
            <form-property name="listeMultiple" type="java.lang.String[]"/>                
            <form-property name="secret" type="java.lang.String" initial="xxx"/>
        </form-bean>            

dynaFormulaire 将如何填充由 Web 客户端发送的 HTML 表单值?

opt
如果 HTML 字段 <input type="radio" name="opt" value="yes"> 被选中,opt 字段将接收值 "yes";如果字段 <input type="radio" name="opt" value="no"> 被选中,则接收值 "no"。
chk1
如果 HTML 字段 <input name="chk1" type="radio" value="1"> 被选中,则 chk1 字段将接收值 "on";否则,它将不接收任何值。在后一种情况下,chk1 字段将保留其先前值。
chk2
相同
chk3
相同
输入字段
`champSaisie` 字段将接收用户在 HTML 字段 `<input type="text" name="champSaisie">` 中输入的文本。该文本可能是空字符串。
密码
`mdp` 字段将接收用户在 HTML 字段 `<input type="password" name="mdp">` 中输入的文本。该文本可以是空字符串。
textarea
inputBox 字段将接收用户在 HTML 字段 <textarea name="inputBox">...</textarea> 中输入的文本。该文本构成一个单一字符串,包含用户输入的各行内容,行与行之间由字符序列 "\r\n" 分隔。生成的文本可选为空字符串。
combo
combo 字段将接收用户在 HTML 字段 <select name="combo" size="1">..</select> 中选定的选项。所选选项即为下拉框中显示的选项。如果选定的 HTML 选项属于 <option value="XX">YY</option> 类型,则 combo 字段将接收值 "XX"。 如果选中的 HTML 选项属于 <option>YY</option> 类型,则组合框字段将接收值 "YY"。
simpleList
如果存在 HTML 字段 <select name="simpleList" size="..">..</select>,则 simpleList 字段将接收用户在该字段中选定的选项。如果不存在,simpleList 字段将不接收任何值并保留其先前值。实际分配给 simpleList 字段的值遵循为下拉框指定的规则。
listeMultiple
类型为 String[]listeMultiple 字段将接收用户在 HTML 字段 <select name="listeMultiple" size=".." multiple>..</select> 中选定的选项(如有)。如果没有选项,listeMultiple 数组将不接收任何值,其内容保持不变。实际赋值给 listeMultiple 数组的值遵循为下拉列表框指定的规则。
secret
secret 字段将接收来自 HTML 字段 <input type="hidden" name="secret" value="XX"> 的值 XX。该文本可选为空字符串。
  1. dynaFormulaire 对象用于为视图 #1 提供初始内容。前面的字段值将用于以下目的:
opt
必须设置为“yes”或“no”,以便浏览器知道应选中哪个单选按钮
chk1
如果 chk1 的值为“on”,复选框将被选中;否则,它将不会被选中
chk2
同上
chk3
相同
输入字段
字段值将显示在输入字段中 fieldInput
密码
该字段的值将显示在 mdp 输入字段中
inputBox
该字段的值将显示在输入框中 inputBox
下拉列表
该字段的值指定在表单显示时,下拉列表中应选中的项目
simpleList
相同
multipleList
multipleList 数组中的值指定了在显示表单时,多选列表中应选中的项目
secret
该字段的值将被赋给 secret HTML 字段的 value 属性。

视图 #1 需要额外信息:

  • 要在下拉列表中显示的值列表
  • 要在 `listeSimple` 列表中显示的值列表
  • 要在 multipleList 中显示的值列表

有多种方法可以将这些信息提供给视图。例如,将数组放入传递给视图的请求中即可。在此,我们将这些数组放入 dynaFormulaire Bean 中:

        <form-bean name="dynaFormulaire" type="istia.st.struts.formulaire.DynaFormulaire">
...
            <form-property name="valeursCombo" type="java.lang.String[]" />
            <form-property name="valeursListeSimple" type="java.lang.String[]" />
            <form-property name="valeursListeMultiple" type="java.lang.String[]"/>                  
        </form-bean>            

dynaFormulaire 表单将由 /init 操作进行初始化,该操作将调用一个名为 InitFormulaireAction 的 Action 派生对象。该对象负责创建显示三个列表所需的三个数组,并将它们放入 dynaFormulaire Bean 中。配置文件将该 Bean 的作用域设置为 session。因此,Struts 控制器会将该 Bean 放入会话中。 因此,我们无需在请求-响应循环之间重新生成它。相应地,/init 动作会被调用一次

  1. dynaFormulaire 对象还用于填充视图 #2。该视图仅用于显示数据值。

6.3.5. 应用程序操作

操作由 Action 类型或其派生类型的对象处理。操作配置在 <action-mappings> 标签内进行:

    <action-mappings>
      <action
          path="/confirmation"
            name="dynaFormulaire"
            validate="false" 
            scope="session"
          parameter="/vues/confirmation.jsp"
          type="org.apache.struts.actions.ForwardAction"
      />

      <action
          path="/init"
            name="dynaFormulaire"
            validate="false" 
            scope="session"
          type="istia.st.struts.formulaire.InitFormulaireAction"
      >
            <forward name="afficherFormulaire" path="/vues/formulaire.jsp"/>
        </action>

      <action
          path="/affiche"
          parameter="/vues/formulaire.jsp"
          type="org.apache.struts.actions.ForwardAction"
      />

    </action-mappings>

请注意,并非每个操作都关联有一个表单。上文中的 /affiche 操作便是如此。在详细介绍每个操作之前,让我们先回顾一下操作与表单的组合在 <action> 标签中是如何运作的:

  • 一个操作始于 Web 客户端的请求,终于响应页面的发送。这就是客户端-服务器 Web 请求-响应循环。请求由类型为 ActionServlet 或其派生类的 Struts 控制器接收。该控制器也会发送响应。
  • 如果尚未存在,则会创建类型为 ActionForm 或其派生类的表单 Bean。控制器会检查是否能在 scope 指定的作用域中找到名为 name 的对象。如果存在,则直接使用该对象;如果不存在,则创建该对象并将其放入 scope 指定的作用域中同时将其与 name 指定的属性关联起来。

例如,在 /init 操作的示例中,控制器将调用 request.getSession().getAttribute("dynaFormulaire") 来确定 dynaFormulaire 是否已创建。若未创建,则通过类似 request.getSession().setAttribute("dynaFormulaire", new DynaFormulaire(...)) 的语句创建并将其添加到会话中。

  • 控制器还会查找类型属性指定的 Action 对象。若未找到,则创建该对象;否则,直接使用该对象。
  • 表单 Bean 的 reset 方法将被调用。该 Bean 除初始创建时外,其余情况均会被复用。因此,它可能包含您需要“清理”的数据。此操作可在 ActionForm Bean 或其派生类的 reset 方法中完成。
  • 如果该操作是提交表单的目标,则客户端请求中找到的表单值会被复制到表单 Bean 中同名的字段中。请注意,复制操作之前已调用了 reset 方法。
  • 如果配置中指定了 validate="true" 属性,则会调用表单 Bean 的 validate 方法。该方法必须对 Bean 中的数据进行验证。这种验证通常仅在表单刚通过提交的表单接收新数据,且您需要验证该数据有效性时发生。该方法会将错误列表通过 ActionErrors 对象返回给控制器。
  • 如果 ActionErrors 对象不为空,控制器将显示由操作的 input 属性指定的视图。
  • 如果不需要数据验证,或者验证成功,控制器将调用与当前操作关联的 `Action` 对象(或其派生类)的 `execute` 方法。Web 客户端的请求就是在该方法中处理的。`execute` 方法返回一个以字符串键为索引的 `ActionForward` 对象。这些键是由已配置操作的 `forward` 标签声明的。 在我们的示例中,`/init` 操作包含一个 `forward` 标签。它将键 "displayForm" 与 `form.jsp` 视图关联起来。
  • 控制器会显示与接收到的键关联的视图。该视图实际上可能是一个动作,在这种情况下,上述过程将重复执行。

/init 操作

        <action
          path="/init"
            name="dynaFormulaire"
            validate="false" 
            scope="session"
          type="istia.st.struts.formulaire.InitFormulaireAction"
      >
            <forward name="afficherFormulaire" path="/vues/formulaire.jsp"/>
        </action>
  • /init 操作通常在用户请求 URL http://localhost:8080/formulaire2/init.do 时,于首次请求-响应循环中发生一次
  • dynaForm 对象被创建或回收。它将根据 scope 属性的指定,从会话中检索(回收)或放入会话(创建)。
  • reset 方法被调用。它应该做什么?通常,ActionForm 对象的字段会被重置为默认值。但在本例中,我们不会这样做,因为 dynaFormulaire 对象被放置在会话中(scope="session")。因此,dynaFormulaire 的字段必须保留其值。在初始创建 dynaFormulaire 对象时,这些值是什么?有两种情况:
  • 字段在配置文件中指定了初始值:
            <form-property name="opt" type="java.lang.String" initial="non"/>

在这种情况下,Struts 控制器将使用该初始值创建该字段。

  • 如果配置中未为该字段指定初始值:则适用 Java 的初始化规则。通常,数值字段的初始值为零,字符串字段的初始值为空字符串,其他对象的初始值为 null

让我们来看一下 dynaFormulaire 的初始配置:

        <form-bean name="dynaFormulaire" type="istia.st.struts.formulaire.DynaFormulaire">
            <form-property name="opt" type="java.lang.String" initial="non"/>
            <form-property name="chk1" type="java.lang.String"/>
            <form-property name="chk2" type="java.lang.String"/>
            <form-property name="chk3" type="java.lang.String"/>            
            <form-property name="champSaisie" type="java.lang.String" initial=""/>
            <form-property name="mdp" type="java.lang.String" initial=""/>        
            <form-property name="boiteSaisie" type="java.lang.String" initial=""/>        
            <form-property name="combo" type="java.lang.String"/>
            <form-property name="listeSimple" type="java.lang.String"/>
            <form-property name="listeMultiple" type="java.lang.String[]"/>                
            <form-property name="secret" type="java.lang.String" initial="xxx"/>
            <form-property name="valeursCombo" type="java.lang.String[]" />
            <form-property name="valeursListeSimple" type="java.lang.String[]" />
            <form-property name="valeursListeMultiple" type="java.lang.String[]"/>             
        </form-bean>            

创建后,dynaFormulaire 字段的初始值如下:

字段
初始值
opt
"no"
chk1, chk2, chk3
空字符串
输入字段
空字符串
mdp
空字符串
输入框
空字符串
下拉菜单
空字符串
简单列表
空字符串
多重列表
空字符串数组
秘密
"xxx"
comboValues、singleList、multipleList
空字符串数组
  • 人们可能会认为,dynaFormulairereset 方法会为这三个数组赋值,从而填充 formulaire.jsp 视图中的三个列表。这在理论上是可行的,因为这三个数组中的数据是任意生成的。 然而,最常见的情况是这些数据来自应用程序模型,即 MVC 中的 M。在此,为了简化示例,我们将采取折中方案,由 InitFormulaireAction 动作(即 MVC 中的 C)生成这些值。
  • dynaFormulaire 中无需编写 reset 方法,因为其继承的 ActionForm 类本身已包含一个不执行任何操作(不进行初始化)的 reset 方法。
  • 一旦调用 dynaFormulairereset 方法,控制器会检查该操作的 validate 属性。此时,该属性的值为“false”。因此,dynaFormulairevalidate 方法将不会被调用。
  • 系统会创建 InitFormulaireAction 对象(若已存在则复用),并调用其 execute 方法。该方法会向 dynaFormulaire 的三个数组(valeursCombovaleursListeSimple valeursListeMultiple)赋值。该方法返回一个键为 "afficherFormulaire" 的 ActionForward 对象
  • 控制器显示 /vues/formulaire.jsp 视图,该视图已通过 /init 动作中的 forward 标签与“afficherFormulaire”键相关联。

/confirmation 操作

      <action
          path="/confirmation"
            name="dynaFormulaire"
            validate="false" 
            scope="session"
          parameter="/vues/confirmation.jsp"
          type="org.apache.struts.actions.ForwardAction"
      />
  • 当用户点击视图 #1 上的 [提交] 按钮时,将触发 /confirmation 操作。随后,浏览器会将用户填写的表单“提交”至 Struts 控制器。
  • dynaFormulaire 对象将从会话中检索
  • reset 方法被调用。一旦该方法被调用,Struts 控制器就会将客户端提交的表单字段值复制到 dynaFormulaire 中同名的字段中。让我们回顾一下 dynaFormulaire 中的字段列表,看看这种复制是如何进行的:
字段
关联的 HTML 代码
字段值
表单值复制后的状态
opt
<input type="radio" name="opt" value="yes">是
<input type="radio" name="opt" value="no" checked="checked">否
- 根据所选单选按钮,返回“yes”或“no”
chk1
<input type="checkbox" name="chk1" value="on">
- 若已勾选 chk1 复选框,则为 "on"
- 若未勾选 chk1 复选框,则保留其先前值
chk2
<input type="checkbox" name="chk2" value="on">
- 若 chk2 复选框已被选中,则为 "on"
- 如果 chk2 复选框未被选中,则保留其先前值
chk3
<input type="checkbox" name="chk2" value="on">
- 若 chk3 复选框已被选中,则为 "on"
- 如果未勾选 chk3 复选框,则保留其先前值
inputField
<input type="text" name="inputField" value="">
- 用户在 inputField 中输入的值
密码
<input type="password" name="password" value="">
- 用户在 password 字段中输入的值
输入框
<textarea name="inputBox"></textarea>
- 用户在文本框中输入的值
下拉菜单
<select name="combo">...</select>
- 用户在下拉列表中选择的值
simpleList
<select name="simpleList" size="3">...</select>
- 用户在 simpleList 中选择的值
multipleList
<select name="listeMultiple" multiple="multiple" size="5">
- 包含用户在 multipleList 中所选值的字符串数组
secret
<input type="hidden" name="secret" value="xxx">
- "xxx"。

我们遇到一个问题:某些字段在浏览器发送的请求中未必会收到值。这适用于复选框 chk1chk3 以及两个列表 listeSimplelisteMultiple。在这种情况下,这些字段会保留其先前值——即在上一次请求-响应循环中获取的值。

以复选框 chk1 为例,假设在之前的请求-响应循环中,用户已勾选了该复选框。随后,浏览器在其请求的参数字符串中发送了 chk1="on" 这一信息。因此,构造函数将值 "on" 赋给了 dynaFormulairechk1 字段。现在假设在当前循环中,用户未勾选 chk1 复选框。 在这种情况下,在新请求的参数字符串中,浏览器不会发送类似 chk1="off" 的内容,而是什么也不发送。结果,dynaFormulaire 中的 chk1 字段将保留其 "on" 值,因此其值无法反映用户提交表单时的实际状态。 我们将使用 dynaFormulairereset 方法来解决此问题。在此方法中,我们将把 chk1、chk2 和 chk3 这三个字段设置为“off”。在我们的 chk1 示例中,用户要么:

  • 勾选 chk1 复选框。此时,浏览器会发送 chk1="on" 的信息,dynaFormulaire 中的 chk1 字段将变为 "on"
  • 未勾选 chk1 复选框。此时浏览器不会发送 chk1 字段的值,该字段将保留其之前的值“off”。无论哪种情况,dynaFormulairechk1 字段存储的值都是正确的。

对于 simpleListmultiList 列表,情况类似。如果这些列表中未选中任何选项,它们将不会出现在请求参数中,因此会保留其先前值。在 dynaFormulaire 的 reset 方法中,我们将使用空字符串重置 simpleList,并使用长度为 0 的字符串数组重置 multiList

  • 一旦调用 dynaFormulairereset 方法,控制器会将客户端请求中发送给它的信息复制回 dynaFormulaire 的字段中
  • 创建或复用一个 ForwardAction 对象,并调用其 execute 方法。ForwardAction 是一个预定义类,它返回一个 ActionForward 对象,该对象指向由动作的“parameter”属性定义的视图,在本例中即 /vues/confirmation.jsp
  • 控制器发送此视图。循环完成。

/display

      <action
          path="/affiche"
          parameter="/vues/formulaire.jsp"
          type="org.apache.struts.actions.ForwardAction"
      />
  • 点击视图 #2 上的 [返回表单] 链接将触发 /display 操作。
  • 此处该操作未关联任何表单。因此,我们将直接执行 `ForwardAction` 对象的 `execute` 方法,该方法将返回一个指向视图 `/vues/formulaire.jsp` 的 `ActionForward` 对象。

6.3.6. 应用程序的消息文件

struts-config.xml 文件的第三部分是消息文件:

        <message-resources 
          parameter="ApplicationResources"
            null="false"
      />    

ApplicationResources.properties 文件位于 WEB-INF/classes 目录下。该文件为空。尽管文件为空,仍必须在配置文件中声明它;否则,我们稍后将讨论的 struts-bean 标签库会报错。该库由 confirmation.jsp 视图使用。

6.4. 视图代码

6.4.1. formulaire.jsp 视图

请记住,该视图在以下两种情况下显示:

  • 在首次请求-响应循环中调用 /init 操作时
  • 在后续请求-响应循环中调用 /affiche 操作时

formulaire.jsp 视图的代码如下:

<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>

<html>
  <head>
      <title>formulaire</title>
  </head>  
  <body background='<html:rewrite page="/images/standard.jpg"/>'>
      <h3>Formulaire Struts</h3>
    <hr>
    <html:form action="/confirmation" name="dynaFormulaire" type="istia.st.struts.formulaire.DynaFormulaire">
      <table border="0">
        <tr>
          <td>bouton radio</td>
          <td>
                        <html:radio name="dynaFormulaire" property="opt" value="oui">Oui</html:radio>
                        <html:radio name="dynaFormulaire" property="opt" value="non">Non</html:radio>                                            
          </td>
        </tr>
        <tr>
          <td>Cases à cocher</td>
          <td>
                        <html:checkbox name="dynaFormulaire" property="chk1">1</html:checkbox>
                        <html:checkbox name="dynaFormulaire" property="chk2">2</html:checkbox>
                        <html:checkbox name="dynaFormulaire" property="chk3">3</html:checkbox>                                                
          </td>
        </tr>
        <tr>
          <td>Champ de saisie</td>
          <td>
                        <html:text name="dynaFormulaire" property="champSaisie" />
          </td>
        </tr>
        <tr>
          <td>Mot de passe</td>
          <td>
              <html:password name="dynaFormulaire" property="mdp" />
          </td>
        </tr>
        <tr>
          <td>Boîte de saisie multilignes</td>
          <td>
              <html:textarea name="dynaFormulaire" property="boiteSaisie" />
          </td>
        </tr>
        <tr>
          <td>Combo</td>
          <td>
              <html:select name="dynaFormulaire" property="combo">
                            <html:options name="dynaFormulaire" property="valeursCombo"/>
                        </html:select>
          </td>
        </tr>
        <tr>
          <td>
                        <table>
                            <tr>
                                <td>Liste à sélection unique</td>
                            </tr>
                            <tr>
                                <td>
                                    <input type="button" value="Effacer" onclick="this.form.listeSimple.selectedIndex=-1"/>
                                </td>
                            </tr>
                        </table>
          <td>
              <html:select name="dynaFormulaire" property="listeSimple" size="3">
                            <html:options name="dynaFormulaire" property="valeursListeSimple"/>                        
                        </html:select>
          </td>
        </tr>
        <tr>
                    <td>
                        <table>
                            <tr>
                                <td>Liste à sélection multiple</td>
                            </tr>
                            <tr>
                                <td>
                                    <input type="button" value="Effacer" onclick="this.form.listeMultiple.selectedIndex=-1"/>                                
                                </td>
                            </tr>
                        </table>
                    </td>
          <td>
              <html:select name="dynaFormulaire" property="listeMultiple" size="5" multiple="true">
                            <html:options name="dynaFormulaire" property="valeursListeMultiple"/>                        
                        </html:select>
          </td>
        </tr>
      </table>
      <html:hidden name="dynaFormulaire" property="secret"/>
            <br>
            <hr>
            <html:submit>Envoyer</html:submit> 
    </html:form>
  </body>
</html>

此 JSP 页面使用了 struts-html 标签库中的标签。请记住,要使用标签库,您必须:

  • 在应用程序的 web.xml 文件中使用 <tag-lib> 标签进行声明
  <taglib>
      <taglib-uri>/WEB-INF/struts-html.tld</taglib-uri>
    <taglib-location>/WEB-INF/struts-html.tld</taglib-location>
  </taglib>
  • 将此库的代码放置在应用程序目录树的某个位置,此处为 WEB-INF/struts-html.tld
  • 在使用该库的 JSP 页面开头声明使用该库:
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>

formulaire.jsp 视图中使用了以下标签,下面我们将对此进行说明:

标签
<body background="<html:rewrite page="/images/standard.jpg"/>">
HTML 转换
<body background="/formulaire2/images/standard.jpg">
说明
html:rewrite 标签允许您在 URL 中省略应用程序名称。它有一个属性:
page
要重写的 URL
因此,在上例中,如果您决定将应用程序命名为“form3”,则背景属性的代码无需重写。`html:rewrite` 标签将生成新的 HTML 代码
background="/formulaire3/images/standard.jpg"
标签
<html:form action="/confirmation" name="dynaFormulaire" type="istia.st.struts.formulaire.DynaFormulaire">
HTML 翻译
<form name="dynaFormulaire" method="post" action="/formulaire2/confirmation.do">
说明
html:form 标签生成 HTML 表单标签。它具有以下几个属性:
action
表单必须提交到的 Struts 操作——必须与 struts-config.xml 中定义的操作之一相对应
name
可选 - ActionForm Bean 或其子类的名称,提交的表单值必须被放入或从中读取。如果省略此属性,则使用由 action 属性定义的操作关联的表单。此信息可在配置文件中找到(即操作的 name 属性)。
type
可选 - 用于实例化表单的 Java 类。如果省略此属性,则使用与 action 属性定义的操作关联的类。该信息可在配置文件中找到(操作的 type 属性)。
我们可以看到,默认情况下,生成的 HTML 代码使用 POST 方法。在同一段 HTML 代码中,action 的 URL 已被重写,使其前缀为应用程序名称,后缀为 .do。
标签
<html:radio name="dynaFormulaire" property="opt" value="yes">是</html:radio>
HTML 翻译
<input type="radio" name="opt" value="yes">
说明
html:radio 标签用于生成 HTML 标签 <input type="radio" ...>。它支持多种属性:
name
可选 - 指定该字段的值必须写入或读取的 ActionForm Bean 或派生 Bean 的名称。如果省略此属性,则使用与 <html:form> 标签关联的表单。
property
用于读写与单选按钮关联的表单 Bean 中字段的名称。
value
可选 - 要赋给 HTML 字段的值
开始标签和结束标签之间的文本将显示在单选按钮旁边。
标签
<html:checkbox name="dynaFormulaire" property="chk1">1</html:checkbox>
HTML 翻译
<input type="checkbox" name="chk1" value="on">
说明
html:checkbox 标签用于生成 HTML 标签 <input type="checkbox" ...>。它支持多种属性:
name
可选 - 指定该字段的值必须写入或读取的 ActionForm Bean 或派生 Bean 的名称。如果省略此属性,则使用与 <html:form> 标签关联的表单。
property
用于读写与复选框关联的表单 Bean 中字段的名称。
value
可选 - 要赋给 HTML 字段的值
开始标签和结束标签之间的文本将显示在复选框旁边。
标签
<html:text name="dynaFormulaire" property="inputField" />
HTML 翻译
<input type="text" name="inputField" value="">
说明
html:text 标签用于生成 HTML 标签 <input type="text" ...>。它支持多种属性:
name
可选 - 指定用于存放或读取该字段值的 ActionForm Bean 或其派生 Bean 的名称。如果省略此属性,则使用与 <html:form> 标签关联的表单。
属性
与该可读写输入字段关联的表单 Bean 中字段的名称。
value
可选 - 要赋给 HTML 字段的值
标签
<html:password name="dynaFormulaire" property="mdp" />
HTML 翻译
<input type="password" name="mdp" value="">
说明
html:password 标签用于生成 HTML 标签 <input type="password" ...>。它支持多种属性:
标签
<html:textarea name="dynaFormulaire" property="boiteSaisie" />
HTML 翻译
<textarea name="boiteSaisie"></textarea>
说明
HTML:textarea 标签用于生成 HTML 标签 <textarea>...</textarea>。它支持多种属性:
name
可选 - 指定该字段值必须写入或读取的 ActionForm Bean 或派生 Bean 的名称。如果省略此属性,则使用与 <html:form> 标签关联的表单。
property
用于读写密码字段的表单 Bean 中字段的名称。
value
可选 - 要赋给 HTML 字段的值
标签
<html:select name="dynaFormulaire" property="combo">....</html:select>
HTML 翻译
<select name="combo">...</select>
说明
HTML:select 选择器用于生成 HTML 标签 <select>...</select>。它支持多种属性:
name
可选 - 指定必须将字段值写入或从中读取的 ActionForm Bean 或派生 Bean 的名称。如果省略此属性,则使用与 <html:form> 标签关联的表单。
property
与该读写输入字段关联的表单 Bean 中字段的名称。
value
可选 - 要赋给 HTML 字段的值。下拉列表中具有此值的选项将被选中。
标签
<html:select name="dynaFormulaire" property="combo">
<html:options name="dynaFormulaire" property="comboValues"/>
</html:select>
HTML 翻译
<option value="combo1">combo1</option>
<option value="combo2">combo2</option>

说明
HTML:options 标签用于在 HTML <select> 标签内部生成 <option>...</option> 这些 HTML 标签。指定如何查找用于填充 select 元素的值有多种方法。在此,我们使用了 name property 属性:
name
可选 - 用于指定应将字段值写入或读取的 ActionForm Bean 或派生 Bean 的名称。如果省略此属性,则使用与 <html:form> 标签关联的表单。
property
表单 Bean 中用于存储下拉选项值的字段名称。该属性必须为字符串数组。

另外两个列表的生成方式与前一个类似:

          <html:select name="dynaFormulaire" property="listeSimple" size="3">
            <html:options name="dynaFormulaire" property="valeursListeSimple"/>            
        </html:select>

在上文中,我们指定了一个不为 1 的 size 属性,以创建列表而非下拉框。

<html:select name="dynaFormulaire" property="listeMultiple" size="5" multiple="true">
        <html:options name="dynaFormulaire" property="valeursListeMultiple"/>                
    </html:select>

在上文中,我们指定了 multiple="true" 属性,以创建一个支持多选的列表。

标签
<html:hidden name="dynaFormulaire" property="secret"/>
HTML 翻译
<input type="hidden" name="secret" value="xxx">
说明
html:hidden 标签用于生成 HTML 标签 <input type="hidden" ...>。
name
可选 - 指定该字段的值必须写入或读取的 ActionForm Bean 或派生 Bean 的名称。如果省略此属性,则使用与 <html:form> 标签关联的表单。
property
与隐藏字段关联的表单 Bean 中字段的名称。分配给秘密 HTML 字段的值“xxx”来自配置文件中的表单定义。该配置文件中已为秘密字段定义了初始值“xxx”。

要充分理解 formulaire.jsp 视图与其在内存中对应的 dynaFormulaire Bean 之间的关系,必须记住 dynaFormulaire Bean 既用于读取,也用于写入:

当用户点击表单上的 [提交] 按钮时,会触发请求。随后,浏览器将 HTML 表单“POST”到 /confirmation 操作。我们之前已经解释过此时会发生什么,特别是 dynaFormulaire 字段将接收 HTML 表单中同名字段的值。

当控制器响应请求时要求显示 formulaire.jsp 页面,会发生什么?让我们逐一仔细查看这些标签:

标签
<body background="<html:rewrite page="/images/standard.jpg"/>">
函数
生成 HTML 代码
<body background="/formulaire2/images/standard.jpg">
标签
<html:form action="/confirmation" name="dynaFormulaire" type="istia.st.struts.formulaire.DynaFormulaire">
...
</html:form>
函数
生成 HTML 代码
<form name="dynaFormulaire" method="post" action="/formulaire2/confirmation.do">
标签
<html:radio name="dynaFormulaire" property="opt" value="yes">是</html:radio>
<html:radio name="dynaFormulaire" property="opt" value="no">否</html:radio>
函数
如果 dynaFormulaire 的 opt 字段为 "yes",则生成 HTML 代码
<input type="radio" name="opt" value="yes" checked="checked"><input type="radio" name="opt" value="no">
标签
<html:checkbox name="dynaFormulaire" property="chk1">1</html:checkbox>
<html:checkbox name="dynaFormulaire" property="chk2">2</html:checkbox>
<html:checkbox name="dynaForm" property="chk3">3</html:checkbox>
function
如果 dynaFormulaire 的 chk1 chk3 字段为“on”,而 chk2 字段为“off”,则生成 HTML 代码
<input type="checkbox" name="chk1" value="on" checked="checked">1
<input type="checkbox" name="chk2" value="on">2
<input type="checkbox" name="chk3" value="on" checked="checked">3
标签
<html:text name="dynaFormulaire" property="inputField" />
函数
如果输入字段的值设置为“this is a test”,则生成 HTML 代码
<input type="text" name="inputField" value="this is a test">
标签
<html:password name="dynaFormulaire" property="password" />
函数
如果密码字段为“azerty”,则生成 HTML 代码
<input type="password" name="password" value="azerty">
标签
<html:password name="dynaFormulaire" property="mdp" />
函数
如果“mdp”字段为“azerty”,则生成 HTML 代码
<input type="password" name="password" value="azerty">
标签
<html:password name="dynaFormulaire" property="mdp" />
函数
如果“mdp”字段为“azerty”,则生成 HTML 代码
<input type="password" name="password" value="azerty">
标签
<html:select name="dynaFormulaire" property="combo">
<html:options name="dynaFormulaire" property="comboValues"/>
</html:select>
函数
如果下拉列表字段为“combo2”,则生成 HTML 代码
<select name="combo">
    <option value="combo0">combo0</option>
    <option value="combo1">combo1</option>
    <option value="combo2" selected="selected">combo2</option>
    <option value="combo3">combo3</option>
    <option value="combo4">combo4</option>
</select>
标签
<html:select name="dynaFormulaire" property="simpleList" size="3">
<html:options name="dynaFormulaire" property="simpleListValues"/>
</html:select>
函数
如果 simpleList 字段为 "simple1",则生成 HTML 代码
<select name="simpleList" size="3">
    <option value="simple0">simple0</option>
    <option value="simple1" selected="selected">simple1</option>
    <option value="simple2">simple2</option>
...
</select>
标签
<html:select name="dynaFormulaire" property="listeMultiple" size="5" multiple="true">
<html:options name="dynaFormulaire" property="multipleListValues"/>
</html:select>
function
如果 listeMultiple 字段是一个包含 {"multiple0", "multiple2"} 的数组,则生成以下 HTML 代码
<select name="multipleList" multiple="multiple" size="5">
    <option value="multiple0" selected="selected">multiple0</option>
    <option value="multiple1">multiple1</option>
    <option value="multiple2" selected="selected">multiple2</option>
    <option value="multiple3">multiple3</option>
...
</select>
标签
<html:hidden name="dynaFormulaire" property="secret"/>
函数
如果 secret 字段的值为 "xxx",则生成 HTML 代码
<input type="hidden" name="secret" value="xxx">
标签
<html:submit>提交</html:submit>
函数
生成 HTML 代码
<input type="submit" value="提交">

最后要说明的是 JSP 页面中包含的 JavaScript 代码,该代码与两个 [清除] 按钮相关联,用于取消选中 simpleListmultipleList 列表中的选定项:

<input type="button" value="Effacer" onclick="this.form.listeSimple.selectedIndex=-1"/>                                
<input type="button" value="Effacer" onclick="this.form.listeMultiple.selectedIndex=-1"/>

该标签

<html:form action="/confirmation" name="dynaFormulaire" type="istia.st.struts.formulaire.DynaFormulaire">

会生成以下 HTML 代码:

<form name="dynaFormulaire" method="post" action="/formulaire2/confirmation.do">

为了理解与 [清除] 按钮相关的 JavaScript 代码,让我们回顾一下在使用该文档的 JavaScript 代码中,如何引用 Web 文档的各个元素:

数据
含义
document
指整个网页文档
document.forms
指文档中定义的表单集合
document.forms[i]
表示文档中的第 i 个表单
document.forms["formName"]
名称属性设置为formName”的 <form> 表单
document.formName
名称属性等于“formName”的 <form> 表单
document.[form].elements
指由表达式 [form] 指定的表单所属的元素集合。该集合包含指定表单中的所有 <input>、<textarea> 和 <select> 标签。
document.[form].elements[i]
指 [form] 中的第 i 个元素
document.[form].elements["componentName"]
指 [form] 中 name 属性等于 componentName 的元素
document.[form].componentName
指 [form] 中 name 属性等于 componentName 的元素
document.[form].[component].value
指当 [form] 表单的 [component] 组件的 HTML 代码可能包含 value 属性(<input>、<textarea>)时,该组件的值
document.[form].[select].selectedIndex
指列表中选中选项的索引。可用于读取和写入。将此属性设置为 -1 将取消选中列表中的所有项目。
document.[form].[select].options
指与 <select> 标签关联的选项数组
document.[form].[select].options[i]
指指定 <select> 标签中的第 i 个选项
document.[form].[select].options[i].selected
一个布尔值,表示指定 [select] 元素的第 i 个选项是否被选中(true)。可用于读取和写入

让我们重新审视这两个按钮的 JavaScript 代码:

<input type="button" value="Effacer" onclick="this.form.listeSimple.selectedIndex=-1"/>                                
<input type="button" value="Effacer" onclick="this.form.listeMultiple.selectedIndex=-1"/>

当点击按钮时,与“onclick”属性关联的代码会被执行。这里是内联代码。通常,我们会写成 onclick="function(...)",其中 function 是在 <script language="javascript">...</script> 标签内定义的函数。上面的代码起什么作用?让我们来分析第一个按钮的代码:

this
指代包含该按钮的网页文档
this.form
指包含该按钮的表单
this.form.simpleList
指表单中的 simpleList 组件
this.form.simpleList.selectedIndex
表示在 listeSimple 中所选选项的索引。将此属性设置为 -1 可取消选中所有选项。

6.4.2. confirmation.jsp 视图

请注意,该视图在 /confirmation 操作之后显示,即在 Web 客户端提交 formulaire.jsp 视图中的表单之后。其唯一目的是显示用户输入的值。其代码如下:

<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>

<html>
    <head>
      <title>Confirmation</title>
  </head>
  <body background="<html:rewrite page="/images/standard.jpg"/>">
      <h3>Confirmation des valeurs saisies</h3>
    <hr/>
    <table border="1">
        <tr>
        <td>Bouton radio</td>
        <td><bean:write name="dynaFormulaire" scope="session" property="opt"/></td>
      </tr>
        <tr>
        <td>Case à cocher chk1</td>
        <td><bean:write name="dynaFormulaire" scope="session" property="chk1"/></td>
      </tr>
        <tr>
        <td>Case à cocher chk2</td>
        <td><bean:write name="dynaFormulaire" scope="session" property="chk2"/></td>
      </tr>
        <tr>
        <td>Case à cocher chk3</td>
        <td><bean:write name="dynaFormulaire" scope="session" property="chk3"/></td>
      </tr>

        <tr>
        <td>Champ de saisie</td>
        <td><bean:write name="dynaFormulaire" scope="session" property="champSaisie"/></td>
      </tr>
        <tr>
        <td>Mot de passe</td>
        <td><bean:write name="dynaFormulaire" scope="session" property="mdp"/></td>
      </tr>

        <tr>
        <td>Boîte de saisie</td>
        <td><bean:write name="dynaFormulaire" scope="session" property="boiteSaisie"/></td>
      </tr>

        <tr>
        <td>combo</td>
        <td><bean:write name="dynaFormulaire" scope="session" property="combo"/></td>
      </tr>
        <tr>
        <td>liste simple</td>
        <td><bean:write name="dynaFormulaire" scope="session" property="listeSimple"/></td>
      </tr>
            <logic:iterate id="choix" indexId="index" name="dynaFormulaire" property="listeMultiple">
          <tr>
          <td>liste multiple[<bean:write name="index"/>]</td>
          <td><bean:write name="choix"/></td>
        </tr>
            </logic:iterate>
    </table>
        <br>
    <html:link page="/affiche.do">
            Retour au formulaire
        </html:link>    
  </body>
</html>

在此,我们将介绍两个新的标签库:struts-beanstruts-logicstruts-bean 库提供了访问请求、会话或应用程序上下文中对象的途径。struts-logic 库允许您使用标签来实现执行逻辑。这两个库都不是绝对必要的。正如我们所见,一个 JSP 页面可以:

  • 从请求(request.getAttribute(...))、会话(session.getAttribute(...))或应用程序上下文中检索对象
  • 使用变量 <%= 变量 %> 在 HTML 代码中插入动态元素
  • 包含 Java 代码 <% Java 代码 %>

在 JSP 页面中包含 Java 代码,让那些希望严格区分应用逻辑(Java 代码)与呈现(标签的使用)的人感到困扰。正因如此,才为他们创建了标签库。

我们将按照处理 formulaire.jsp 视图时的做法进行,并对 confirmation.jsp 代码中出现的每个标签进行说明(如果这些标签在 formulaire.jsp 视图中尚未出现过)。首先,请注意该页面开头声明了将要使用的三个标签库:

<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>

另请注意,这三个库必须在应用程序的 web.xml 文件中进行声明。接下来我们将对 formulaire.jsp 文档中的标签进行说明:

标签
<bean:write name="dynaFormulaire" scope="session" property="opt"/>
function
将值写入当前的 HTML 流。bean:write 标签支持以下属性:
name:要使用的对象名称
scope:用于查找该对象的作用域(request、session、context)
property:指定要写入其属性的、由 name 标识的对象的字段。该字段可以是任何类型的对象。将使用该对象的 toString 方法。
此处将写入 dynaFormulaire 中 opt 字段的值。结果将根据用户所选单选按钮的 value="yes" 或 value="no" 属性,分别显示为 "yes" 或 "no"
标签
<bean:write name="dynaFormulaire" scope="session" property="chk1"/>
函数
dynaFormulairechk1 字段的值写入该字段。如果用户勾选了复选框,结果为“on”;否则为“off”。chk2 chk3 同样适用。
标签
<bean:write name="dynaFormulaire" scope="session" property="inputField"/>
函数
dynaFormulairechampSaisie 字段的值写入,即用户在该字段中输入的文本。mdp boiteSaisie 同样适用
标签
<bean:write name="dynaFormulaire" scope="session" property="combo"/>
函数
dynaFormulaire 中下列表字段的值写入。该值即为用户选中的 <option> 元素的 value 属性值。
标签
<bean:write name="dynaFormulaire" scope="session" property="listeSimple"/>
函数
dynaFormsimpleList 字段的值写入。如果用户选择了某个 <option> 元素,该值将包含该元素的 value 属性;否则,该值为空字符串。
标签
            <logic:iterate id="choice" indexId="index" name="dynaForm" property="multipleList">
          <tr>
          <td>多选列表[<bean:write name="index"/>]</td>
          <td><bean:write name="choice"/></td>
        </tr>
            </logic:iterate>
函数
在此,我们将介绍逻辑标签。我们正在处理一个多选列表。dynaFormulaire 对象的 listeMultiple 字段的值是一个字符串数组。在 Java 中,我们会编写一个循环。logic:iterate 标签允许我们在不编写 Java 代码的情况下执行相同的循环。在此示例中,logic:iterate 标签具有以下属性:
name="dynaFormulaire":要使用的对象名称
property="listeMultiple":指定由 name 标识的对象中,包含循环迭代集合的属性名称。此处该集合即为 listeMultiple 中选中的值数组。该数组可能为空。
id="choix":标识符,用于在每次迭代中指定数组的当前元素。在第一次迭代中,choix 将表示 listeMultiple[0];在第二次迭代中表示 listeMultiple[1],依此类推。
indexID="index": 标识符,用于在每次循环迭代中指定当前数组元素的索引。在第一次迭代中,index 的值为 0;在第二次迭代中,值为 1;以此类推。
位于 <logic:iterate ...> </logic:iterate> 标签之间的 HTML 代码将针对由 (name,property) 对指定的集合中的每个元素重复执行。该代码的动态部分如下:
          <td>multiple list[<bean:write name="index"/>]</td>
          <td><bean:write name="choice"/></td>
根据前文所述,在第 i 次迭代(i>=0)时,生成的 HTML 代码等同于以下内容:
          <td>多选列表[<%=i%>]</td>
          <td><%=multipleList[i]%></td>
标签
    <html:link page="/affiche.do">
            返回表单
        </html:link>
函数
会生成一个相对于应用程序上下文的链接,从而无需了解具体上下文。该标签生成的 HTML 代码如下:
<a href="/formulaire2/affiche.do">返回表单</a>

6.5. Java 类

struts-config.xml 配置文件引用了两个 Java 类:

        <form-bean name="dynaFormulaire" type="istia.st.struts.formulaire.DynaFormulaire">
...
      <action
          path="/init"
            name="dynaFormulaire"
            validate="false" 
            scope="session"
          type="istia.st.struts.formulaire.InitFormulaireAction"
      >

DynaFormulaire 类是用于存储来自视图 #1(formulaire.jsp)的值的类。InitFormulaireAction 类是用于处理通过 formulaire.jsp 上的 [提交] 按钮提交的表单值的类。

6.5.1. DynaFormulaire 类

要保存表单的值,一个 DynaActionForm 类型的对象就足够了,除非你需要重写该类的 resetvalidate 方法之一。 在此,由于未进行数据验证,因此无需重写 validate 方法。但必须重写 reset 方法。这是因为 DynaFormulaire 对象的字段将从 Web 客户端提交的表单中获取其值。然而,如果某些字段在请求中不存在,它们可能无法获得值。这种情况发生在以下情形中:

  • 用户未勾选的复选框
  • 包含多个选项但未被选中的下拉列表

对于包含此类组件的表单,必须重写 reset 方法

  • 将与该复选框关联的字段值设置为“off”
  • 将空字符串赋值给与单选列表关联的字段
  • 将一个空字符串数组赋值给与多选列表关联的字段

因此,如果这些字段未从查询中获取值,它们将保留由 reset 赋值的值,该值对应于用户验证表单时组件的状态(复选框未勾选,列表中未选中任何项目)。

作为 DynaActionForm 的子类,DynaFormulaire 类的代码如下:

package istia.st.struts.formulaire;

import org.apache.struts.action.DynaActionForm;
import org.apache.struts.action.ActionMapping;
import javax.servlet.http.HttpServletRequest;

public class DynaFormulaire extends DynaActionForm {
  public void reset(ActionMapping mapping, HttpServletRequest request){
    // reset checkboxes - value off
    set("chk1","off");
    set("chk2","off");
    set("chk2","off");
    // reset listeSimple - empty string
    set("listeSimple","");
     // reset listeMultiple - empty table
    set("listeMultiple",new String[]{});
  }
}

6.5.2. InitFormAction 类

InitFormAction 类与 struts-config.xml 文件中的 /init 动作相关联:

      <action
          path="/init"
            name="dynaFormulaire"
            validate="false" 
            scope="session"
          type="istia.st.struts.formulaire.InitFormulaireAction"
      >
            <forward name="afficherFormulaire" path="/vues/formulaire.jsp"/>
        </action>

/init 操作仅在 DynaForm 对象的初始化过程中调用一次。其目的是为表单的三个下拉列表(simpleListmultipleListdropdownList)提供内容。这些内容以三个数组的形式提供,这些数组是 dynaForm 对象的属性:

        <form-bean name="dynaFormulaire" type="istia.st.struts.formulaire.DynaFormulaire">
            <form-property name="opt" type="java.lang.String" initial="non"/>
...
            <form-property name="valeursCombo" type="java.lang.String[]" />
            <form-property name="valeursListeSimple" type="java.lang.String[]" />
            <form-property name="valeursListeMultiple" type="java.lang.String[]"/>                                          
        </form-bean>            

一旦 valuesCombovaluesSingleListvaluesMultipleList 这三个数组被 InitFormAction 初始化后,就不再需要再次初始化。这是因为 dynaForm 对象被放置在会话中,因此会在请求-响应循环中保留其值。这就是为什么 /init 操作只执行一次。InitFormAction 的代码如下:

package istia.st.struts.formulaire;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import org.apache.struts.action.*;

public class InitFormulaireAction
  extends Action {
  public ActionForward execute(ActionMapping mapping, ActionForm form,
       HttpServletRequest request, HttpServletResponse response) throws IOException,    ServletException {

     // prepares the form to be displayed

     // we put the information needed for the form in its bean
    DynaFormulaire formulaire = (DynaFormulaire) form;
    formulaire.set("valeursCombo", getValeurs(5, "combo"));
    formulaire.set("valeursListeSimple", getValeurs(7, "simple"));
    formulaire.set("valeursListeMultiple", getValeurs(10, "multiple"));
     // we give back
    return mapping.findForward("afficherFormulaire");
  } //execute

   // list of combo values
  private String[] getValeurs(int taille, String label) {
    String[] valeurs = new String[taille];
    for (int i = 0; i < taille; i++) {
      valeurs[i] = label + i;
    }
    return valeurs;
  }
}
  • 该类继承自 Action 类。这是强制要求的。
  • Struts 控制器通过其 execute 方法使用 Action 对象。因此,必须重写此方法。该方法接收以下参数:
    • ActionMapping mapping:一个表示 struts-config.xml 中应用程序配置的对象
    • ActionForm form:与该 Action 关联的表单,前提是在 Action 的配置中(即 Action 的 name 属性)定义了该表单。
    • HttpServletRequest request:客户端请求
    • HttpServletResponse:发回给客户端的响应
  • InitFormulaireAction 类必须初始化 dynaFormulaire 表单。该表单作为 ActionForm 表单参数传递给 execute 方法。请注意,dynaFormulaire 的类型为 DynaFormulaire,该类继承自 DynaActionForm 类,而 DynaActionForm 类又继承自 ActionForm 类。
  • execute 方法中,使用 DynaActionForm 类的 set 方法为 valeursCombovaleursListeSimple valeursListeMultiple 这三个字段赋值。为简化起见,这些值是任意的数组。 请注意,set 方法用于为现有字段赋值,无法用于创建新字段。因此,必须在 struts-config.xml 文件中定义 dynaFormulaire 对象时,预先声明 valeursCombovaleursListeSimple valeursListeMultiple 这三个字段。
  • execute 方法最后将要显示的视图的键作为响应返回给控制器。这里是 afficherFormulaire 键,该键在 struts-config.xml 文件中已与 /vues/formulaire.jsp 视图相关联。

6.6. 部署

应用程序的目录结构如下:

  

Image

Image

请注意,上面的 ApplicationResources.properties 文件是 struts-bean 标签库所必需的。我们知道该文件包含应用程序的消息,这些消息可供 struts-bean 库访问。在此,我们的应用程序未定义任何消息。因此,ApplicationResources.properties 文件虽然存在,但内容为空。

6.7. 结论

在本课中,我们详细介绍了如何管理 HTML 表单的各个组件。现在,我们可以在 Struts 应用程序中使用复杂的表单了。