Skip to content

9. Ejemplo 07 – Las etiquetas de formulario

Vamos a crear y utilizar el siguiente formulario:

Vamos a ampliar el concepto de inyección de parámetros que vimos para un campo de entrada de texto a los demás elementos HTML de un formulario.

9.1. El proyecto de NetBeans

  • en [1], las vistas del proyecto [Formx.JSP]
  • en [2], el archivo de configuración de Struts y los mensajes internacionalizados
  • en [3], las acciones [Formx.java] y otro archivo de configuración de Struts.

9.2. Configuración de Struts

El archivo [struts.xml] es el siguiente:


<?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>
  • línea 9: incluye otro archivo de configuración, [example.xml]. Este es el que configurará las acciones.
  • Líneas 11-19: el paquete default. Define una dirección de redirección para URL/. Se redirigirá a URL/example/Form.action.

El archivo [example.xml] que configura las acciones es el siguiente:


<?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>
  • líneas 7-17: definen las acciones /example/Form, /example/Form1 y /example/Form2, donde /example es el espacio de nombres (línea 7) y Formx son las acciones.
  • líneas 8-10: al ejecutar la acción Form, se mostrará la vista /example/Form.JSP.
  • líneas 11-13: al ejecutar la acción Form1, se mostrará la vista /example/Form1.JSP.
  • líneas 14-16: la ejecución de la acción Form2 generará la visualización de la vista /example/Form2.JSP.

9.3. Los archivos de mensajes

No nos detendremos en la internacionalización del proyecto. Ya la hemos analizado en el ejemplo 01. Presentamos el contenido del archivo [messages.properties] para que el lector pueda comprender las vistas que vendrán a continuación.


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. La vista [Form.JSP] – sección de entrada

Su apariencia visual es la siguiente:

 

Es la siguiente:


<%@ 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>
  • El formulario comienza en la línea 26 y termina en la línea 38. En la línea 26, falta el atributo action. Este atributo especifica a qué acción se enviarán los datos del formulario. Al no existir el atributo action, se enviarán a la acción que provocó la visualización de la vista, en este caso la acción [Form].
  • línea 27: un campo de entrada. El atributo key especifica su texto. El atributo name es el nombre del parámetro que se enviará. La cadena enviada para este campo tendrá el siguiente formato:
textfield=texte_saisi

El nombre del parámetro corresponde a un campo de la acción [Form]. El valor del campo de entrada texte_saisi se insertará aquí en el campo textfield de la acción [Form]. Por el contrario, si se muestra la vista [Form.JSP] como resultado de la acción [Form], el valor del campo de entrada será el del campo textfield de la acción [Form]. Por lo tanto, este campo se utiliza tanto para lectura como para escritura. Para ello, la clase [Form] debe contar con los métodos getTextField y setTextField.

Este razonamiento es válido para todas las etiquetas de entrada que vendrán a continuación. No lo repetiremos.

  • línea 28: ingreso de una contraseña. La cadena enviada a la acción [Form] tendrá el formato
password=mot_de_passe_saisi

Debe existir un campo password con sus métodos get y set en la acción [Form].

  • línea 29: entrada de un texto de varias líneas en un área de 5 líneas y 40 columnas. La cadena enviada a la acción [Form] tendrá el formato
textarea=saisie

Debe existir un campo textarea con sus métodos get y set en la acción [Form].

  • Línea 30: una lista desplegable (size=1) de selección única. Solo se puede seleccionar un valor de la lista. La cadena enviada a la acción [Form] tendrá el siguiente formato:
select1=valeur_sélectionnée

Debe existir un campo select1 con sus métodos get y set en la acción [Form].

El atributo key es el título de la lista. El atributo list define la lista de valores que se incluirán en el menú desplegable. Aquí, se llamará al método getSelect1Values de la acción [Form] para llenar el menú desplegable. Este método puede devolver una colección, un diccionario o una matriz. En este caso, devolverá un array de cadenas de caracteres [chaine1, chaine2, ...] que generará el siguiente código HTML:


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

El texto entre las etiquetas <option>...</option> se mostrará en el menú desplegable. El atributo value indica el valor que se enviará si el usuario selecciona la opción. En este caso, el atributo value y el texto que se muestra en el menú desplegable son idénticos. Por lo general, este no es el caso. Si se selecciona la segunda opción, se enviará la siguiente cadena a la acción [Form]:

select1=chaine2

El campo [Form].select1 recibirá entonces el valor chaine2. Por el contrario, si al momento de mostrar el formulario este campo tiene el valor chaine3, entonces, visualmente, el tercer elemento


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

aparecerá seleccionado en el menú desplegable.

