Skip to content

6. 表单 HTML

迄今为止,我们仅使用了一个包含两个输入字段的表单。在此,我们将尝试创建并处理一个使用常用图形组件(单选按钮、复选框、输入字段、下拉列表、列表)的表单。

6.1. 应用程序的视图

该应用程序只有两个视图。第一个视图显示一个空白表单:

QZXW2HTMLP000546ZQX 1 - formulaire

Image

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

Image

点击按钮 [Envoyer] 可确认已输入的值。这将是第二个视图:

Image

通过这个第二视图,我们将能够使用另外两个标签库:struts-beanstruts-logic。链接 [Retour au formulaire] 允许我们返回已填写的表单。此时将回到第一个视图。

6.2. 应用程序架构

  • 表单(视图 1)将由一个名为 dynaFormulaire 的动态 Struts 对象表示,该对象的类型继承自 DynaActionForm。它将通过视图 formulaire.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 所需的数据
  • 在用户提交(submit)表单时,从视图 1 中获取表单值
  • 包含显示视图 2 所需的数据

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

编号
类型 HTML
角色
1
<input name="opt" type="radio" value="是">
<input name="opt" type="radio" value="否">
相互关联的单选按钮组(名称相同)
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="champSaisie">
一个输入字段
4
<input type="password" name="mdp">
一个密码输入框
5
<textarea name="boiteSaisie">...</textarea>
多行输入框
6
<select name="combo" size="1">..</select>
一个下拉列表
7
<select name="listeSimple" size="3">..</select>
单选列表
8
<select name="listeMultiple" size="3" multiple>..</select>
多选列表
9
<input type="button" value="清除"
onclick='effacerListe("listeSimple")'>
用于取消选中
listeSimple (7) 中选中的项目
10
<input type="button" value="清除"
onclick='effacerListe("listeMultiple")'>
用于取消选中
listeMultiple(8)中选中的项目
11
<input type="submit" value="提交">
表单提交按钮
12
<input type="hidden" name="secret" value="...">
一个隐藏字段

我们来区分几种情况:

  1. 对象 dynaFormulaire 用于存储上述表单 HTML 的值,该表单将通过按钮 [Envoyer] 提交。 因此,它必须包含与表单 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>            

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

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

视图 1 还需要其他信息:

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

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

        <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 操作初始化,该操作将调用一个名为 InitFormulaireActionAction 派生对象。 该对象将负责创建显示三个列表所需的三个数组,并将其放入 dynaFormulaire Bean 中。 配置文件将该 Bean 的作用域设为会话。这意味着 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 中的数据进行验证。 这种验证通常仅在表单刚通过 POST 请求接收新数据,且需要验证这些数据有效性时进行。该方法会将可能的错误列表以 ActionErrors 对象的形式返回给控制器。
  • 如果 ActionErrors 对象不为空,控制器将显示由操作的 input 属性指定的视图。
  • 如果未要求进行数据验证,或者验证成功,控制器将执行与当前操作关联的 Action 类型对象或其派生对象的 execute 方法。Web 客户端的请求将在该方法中得到处理。execute 方法返回一个由字符串类型键索引的 ActionForward 对象。 这些键由已配置操作的 forward 标签声明。在本例中,操作 /init 仅有一个 forward 标签。它将键 "afficherFormulaire" 与视图 formulaire.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
  • 对象 dynaFormulaire 被创建或回收。根据 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
"否"
chk1, chk2, chk3
空字符串
champSaisie
空字符串
mdp
空字符串
boiteSaisie
空字符串
combo
空字符串
listeSimple
空字符串
listeMultiple
空字符串数组
secret
"xxx"
valeursCombo, listeSimple, listeMultiple
空字符串数组
  • 可以设想,dynaFormulairereset 方法会为三个数组赋值,这些数组将用于填充视图 formulaire.jsp 中的三个列表。这里之所以可行,是因为这三个数组的数据是任意生成的。 然而,最常见的情况是这些数据来自应用程序的模型,即 MVC 中的 M。 在此,为了不使示例过于复杂,我们将采取折中方案,让 InitFormulaireAction 操作(即 MVC 中的 C 部分)生成这些值。
  • dynaFormulaire 无需编写 reset 方法,因为其派生类 ActionForm 已包含该方法,且该方法不执行任何操作(不进行初始化)。
  • 一旦调用 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中的按钮[Envoyer]时,将触发/confirmation操作。此时,浏览器会将用户填写好的表单“提交”给Struts控制器。
  • dynaFormulaire 对象被纳入会话
  • reset 方法被调用。该方法调用后,Struts 控制器将把客户端提交的表单字段值复制到 dynaFormulaire 中同名的字段中。让我们回顾一下后者的字段列表,看看复制过程是如何进行的:
字段
关联的 HTML 代码
字段值
(复制表单值后)
opt
<input type="radio" name="opt" value="是">是
<input type="radio" name="opt" value="no" checked="checked">否
- 根据选中的单选按钮,返回“是”或“否”
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 复选框,则保持其先前值
champSaisie
<input type="text" name="champSaisie" value="">
- 用户在 champSaisie 中输入的值
mdp
<input type="password" name="mdp" value="">
- 用户在 mdp 中输入的值
boiteSaisie
<textarea name="boiteSaisie"></textarea>
- 用户在 boiteSaisie 中输入的值
combo
<select name="combo">...</select>
- 用户在 combo 中选择的值
listeSimple
<select name="listeSimple" size="3">...</select>
- 用户在 listeSimple 中选择的值
listeMultiple
<select name="listeMultiple" multiple="multiple" size="5">
- 包含用户在 listeMultiple 中所选值的字符串数组
secret
<input type="hidden" name="secret" value="xxx">
- "xxx"。

对于那些在浏览器发送的请求中未必会收到值的字段,我们遇到了一些困难。例如复选框 chk1chk3,以及两个列表 listeSimplelisteMultiple。 在这种情况下,这些字段会保留其先前值,即在上一个请求-响应循环中获取的值。

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

  • 勾选了 chk1 复选框。此时浏览器会发送 chk1="on" 的信息,dynaFormulaire 中的 chk1 字段将变为 "on"
  • 未勾选 chk1 复选框。此时浏览器不会发送 chk1 字段的值,该字段将保持其之前的“off”值。在两种情况下,dynaFormulairechk1 字段记录的值都是正确的。

对于列表 listeSimplelisteMultiple,问题也是类似的。如果在这两个列表中未选择任何选项,它们将不会出现在查询参数中,因此会保留其之前的值。 在 dynaFormulairereset 方法中,我们将使用空字符串重置 listeSimple,并使用长度为 0 的字符串数组重置 listeMultiple

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

该操作 /显示

      <action
          path="/affiche"
          parameter="/vues/formulaire.jsp"
          type="org.apache.struts.actions.ForwardAction"
      />
  • /affiche 操作由视图 2 中的链接 [Retour vers le formulaire] 触发。
  • 此处没有与该操作关联的表单。因此,系统会立即执行 ForwardAction 对象的 execute 方法,该方法将返回一个指向视图 /vues/formulaire.jspActionForward 对象。

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
  • 在使用该库的页面开头声明其引用:
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>

视图 formulaire.jsp 使用了以下标签,现对其进行说明:

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

explication
html:options 标签用于在 HTML <select> 标签内生成 HTML <option>...</option> 标签。指定如何查找下拉列表填充值的方法有多种。在此,我们使用了 name property 属性:
name
可选 - 用于放置或读取字段值的 Bean(ActionForm 或其派生类)的名称。若未指定此属性,则使用与 <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" 属性,以实现多选列表。

balise
<html:hidden name="dynaFormulaire" property="secret"/>
traduction HTML
<input type="hidden" name="secret" value="xxx">
explication
html:hidden 标签用于生成 HTML <input type="hidden" ...> 标签。
name
可选 - 用于存放或读取字段值的 Bean 名称(ActionForm 或其派生名称)。若未指定此属性,则使用与 <html:form> 标签关联的表单。
property
与隐藏字段关联的表单 Bean 中的字段名称。分配给 HTML secret 字段的值“xxx”源自配置文件中的表单定义。其中为 secret 字段定义了初始值“xxx”。

要充分理解视图 formulaire.jsp 与其在内存中表示该视图的 Bean dynaFormulaire 之间的关联,需注意 Bean dynaFormulaire 同时用于读写操作:

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

当控制器响应请求时,要求显示视图 formulaire.jsp,此时会发生什么?让我们逐一重新审视这些标签:

balise
<body background="<html:rewrite page="/images/standard.jpg"/>">
fonction
生成代码 HTML
<body background="/formulaire2/images/standard.jpg">
balise
<html:form action="/confirmation" name="dynaFormulaire" type="istia.st.struts.formulaire.DynaFormulaire">
...
</html:form>
fonction
生成代码 HTML
<form name="dynaFormulaire" method="post" action="/formulaire2/confirmation.do">
balise
<html:radio name="dynaFormulaire" property="opt" value="是">是</html:radio>
<html:radio name="dynaFormulaire" property="opt" value="no">否</html:radio>
fonction
如果 dynaFormulaire 的 opt 字段值为 "是",则生成代码 HTML
<input type="radio" name="opt" value="oui" checked="checked">Oui
<input type="radio" name="opt" value="non">Non
balise
<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>
fonction
如果 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                                                
balise
<html:text name="dynaFormulaire" property="champSaisie" />
fonction
如果字段 champSaisie 的值为“这是测试”,则生成代码 HTML
<input type="text" name="champSaisie" value="ceci est un essai">
balise
<html:password name="dynaFormulaire" property="mdp" />
fonction
如果 mdp 字段值为 "azerty",则生成代码 HTML
<input type="password" name="mdp" value="azerty">
balise
<html:password name="dynaFormulaire" property="mdp" />
fonction
如果 mdp 字段值为 "azerty",则生成代码 HTML
<input type="password" name="mdp" value="azerty">
balise
<html:password name="dynaFormulaire" property="mdp" />
fonction
如果 mdp 字段值为 "azerty",则生成代码 HTML
<input type="password" name="mdp" value="azerty">
balise
<html:select name="dynaFormulaire" property="combo">
<html:options name="dynaFormulaire" property="valeursCombo"/>
</html:select>
fonction
如果拉框的值为“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>
balise
<html:select name="dynaFormulaire" property="listeSimple" size="3">
<html:options name="dynaFormulaire" property="valeursListeSimple"/>
</html:select>
fonction
如果字段 listeSimple 的值为 "simple1",则生成代码 HTML
<select name="listeSimple" size="3">
    <option value="simple0">simple0</option>
    <option value="simple1" selected="selected">simple1</option>
    <option value="simple2">simple2</option>
...
</select>
balise
<html:select name="dynaFormulaire" property="listeMultiple" size="5" multiple="true">
<html:options name="dynaFormulaire" property="valeursListeMultiple"/>
</html:select>
fonction
如果字段 listeMultiple 是数组 {"multiple0","multiple2"},则生成代码 HTML
<select name="listeMultiple" 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>
balise
<html:hidden name="dynaFormulaire" property="secret"/>
fonction
如果“secret”字段的值为“xxx”,则生成代码 HTML
<input type="hidden" name="secret" value="xxx">
balise
<html:submit>提交</html:submit>
fonction
生成代码 HTML
<input type="submit" value="Envoyer">

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

<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 代码 [Effacer],让我们回顾一下在使用该网页文档的 JavaScript 代码中,网页文档的各个元素是如何被命名的:

数据
含义
document
指代整个网页文档
document.forms
指代文档中定义的表单集合
document.forms[i]
表示文档中的第 i 个表单
document.forms["nomFormulaire"]
表示表单 <form>,其 name 属性等于 "nomFormulaire"
document.nomFormulaire
指表单 <form>,其 name 属性等于 "nomFormulaire"
document.[formulaire].elements
指由表达式 [formulaire] 指定的表单的成员元素集合。该集合包含指定表单中的所有 <input>、<textarea>、<select> 标签。
document.[formulaire].elements[i]
表示 [formulaire] 集合中的第 i 个元素
document.[formulaire].elements["nomComposant"]
指[formulaire]中name属性值为nomComposant的元素
document.[formulaire]. nomComposant
指代 [formulaire] 中 name 属性等于 nomComposant 的元素
document.[formulaire].[composant].value
表示表单 [formulaire] 中组件 [composant] 的值,当该表单的代码 HTML 可能具有 value 属性时(<input>,<textarea>)
document.[formulaire].[select].selectedIndex
表示在列表中选中选项的索引。可用于读写操作。将此属性设置为 -1 将取消选中列表中的所有项目。
document.[formulaire].[select].options
表示与 <select> 标签关联的选项数组
document.[formulaire].[select].options[i]
表示指定 <select> 标签中的第 i 个选项
document.[formulaire].[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.listeSimple
表示表单中的组件 listeSimple
this.form.listeSimple.selectedIndex
表示在 listeSimple 中所选选项的索引。将此属性设置为 -1 将导致所有选项均被取消选中。

6.4.2. 视图 confirmation.jsp

需要说明的是,该视图是在 /confirmation 操作(c.a.d)执行后显示的,此时 formulaire.jsp 视图中的表单已由 Web 客户端提交。其唯一目的是显示用户输入的值。其代码如下:

<%@ 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 文档中的标签进行注释:

balise
<bean:write name="dynaFormulaire" scope="session" property="opt"/>
fonction
在当前的 HTML 流中写入一个值。bean:write 标签支持以下属性:
name:要使用的对象名称
scope:搜索该对象的作用域(request、session、context)
property:指定要写入属性的 name 所指对象的字段。该字段可以是任何类型的对象。将使用该对象的 toString 方法。
此处将写入 dynaFormulaire 对象中 opt 字段的值。如果用户勾选了 value="是" 的单选按钮,则返回“是”;如果勾选了 value="否" 的单选按钮,则返回“否”
balise
<bean:write name="dynaFormulaire" scope="session" property="chk1"/>
fonction
写入 dynaFormulairechk1 字段的值。如果用户勾选了复选框,则返回“on”;否则返回“off”。chk2chk3 同理。
balise
<bean:write name="dynaFormulaire" scope="session" property="champSaisie"/>
fonction
写入 champSaisie 字段的值,即用户在该字段中输入的文本。mdpboiteSaisie)亦同
balise
<bean:write name="dynaFormulaire" scope="session" property="combo"/>
fonction
写入下拉列表字段 dynaFormulaire 的值。这将获取用户所选 <option> 元素的 value 属性。
balise
<bean:write name="dynaFormulaire" scope="session" property="listeSimple"/>
fonction
写入字段 listeSimple 的值,该值来自 dynaFormulaire。如果用户选择了 <option> 元素,则该字段将包含该元素的 value 属性值;否则,该字段将包含空字符串。
balise
            <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>
