Skip to content

9. Exemplo 07 – As tags de formulário

Vamos criar e utilizar o seguinte formulário:

Vamos alargar o conceito de injeção de parâmetros, visto para um campo de introdução de texto, aos outros elementos HTML de um formulário.

9.1. O projeto NetBeans

  • em [1], as vistas do projeto [Formx.JSP]
  • em [2], o ficheiro de configuração do Struts e as mensagens internacionalizadas
  • em [3], as ações [Formx.java] e outro ficheiro de configuração do Struts.

9.2. Configuração do Struts

O ficheiro [struts.xml] é o seguinte:


<?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>
  • linha 9: inclui outro ficheiro de configuração, o [example.xml]. É este ficheiro que irá configurar as ações.
  • linhas 11-19: o pacote default. Define um endereço de redirecionamento para o URL /. Este será redirecionado para o URL /example/Form.action.

O ficheiro [example.xml] que configura as ações é o seguinte:


<?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>
  • linhas 7-17: definem as ações /example/Form, /example/Form1, /example/Form2, em que /example é o namespace (linha 7) e Formx são as ações.
  • linhas 8-10: a execução da ação Form fará com que a vista /example/Form.JSP seja apresentada.
  • linhas 11-13: a execução da ação Form1 resultará na exibição da vista /example/Form1.JSP.
  • linhas 14-16: a execução da ação Form2 resultará na exibição da vista /example/Form2.JSP.

9.3. Os ficheiros de mensagens

Não nos deteremos na internacionalização do projeto. Já a abordámos no exemplo 01. Apresentamos o conteúdo do ficheiro [messages.properties] para que o leitor possa compreender as vistas que se seguem.


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. A vista [Form.JSP] – parte de introdução de dados

A sua apresentação visual é a seguinte:

 

É a seguinte:


<%@ 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>
  • O formulário começa na linha 26 e termina na linha 38. Na linha 26, o atributo action está ausente. Este atributo especifica para que ação os dados do formulário serão enviados. Na ausência do atributo action, os dados serão enviados para a ação que levou à exibição da vista, neste caso a ação [Form].
  • linha 27: um campo de introdução de dados. O atributo key especifica o seu rótulo. O atributo name é o nome do parâmetro que será enviado. A cadeia de caracteres enviada para este campo terá o seguinte formato:
textfield=texte_saisi

O nome do parâmetro corresponde a um campo da ação [Form]. O valor do campo de introdução de dados texte_saisi será aqui inserido no campo textfield da ação [Form]. Por outro lado, se a vista [Form.JSP] for apresentada na sequência da ação [Form], o valor do campo de introdução será o do campo textfield da ação [Form]. Este campo é, portanto, utilizado tanto para leitura como para escrita. Para tal, a classe [Form] deve dispor dos métodos getTextField e setTextField.

Este raciocínio aplica-se a todas as etiquetas de introdução de dados que se seguem. Não o repetiremos.

  • linha 28: introdução de uma palavra-passe. A cadeia enviada para a ação [Form] terá o seguinte formato
password=mot_de_passe_saisi

Deve existir um campo password com os seus métodos get/set na ação [Form].

  • linha 29: introdução de um texto com várias linhas numa área de 5 linhas e 40 colunas. A cadeia enviada para a ação [Form] terá o seguinte formato
textarea=saisie

Deve existir um campo textarea com os seus métodos get/set na ação [Form].

  • linha 30: uma lista suspensa (size=1) de seleção única. Só é possível selecionar um valor da lista. A cadeia enviada para a ação [Form] terá o seguinte formato:
select1=valeur_sélectionnée

Deve existir um campo select1 com os seus métodos get/set na ação [Form].

O atributo key é o título da lista. O atributo list define a lista de valores a incluir no menu suspenso. Aqui, o método getSelect1Values da ação [Form] será chamado para preencher o menu suspenso. Este método pode devolver uma coleção, um dicionário ou um array. Neste caso, irá devolver uma matriz de cadeias de caracteres [chaine1, chaine2, ...] que irá gerar o seguinte código HTML:


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

O texto entre as balizas <option>...</option> será apresentado na lista suspensa. O atributo value indica o valor a enviar caso a opção seja selecionada pelo utilizador. Aqui, o atributo value e o texto apresentado na lista suspensa são idênticos. Na maioria das vezes, não é esse o caso. Se a segunda opção for selecionada, a seguinte cadeia de caracteres será enviada para a ação [Form]:

select1=chaine2