El atributo headerValue establece el texto que se mostrará como primera opción del menú desplegable y headerKey el valor que se enviará si el usuario selecciona esa opción.

  • línea 31: aquí también hay una lista de tamaño 3 (size=3): a diferencia de la lista anterior, en la que solo se ve un elemento, aquí se verán tres elementos de la lista. El modo de selección sigue siendo el mismo. Solo se puede seleccionar un elemento.
  • línea 32: nuevamente una lista, pero con selección múltiple (multiple=true). En este caso, el valor enviado a la acción [Form] tendrá el siguiente formato:
select3=chaine2&select3=chaine5

si se han seleccionado las opciones chaine2 y chaine5. Cuando se registran varios valores para un mismo parámetro, en este caso select3, la acción debe tener un campo con el nombre del parámetro y ser del tipo tableau. Por ejemplo, en este caso, la acción [Form] podría tener un campo del tipo:

String[] select3 ;

y los métodos get / set correspondientes. El array select3 recibirá entonces el array de cadenas de caracteres [chaine2,chaine5]. Por el contrario, al mostrar la vista [Form.JSP], los valores del campo [Form].select3 determinan las opciones de la lista select3 que aparecerán seleccionadas. Siempre se trata de lectura/escritura.

  • línea 33: un grupo de botones de opción. El texto del grupo lo proporciona el atributo key. Los textos de los botones individuales los proporciona el atributo list. El valor de este atributo es el nombre de un método de la acción [Form] que debe devolver una colección, un diccionario o una matriz. En este caso, el método [Form].getRadioValues devolverá una matriz de cadenas de caracteres [chaine1,chaine2,...] que generará las siguientes etiquetas HTML:

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

Si se marca la opción de radio chaine2, el valor enviado será

radio=chaine2

y este valor se insertará en el campo [Form].radio.

  • línea 34: una casilla de selección. Si está marcada, se enviará la siguiente cadena de parámetros a la acción [Form]
checkbox=true

Si no está marcada, no se envía ninguna cadena de parámetros. Struts 2 cuenta con un mecanismo interno que hace que todo suceda como si la cadena

checkbox=false

se hubiera enviado.

  • Línea 35: una lista de casillas de selección. Se pueden marcar varias. El texto del grupo lo proporciona el atributo key. Los nombres de las casillas de selección individuales se proporcionan mediante el atributo list. El valor de este atributo es el nombre de un método de la acción [Form] que debe devolver una colección, un diccionario o un array. En este caso, el método [Form].getCheckboxlistValues devolverá un array de cadenas de caracteres [chaine1,chaine2,...] que generará las siguientes etiquetas HTML:

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

Si el usuario selecciona las casillas denominadas chaine2 y chaine5, se enviará la siguiente cadena de caracteres a la acción [Form]:

checkboxlist=chaine2&checkboxlist=chaine5

La tabla [chaine2, chaine5] se asignará al campo [Form].checkboxlist, el cual, por lo tanto, debe ser de tipo tabla de cadenas de caracteres, al igual que en las listas de selección múltiple. Por el contrario, al mostrar la vista [Form.JSP], los valores del campo [Form].checkboxlist determinan las casillas de selección que estarán marcadas.

  • línea 36: un campo oculto. Un campo oculto es un campo que se envía sin que el usuario lo ingrese. Su valor lo establece el programador. En este caso, el campo oculto tomará su valor del campo [Form].hidden. Este mismo valor se enviará de la siguiente forma:
hidden=qqchose

y se reasignará al campo [Form].hidden.

  • línea 37: un botón de tipo submit. Al hacer clic en este botón, se enviarán todos los valores del formulario a la acción [Form]. La cadena de parámetros enviados tendrá el siguiente aspecto:
textfield=texte&password=&textarea=ligne1%0D%0Aligne2%0D%0A&select1=z%C3%A9ro&select2=trois&select3=z%C3%A9ro&select3=deux&__multiselect_select3=&radio=bleu&checkbox=true&__checkbox_checkbox=true&checkboxlist=v%C3%A9lo&checkboxlist=bus&__multiselect_checkboxlist=&hidden=initial&submitText=Valider 

9.5. La acción [Form] – sección de entrada

Su código es el siguiente:


package example;

import com.opensymphony.xwork2.ActionSupport;

public class Form extends ActionSupport {

  // constructor sin parámetros
  public Form() {
  }
  // campos del formulario
  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;

  // valores seleccionados en el campo select3
  public String getSelect3SelectedValues() {
    return getValue(select3);
  }

  // valores seleccionados en el campo checkboxlist
  public String getCheckboxlistSelectedValues() {
    return getValue(checkboxlist);
  }