fonction
在此,我们引入逻辑标签。我们处理的是一个多选列表。对象 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>liste multiple[<bean:write name="index"/>]</td>
          <td><bean:write name="choix"/></td>
根据前文所述,在第 i 次迭代(i>=0)中,生成的代码 HTML 等同于以下代码:
          <td>liste multiple[<%=i%>]</td>
          <td><%=listeMultiple[i]%></td>
balise
    <html:link page="/affiche.do">
            Retour au formulaire
        </html:link>    
fonction
生成一个与应用程序上下文相关的链接,从而无需了解该上下文。此标签生成的代码 HTML 如下:
<a href="/formulaire2/affiche.do">Retour au formulaire</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 的按钮 [Envoyer] 提交的表单中的值。

6.5.1. 类 DynaFormulaire

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

  • 用户未勾选的复选框
  • 下拉列表中选项数量超过1个,或未选中任何选项

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

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

因此,如果这些字段未从请求中接收值,它们将保留由 reset 赋值的值,该值对应于用户提交表单时组件的状态(复选框未勾选,列表中未选中任何项目)。

DynaFormulaire(该类继承自 DynaActionForm)的代码如下:

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){
    // 复选框重置 - 值为 off
    set("chk1","off");
    set("chk2","off");
    set("chk2","off");
    // 重置 listeSimple - 空字符串
    set("listeSimple","");
     // 重置 listeMultiple - 空数组
    set("listeMultiple",new String[]{});
  }
}

6.5.2. 类 InitFormulaireAction

在文件 struts-config.xml 中,类 InitFormulaireAction 与操作 /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 操作仅在初始化 DynaFormulaire 对象时使用一次。其目的是为下拉列表表单的三个列表(listeSimplelisteMultiple)提供内容。 该内容以三个数组的形式提供,这些数组是 dynaFormulaire 对象的属性:

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

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

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 {

     // 准备显示表单

     // 将表单所需的信息放入其 Bean 中
    DynaFormulaire formulaire = (DynaFormulaire) form;
    formulaire.set("valeursCombo", getValeurs(5, "combo"));
    formulaire.set("valeursListeSimple", getValeurs(7, "simple"));
    formulaire.set("valeursListeMultiple", getValeurs(10, "multiple"));
     // 控制权交还
    return mapping.findForward("afficherFormulaire");
  } //执行

   // 下拉列表的值列表
  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:与该操作关联的表单(如果操作配置中定义了表单,则通过操作的 name 属性获取)。
    • HttpServletRequest request:客户端的请求
    • HttpServletResponse:对客户端的响应
  • InitFormulaireAction 必须初始化表单 dynaFormulaire。该表单以参数 ActionForm form 的形式传递给 execute 方法。 需要说明的是,dynaFormulaire 属于 DynaFormulaire 类型,该类是从 DynaActionForm 类派生而来的,而 DynaActionForm 类本身又是从 ActionForm 类派生而来的。
  • execute 方法中,通过 DynaActionForm 类的 set 方法,为三个字段 valeursCombovaleursListeSimplevaleursListeMultiple 赋值。 为简化起见,这些值是任意的数组。需要注意的是,set方法仅用于为现有字段赋值,无法用于创建新字段。因此,必须在valeursCombovaleursListeSimplevaleursListeMultiple
  • execute方法结束时,会将要显示给客户端的视图键作为响应返回给控制器。在此,afficherFormulaire键在文件struts-config.xml中已被关联到视图/vues/formulaire.jsp

6.6. 部署

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

  

Image

Image

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

6.7. 结论

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