O campo [Form].select1 receberá então o valor chaine2. Por outro lado, se, no momento da exibição do formulário, este campo tiver o valor chaine3, então, visualmente, o terceiro elemento


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

aparecerá selecionado na lista suspensa.

O atributo headerValue define o texto a apresentar como primeira opção da lista suspensa e headerKey o valor a enviar caso essa opção seja selecionada pelo utilizador.

  • linha 31: mais uma vez, uma lista de tamanho 3 (size=3): ao contrário da lista anterior, em que apenas um elemento é visível, aqui serão visíveis 3 elementos da lista. O modo de seleção mantém-se o mesmo. Só é possível selecionar um elemento.
  • linha 32: mais uma vez uma lista, mas com seleção múltipla (multiple=true). Neste caso, o valor enviado para a ação [Form] terá o seguinte formato:
select3=chaine2&select3=chaine5

se as opções chaine2 e chaine5 tiverem sido selecionadas. Quando são lançados vários valores para um mesmo parâmetro, neste caso select3, a ação deve ter um campo com o nome do parâmetro e ser do tipo tableau. Assim, por exemplo, neste caso, a ação [Form] poderia ter um campo do tipo:

String[] select3 ;

e os métodos get / set correspondentes. A matriz select3 receberá então a matriz de cadeias de caracteres [chaine2,chaine5]. Por outro lado, ao apresentar a vista [Form.JSP], os valores do campo [Form].select3 determinam as opções da lista select3 que aparecerão selecionadas. Estamos sempre em modo de leitura/gravação.

  • linha 33: um grupo de botões de opção. O rótulo do grupo é fornecido pelo atributo key. Os rótulos dos botões individuais são fornecidos pelo atributo list. O valor deste atributo é o nome de um método da ação [Form] que deve devolver uma coleção, um dicionário ou um array. Neste caso, o método [Form].getRadioValues irá devolver uma matriz de cadeias de caracteres [chaine1,chaine2,...], que irá gerar as seguintes etiquetas HTML:

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

Se o botão de opção chaine2 estiver selecionado, o valor enviado será

radio=chaine2

e esse valor será inserido no campo [Form].radio.

  • linha 34: uma caixa de seleção. Se estiver marcada, a seguinte cadeia de parâmetros será enviada para a ação [Form]
checkbox=true

Se não estiver marcada, não é enviada nenhuma cadeia de parâmetros. O Struts 2 possui um mecanismo interno que faz com que tudo aconteça como se a cadeia

checkbox=false

tivesse sido enviada.

  • linha 35: uma lista de caixas de seleção. É possível selecionar várias. O rótulo do grupo é fornecido pelo atributo key. Os rótulos das caixas de seleção individuais são fornecidos pelo atributo list. O valor deste atributo é o nome de um método da ação [Form], que deve devolver uma coleção, um dicionário ou um array. Neste caso, o método [Form].getCheckboxlistValues irá devolver uma matriz de cadeias de caracteres [chaine1,chaine2,...], que irá gerar as seguintes etiquetas HTML:

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

Se o utilizador selecionar as caixas de seleção denominadas chaine2 e chaine5, a seguinte cadeia de caracteres será enviada para a ação [Form]:

checkboxlist=chaine2&checkboxlist=chaine5

A tabela [chaine2, chaine5] será atribuída ao campo [Form].checkboxlist, que deve, portanto, ser do tipo tabela de cadeias de caracteres, tal como nas listas de seleção múltipla. Por outro lado, na visualização da vista [Form.JSP], os valores do campo [Form].checkboxlist determinam as caixas de seleção que ficarão marcadas.

  • linha 36: um campo oculto. Um campo oculto é um campo que é enviado sem ser preenchido pelo utilizador. O seu valor é definido pelo programador. Aqui, o campo oculto assumirá o valor do campo [Form].hidden. Esse mesmo valor será enviado na forma:
hidden=qqchose

e reatribuído ao campo [Form].hidden.

  • linha 37: um botão do tipo submit. Ao clicar neste botão, todos os valores do formulário serão enviados para a ação [Form]. A cadeia de parâmetros enviados terá o seguinte aspeto:
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. A ação [Form] – parte de preenchimento

O seu código é o seguinte:


package example;

import com.opensymphony.xwork2.ActionSupport;

public class Form extends ActionSupport {

  // construtor sem parâmetros
  public Form() {
  }
  // campos do formulário
  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 selecionados no campo select3
  public String getSelect3SelectedValues() {
    return getValue(select3);
  }