  // método de utilidad
  public String getValue(String[] values) {
    String result = "";
    for (String value : values) {
      result += " " + value;
    }
    return result;
  }

// getters y setters de los campos
  ....
}
  • línea 1: el campo asociado a la etiqueta textfield del formulario. Se utilizará tanto para inicializar este campo de entrada como para recuperar su valor. Recibe, en el momento de la acción POST, el valor ingresado en este campo.
  • línea 2: el campo asociado a la etiqueta password del formulario. Recibe, en el momento de la llamada a POST, el valor ingresado en este campo.
  • línea 13: asociada a la etiqueta textarea del formulario. Recibe, en el momento del POST, el texto ingresado en este campo.
  • línea 14: asociada a la etiqueta select1 del formulario, una lista de selección única. Su valor inicial selecciona un elemento del menú desplegable. Se seleccionará el elemento cuyo atributo value sea igual a este valor. En el momento del POST, recibe el atributo value de la opción seleccionada en el menú desplegable.
  • línea 15: las cadenas de caracteres que alimentarán el menú desplegable select1.
  • líneas 16 y 17: lo mismo que select1
  • línea 18: asociada a la etiqueta select1 del formulario, una lista de selección múltiple. Los valores iniciales en la tabla seleccionan los elementos correspondientes de la lista. Se seleccionarán aquellos elementos cuyo atributo value sea igual a uno de estos elementos. Al momento del POST, recibe los atributos value de las diferentes opciones seleccionadas en el menú desplegable.
  • línea 19: las cadenas de caracteres que alimentarán la lista select3
  • línea 20: asociada a la etiqueta radio del formulario. Su valor sirve para marcar uno de los botones de opción, aquel cuyo atributo value sea igual a este valor. Recibe, en el momento de POST, el valor del botón de opción seleccionado.
  • línea 21: los diferentes textos y valores de los botones de opción.
  • línea 22: asociada a la etiqueta checkbox del formulario. Su valor inicial sirve para marcar o desmarcar la casilla. Al ejecutarse POST, recibe el valor true si la casilla ha sido marcada, y false en caso contrario.
  • línea 23: asociada a la etiqueta checkboxlist del formulario. Los valores iniciales de la tabla seleccionan las casillas de selección correspondientes. Se marcarán aquellas casillas cuyo atributo value sea igual a uno de estos valores iniciales. En el momento de la ejecución de POST, recibe los atributos value de las diferentes casillas marcadas.
  • línea 24: las cadenas de caracteres que alimentarán los textos de las casillas de checkboxlist.
  • línea 25: asociada al campo oculto hidden. Su valor inicial determinará el valor del campo oculto. Al momento de POST, recibirá este mismo valor, ya que el usuario no tiene la posibilidad de modificarlo.
  • línea 26: asociado a la etiqueta submit. Al ejecutar POST, recibe el texto del botón. Se puede prescindir de este campo.
  • Los métodos de las líneas 29, 34 y 39 se comentarán más adelante.

9.6. La vista [Form.JSP] – sección «Confirmación»

La vista [Form.JSP] tiene una sección de «Confirmación de los datos ingresados» que aún no se ha visto:

 

Su código es el siguiente:


<%@ 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>
  • línea 13: muestra el valor del campo [Form].textfield
  • línea 17: muestra el valor del campo [Form].password
  • línea 21: muestra el valor del campo [Form].textarea
  • línea 25: muestra el valor del campo [Form].select1
  • línea 29: muestra el valor del campo [Form].select2
  • línea 33: muestra los valores de la tabla [Form].select3. Para ello, utiliza el método [Form].getSelect3SelectedValues.
  • línea 37: muestra el valor del campo [Form].radio
  • línea 41: muestra el valor del campo [Form].checkbox
  • línea 45: muestra los valores de la tabla [Form].checkboxlist. Para ello, utiliza el método [Form].getCheckboxlistSelectedValues.
  • línea 49: muestra el valor del campo [Form].hidden

9.7. La acción [Form] – parte de confirmación

Los métodos getSelect3SelectedValues y getCheckboxlistSelectedValues mencionados anteriormente están definidos en la clase [Form]:


  // valores seleccionados en el campo select3
  public String getSelect3SelectedValues() {
    return getValue(select3);
  }

  // valores seleccionados en el campo checkboxlist
  public String getCheckboxlistSelectedValues() {
    return getValue(checkboxlist);
  }

  // método de utilidad
  public String getValue(String[] values) {
    String result = "";
    for (String value : values) {
      result += " " + value;
    }
    return result;
}

Se limitan a concatenar los elementos de las tablas que deben mostrar.

9.8. Las pruebas

Al ejecutar el proyecto en NetBeans, se obtiene la siguiente página inicial:

Los valores que se muestran en [1] y [2] provienen de los valores iniciales de los campos de la acción [Form]. Veamos algunos ejemplos:


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

El valor del campo textarea explica los valores mostrados en [1A] y [2A].


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

El valor del campo select3Values explica el contenido de la lista en [1B]. El valor del campo select3 explica los elementos seleccionados en [1B] y mostrados en [2B].

Si ingresamos otros valores en el formulario y lo validamos, los valores ingresados se registrarán en los campos correspondientes de la acción [Form]. Luego, se volverá a mostrar la vista [Form.JSP]. Por lo tanto, nos encontramos en el mismo caso que antes, salvo que los valores iniciales han sido reemplazados por los valores enviados. A continuación, un ejemplo:

9.9. La acción [Form1] y la vista [Form1.JSP]

El archivo de configuración [example.xml] configura la siguiente acción [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>

En las líneas 11 a 13, la llamada a la acción [Form1] genera la vista [Form1.JSP].

La acción [Form1] es la siguiente:


package example;

import com.opensymphony.xwork2.ActionSupport;

public class Form1 extends ActionSupport{

  // constructor sin parámetros
  public Form1() {
  }
  // campos del formulario
  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;
  }
}

La acción [Form1] es idéntica a la acción [Form], salvo que su parte de modelo se ha trasladado a una clase secundaria, la clase [Data], línea 11. La clase [Data] es la siguiente:


package example;

public class Data {

  // constructor sin parámetros
  public Data() {
  }
  
// campos del formulario
  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;

  // valores seleccionados en el campo select3
  public String getSelect3SelectedValues() {
    return getValue(select3);
  }

  // valores seleccionados en el campo checkboxlist
  public String getCheckboxlistSelectedValues() {
    return getValue(checkboxlist);
  }

  // método de utilidad
  public String getValue(String[] values) {
    String result = "";
    for (String value : values) {
      result += " " + value;
    }
    return result;
  }

  // getters y setters
....

}

Encontramos los campos declarados anteriormente en la acción [Form].

Como es lógico, la vista [Form1.JSP] es similar a la vista [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>

La diferencia entre las vistas [Form] y [Form1] puede ilustrarse con la línea 27 que se muestra a continuación:


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

El campo de entrada está vinculado, tanto en lectura como en escritura, al campo data.textfield de la acción [Form1]. Cabe recordar que, tras la ejecución, las propiedades de la acción [Form1] son accesibles desde la vista. Por lo tanto, el atributo name anterior se evaluará como [Form1].getData().getTextField(). Por lo tanto, es necesario que los dos métodos get anteriores existan.

9.10. La acción [Form2] y la vista [Form2.JSP]

El archivo de configuración [example.xml] configura la siguiente acción [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>

En las líneas 14 a 16, la llamada a la acción [Form2] genera la vista [Form2.JSP].

La acción [Form2] es la siguiente:


package example;

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

public class Form2 extends ActionSupport implements ModelDriven {

  // constructor sin parámetros
  public Form2() {
  }
  // modelo de la acción
  public Object getModel() {
    return new Data();
  }
}
  • línea 6: la acción [Form2] implementa la interfaz [ModelDriven]. Solo hay un método que implementar: el método getModel de la línea 12. Este devuelve una instancia de la clase [Data] analizada anteriormente.

El hecho de que la acción [Form2] implemente la interfaz [ModelDriven] tiene la siguiente consecuencia: la vista que se muestra tras la acción [Form2] tiene acceso directo a las propiedades del modelo de la acción [Form2], el cual es devuelto por el método getModel(). Por lo tanto, la vista [Form2.JSP] vuelve a ser lo que era con la acción inicial [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>

De este modo, la línea 27, que anteriormente era:


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

vuelve a ser:


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

La ventaja de colocar el modelo fuera de la vista es que esta arquitectura delimita con mayor claridad las funcionalidades de cada elemento de la arquitectura MVC. El modelo M se identifica claramente mediante una clase. Además, el modelo puede existir antes que las acciones. Así, si una aplicación web administra una base de datos de personas, es probable que exista la clase Personne. Si una acción ofrece un formulario de entrada para crear una nueva persona, el modelo de esa acción ya está definido: es la clase Personne.

Hemos visto lo sencillo que fue pasar de la acción [Form] a la acción [Form2]:

  • el modelo M de la vista V se encapsuló en una clase separada
  • la acción que genera la vista utilizando este modelo implementa la interfaz ModelDriven con el método getModel, que devuelve una instancia del modelo M. La acción puede verse entonces como una extensión del controlador C de Struts 2, la clase FilterDispatcher.

El lector podrá aplicar este enfoque a cualquier acción que se describa a continuación.