  // valores selecionados no campo checkboxlist
  public String getCheckboxlistSelectedValues() {
    return getValue(checkboxlist);
  }

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

// getters e setters dos campos
  ....
}
  • linha 1: o campo associado à baliza textfield do formulário. Servirá para inicializar este campo de introdução de dados, bem como para recuperar o seu valor. Recebe, no momento da ação POST, o valor introduzido neste campo.
  • linha 2: o campo associado à baliza password do formulário. Recebe, no momento do POST, o valor introduzido neste campo.
  • linha 13: associada à etiqueta textarea do formulário. Recebe, no momento do POST, o texto introduzido neste campo.
  • linha 14: associada à baliza select1 do formulário, uma lista de escolha única. O seu valor inicial seleciona um elemento da lista suspensa. Será selecionado o elemento cujo atributo value seja igual a esse valor. No momento do POST, recebe o atributo value da opção selecionada no menu suspenso.
  • linha 15: as cadeias de caracteres que irão alimentar a lista suspensa select1.
  • linhas 16 e 17: o mesmo que select1
  • linha 18: associada à baliza select1 do formulário, uma lista de seleção múltipla. Os valores iniciais na tabela selecionam os elementos correspondentes da lista. Serão selecionados os elementos cujo atributo value seja igual a um desses elementos. No momento do POST, recebe os atributos value das diferentes opções selecionadas na lista suspensa.
  • linha 19: as cadeias de caracteres que irão alimentar a lista select3
  • linha 20: associada à baliza radio do formulário. O seu valor serve para selecionar um dos botões de opção, aquele cujo atributo value é igual a este valor. Recebe, no momento do POST, o valor do botão de opção selecionado.
  • linha 21: os diferentes rótulos e valores dos botões de opção.
  • linha 22: associada à baliza checkbox do formulário. O seu valor inicial serve para marcar/desmarcar a caixa de seleção. Recebe, no momento do POST, o valor true se a caixa tiver sido marcada, false caso contrário.
  • linha 23: associada à baliza checkboxlist do formulário. Os valores iniciais na tabela selecionam as caixas de seleção correspondentes. Serão marcadas as caixas de seleção cujo atributo value for igual a um desses valores iniciais. No momento do POST, recebe os atributos value das diferentes caixas de seleção marcadas.
  • linha 24: as cadeias de caracteres que irão alimentar os rótulos das caixas de seleção de checkboxlist.
  • linha 25: associada ao campo oculto hidden. O seu valor inicial irá definir o valor do campo oculto. No momento do POST, receberá esse mesmo valor, uma vez que o utilizador não tem a possibilidade de o alterar.
  • linha 26: associado à baliza submit. No momento do POST, recebe o texto do botão. É possível prescindir deste campo.
  • Os métodos das linhas 29, 34 e 39 serão comentados mais adiante.

9.6. A vista [Form.JSP] – parte «Confirmação»

A vista [Form.JSP] tem uma parte intitulada «Confirmação das entradas» que ainda não foi abordada:

 

O seu código é o seguinte:


<%@ 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>
  • linha 13: apresenta o valor do campo [Form].textfield
  • linha 17: apresenta o valor do campo [Form].password
  • linha 21: apresenta o valor do campo [Form].textarea
  • linha 25: apresenta o valor do campo [Form].select1
  • linha 29: apresenta o valor do campo [Form].select2
  • linha 33: apresenta os valores da tabela [Form].select3. Para tal, utiliza o método [Form].getSelect3SelectedValues.
  • linha 37: apresenta o valor do campo [Form].radio
  • linha 41: apresenta o valor do campo [Form].checkbox
  • linha 45: apresenta os valores da tabela [Form].checkboxlist. Para tal, utiliza o método [Form].getCheckboxlistSelectedValues.
  • linha 49: apresenta o valor do campo [Form].hidden

9.7. A ação [Form] – parte de confirmação

Os métodos getSelect3SelectedValues e getCheckboxlistSelectedValues, mencionados anteriormente, estão definidos na classe [Form]:


  // valores selecionados no campo select3
  public String getSelect3SelectedValues() {
    return getValue(select3);
  }

  // valores selecionados no campo checkboxlist
  public String getCheckboxlistSelectedValues() {
    return getValue(checkboxlist);
  }

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

Limitam-se a concatenar os elementos das tabelas que têm de apresentar.

9.8. Os testes

Ao executar o projeto no NetBeans, obtém-se a seguinte página inicial:

Os valores apresentados em [1] e [2] provêm dos valores iniciais dos campos da ação [Form]. Vejamos alguns exemplos:


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

O valor do campo textarea explica as exibições [1A] e [2A].


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

O valor do campo select3Values explica o conteúdo da lista em [1B]. O valor do campo select3 explica os elementos selecionados em [1B] e apresentados em [2B].

Se introduzirmos outros valores no formulário e o validarmos, os valores introduzidos serão lançados nos campos correspondentes da ação [Form]. Em seguida, a vista [Form.JSP] será novamente apresentada. Estamos, portanto, na mesma situação que anteriormente, exceto que os valores iniciais foram substituídos pelos valores lançados. Eis um exemplo:

9.9. A ação [Form1] e a vista [Form1.JSP]

O ficheiro de configuração [example.xml] configura a seguinte ação [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>

Nas linhas 11-13, a chamada à ação [Form1] gera a vista [Form1.JSP].

A ação [Form1] é a seguinte:


package example;

import com.opensymphony.xwork2.ActionSupport;

public class Form1 extends ActionSupport{

  // construtor sem parâmetros
  public Form1() {
  }
  // campos do formulário
  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;
  }
}

A ação [Form1] é idêntica à ação [Form], com a única diferença de que a sua parte de modelo foi transferida para uma classe anexa, a classe [Data], linha 11. A classe [Data] é a seguinte:


package example;

public class Data {

  // construtor sem parâmetros
  public Data() {
  }
  
// campos do formulário
  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 selecionados no campo select3
  public String getSelect3SelectedValues() {
    return getValue(select3);
  }

  // valores selecionados no campo checkboxlist
  public String getCheckboxlistSelectedValues() {
    return getValue(checkboxlist);
  }

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

  // getters e setters
....

}

Encontramos aqui os campos declarados anteriormente na ação [Form].

Como seria de esperar, a vista [Form1.JSP] é semelhante à 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>

A diferença entre as vistas [Form] e [Form1] pode ser ilustrada pela linha 27 reproduzida abaixo:


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

O campo de introdução de dados está ligado, em leitura e escrita, ao campo data.textfield da ação [Form1]. Recorde-se que, após a execução, as propriedades da ação [Form1] ficam acessíveis à vista. Assim, o atributo name anterior será avaliado como [Form1].getData().getTextField(). Por conseguinte, é necessário que os dois métodos get anteriores existam.

9.10. A ação [Form2] e a vista [Form2.JSP]

O ficheiro de configuração [example.xml] configura a seguinte ação [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>

Nas linhas 14-16, a chamada à ação [Form2] gera a vista [Form2.JSP].

A ação [Form2] é a seguinte:


package example;

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

public class Form2 extends ActionSupport implements ModelDriven {

  // construtor sem parâmetros
  public Form2() {
  }
  // modelo da ação
  public Object getModel() {
    return new Data();
  }
}
  • linha 6: a ação [Form2] implementa a interface [ModelDriven]. Há apenas um método a implementar, o método getModel da linha 12. Este devolve uma instância da classe [Data] analisada anteriormente.

O facto de a ação [Form2] implementar a interface [ModelDriven] tem a seguinte consequência: a vista exibida na sequência da ação [Form2] tem acesso direto às propriedades do modelo da ação [Form2], sendo este devolvido pelo método getModel(). Assim, a vista [Form2.JSP] volta a ser o que era com a ação 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>

Assim, a linha 27, que anteriormente era:


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

volta a ser:


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

A vantagem de colocar o modelo fora da vista é que esta arquitetura delimita mais claramente as funcionalidades de cada elemento da arquitetura MVC. O modelo M é claramente identificado por uma classe. Além disso, o modelo pode existir antes das ações. Assim, se uma aplicação web gerir uma base de dados de pessoas, é provável que a classe Personne já exista. Se uma ação apresentar um formulário de preenchimento para criar uma nova pessoa, o modelo dessa ação é óbvio: é a classe Personne.

Vimos como foi simples passar da ação [Form] para a ação [Form2]:

  • o modelo M da vista V foi encapsulado numa classe separada
  • a ação que gera a vista utilizando este modelo implementa a interface ModelDriven com o método getModel, que devolve uma instância do modelo M. A ação pode, então, ser vista como uma extensão do controlador C do Struts 2, a classe FilterDispatcher.

O leitor poderá aplicar esta abordagem a qualquer ação descrita a seguir.