Skip to content

9. Exemplo 07 – Etiquetas de formulário

Vamos criar e utilizar o seguinte formulário:

Image

Image

Vamos alargar o conceito de injeção de parâmetros visto para um campo de entrada de texto a outros elementos HTML num 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 [example.xml]. Este ficheiro configura as ações.
  • Linhas 11–19: o pacote padrão. Define um endereço de redirecionamento para a URL /. Será redirecionado para a 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, onde /example é o namespace (linha 7) e Formx são as ações.
  • Linhas 8-10: a execução da ação Form exibirá a vista /example/Form.jsp.
  • Linhas 11–13: a execução da ação Form1 exibirá a vista /example/Form1.jsp.
  • Linhas 14–16: A execução da ação Form2 exibirá a vista /example/Form2.jsp.

9.3. Ficheiros de mensagens

Não nos deteremos na internacionalização do projeto. Já abordámos este tema 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] – secção de entrada

Tem o seguinte aspeto:

Image

É o 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, falta o atributo `action`. Este atributo especifica a ação para a qual os dados do formulário serão enviados. Se o atributo `action` estiver em falta, os dados serão enviados para a ação que fez com que a vista fosse exibida, neste caso a ação `[Form]`.
  • Linha 27: um campo de entrada. 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=text_entered

O nome do parâmetro corresponde a um campo na ação [Form]. O valor do campo de entrada text_saisi será inserido no campo textfield da ação [Form]. Por outro lado, se a vista [Form.jsp] for exibida após a ação [Form], o valor do campo de entrada será o do campo textfield na ação [Form]. Este campo é, portanto, utilizado tanto para leitura como para escrita. A classe [Form] deve possuir os métodos getTextField e setTextField para este fim.

Este raciocínio aplica-se a todas as tags input subsequentes. Não o repetiremos.

  • Linha 28: introdução da palavra-passe. A cadeia de caracteres enviada para a ação [Form] terá o formato

password=entered_password

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

  • Linha 29: Introdução de texto multilinha numa área de 5 linhas e 40 colunas. A string enviada para a ação [Form] terá o formato

textarea=input

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

  • linha 30: uma lista suspensa de seleção única (size=1). Apenas um valor da lista pode ser selecionado. A string enviada para a ação [Form] terá o seguinte formato:

select1=valor_selecionado

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

O atributo key é o rótulo da lista. O atributo list define a lista de valores a colocar na caixa combinada. Aqui, o método getSelect1Values da ação [Form] será chamado para preencher a caixa combinada. Este método pode devolver uma coleção, um dicionário ou uma matriz. Aqui, devolverá uma matriz de cadeias de caracteres [string1, string2, ...] que irá gerar o seguinte código HTML:

<option value="string1">string1</option>

<option value="string2">string2</option>

...

O texto entre as tags <option>...</option> será exibido na caixa de combinação. O atributo value especifica o valor a ser enviado se a opção for selecionada pelo utilizador. Aqui, o atributo value e o texto exibido na caixa de combinação são idênticos. Na maioria das vezes, este não é o caso. Se a segunda opção for selecionada, a seguinte string será enviada para a ação [Form]:

select1=string2

O campo [Form].select1 receberá então o valor string2. Por outro lado, se, no momento em que o formulário é exibido, este campo estiver definido como string3, então, visualmente, o terceiro elemento

<option value="string3">string3</option>

aparecerá selecionado na caixa combinada.

O atributo headerValue define o texto a ser exibido como a primeira opção na caixa de combinação, e headerKey define o valor a ser enviado se esta opção for selecionada pelo utilizador.

  • Linha 31: mais uma vez, uma lista de tamanho 3 (size=3): ao contrário da lista anterior, onde apenas um item é visível, aqui serão visíveis 3 itens da lista. O modo de seleção permanece o mesmo. Apenas um item pode ser selecionado.
  • Linha 32: outra lista, mas desta vez com seleção múltipla (multiple=true). Neste caso, o valor enviado para a ação [Form] terá o seguinte formato:

select3=string2&select3=string5

se as opções string2 e string5 tiverem sido selecionadas. Quando são enviados vários valores para o mesmo parâmetro — neste caso, select3 — a ação deve ter um campo com o nome do parâmetro e ser do tipo matriz. Assim, por exemplo, aqui a ação [Form] poderia ter um campo como:

String[] select3;

e os métodos get/set correspondentes. A matriz select3 receberá então a matriz de cadeias de caracteres [string2, string5]. Por outro lado, quando a vista [Form.jsp] é apresentada, os valores do campo [Form].select3 determinam quais as opções da lista select3 que aparecerão selecionadas. Esta é sempre uma operação 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 na ação [Form] que deve devolver uma coleção, um dicionário ou uma matriz. Aqui, o método [Form].getRadioValues devolverá uma matriz de cadeias de caracteres [string1, string2, ...] que irá gerar as seguintes tags HTML:

<input type="radio" name="radio" ... value="string1"/>string1

<input type="radio" name="radio" ... value="string2"/>string2

...

Se o botão de opção string2 for selecionado, o valor enviado será

radio=string2

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

  • Linha 34: uma caixa de seleção. Se 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 caixa de seleção

checkbox=false

tivesse sido enviada.

  • Linha 35: uma lista de caixas de seleção. É possível selecionar várias caixas de seleção. 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 na ação [Form] que deve devolver uma coleção, um dicionário ou uma matriz. Aqui, o método [Form].getCheckboxlistValues devolverá uma matriz de cadeias de caracteres [string1, string2, ...] que irá gerar as seguintes tags HTML:

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

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

...

Se o utilizador selecionar as caixas de seleção rotuladas como string2 e string5, a seguinte string será enviada para a ação [Form]:

checkboxlist=string2&checkboxlist=string5

A matriz [string2, string5] será atribuída ao campo [Form].checkboxlist, que deve, portanto, ser do tipo matriz de cadeias de caracteres, tal como acontece com as listas de seleção múltipla. Por outro lado, quando a vista [Form.jsp] é apresentada, os valores do campo [Form].checkboxlist determinam quais as caixas de seleção que serã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 irá obter o seu valor do campo [Form].hidden. Este mesmo valor será enviado no formulário:

hidden=something

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

  • Linha 37: um botão de envio. Clicar neste botão enviará todos os valores do formulário para a ação [Form]. A sequência de parâmetros enviados terá o seguinte aspeto:

textfield=text&password=&textarea=line1%0D%0Aline2%0D%0A&select1=zero&select2=three&select3=zero&select3=two&__multiselect_select3=&radio=blue&checkbox=true&__checkbox_checkbox=true&checkboxlist=v%C3%A9lo&checkboxlist=bus&__multiselect_checkboxlist=&hidden=initial&submitText=Validate

9.5. A ação [Form] – secção de entrada

O seu código é o seguinte:


package example;
 
import com.opensymphony.xwork2.ActionSupport;
 
public class Form extends ActionSupport {
 
  // constructor without parameters
  public Form() {
  }
  // form fields
  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;
 
  // values selected in select3 field
  public String getSelect3SelectedValues() {
    return getValue(select3);
  }
 
  // values selected in checkboxlist field
  public String getCheckboxlistSelectedValues() {
    return getValue(checkboxlist);
  }
 
  // utilitarian method
  public String getValue(String[] values) {
    String result = "";
    for (String value : values) {
      result += " " + value;
    }
    return result;
  }
 
// field getters and setters
  ....
}

  • linha 1: o campo associado à tag textfield do formulário. Utilizado para inicializar este campo de entrada, bem como para recuperar o seu valor. Recebe o valor introduzido neste campo no momento da solicitação POST.
  • linha 2: o campo associado à tag password do formulário. Recebe o valor introduzido neste campo durante a solicitação POST.
  • linha 13: associada à tag textarea do formulário. Recebe o texto inserido neste campo durante a solicitação POST.
  • Linha 14: Associada à tag `select1` do formulário, uma lista de seleção única. O seu valor inicial seleciona um item da lista suspensa. O item com um atributo `value` que corresponda a este valor será selecionado. Após o POST, recebe o atributo `value` da opção selecionada na lista suspensa.
  • Linha 15: As cadeias de caracteres que irão preencher o menu suspenso select1.
  • Linhas 16 e 17: Igual a select1
  • Linha 18: associada à tag `select1` do formulário, uma lista de seleção múltipla. Os valores iniciais na matriz selecionam os elementos correspondentes na lista. Os elementos cujo atributo `value` corresponda a um desses valores serão selecionados. Após o POST, recebe os atributos `value` das várias opções selecionadas no menu suspenso.
  • Linha 19: as cadeias de caracteres que irão preencher a lista select3
  • Linha 20: Associada à tag radio do formulário. O seu valor é utilizado para selecionar um dos botões de opção — aquele cujo atributo value corresponda a este valor. Recebe o valor do botão de opção selecionado durante a solicitação POST.
  • Linha 21: Os vários rótulos e valores dos botões de opção.
  • Linha 22: Associada à tag checkbox do formulário. O seu valor inicial é utilizado para marcar ou desmarcar a caixa. Após o POST, recebe o valor true se a caixa estiver marcada, false caso contrário.
  • Linha 23: Associada à tag checkboxlist do formulário. Os valores iniciais na matriz selecionam as caixas de seleção correspondentes. As caixas de seleção cujo atributo value corresponda a um desses valores iniciais serão marcadas. Após o POST, recebe os atributos value das várias caixas de seleção marcadas.
  • Linha 24: As cadeias de caracteres que irão preencher os rótulos das caixas de seleção da checkboxlist.
  • Linha 25: associada ao campo oculto. O seu valor inicial define o valor do campo oculto. Após o POST, receberá este mesmo valor, uma vez que o utilizador não o pode modificar.
  • Linha 26: Associado à tag submit. Durante a solicitação POST, recebe o rótulo do botão. Este campo é opcional.
  • Os métodos nas linhas 29, 34 e 39 serão discutidos mais adiante.

9.6. A vista [Form.jsp] – Secção de confirmação

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

Image

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: exibe o valor do campo [Form].select2
  • Linha 33: Exibe os valores da matriz [Form].select3. Para tal, utiliza o método [Form].getSelect3SelectedValues.
  • linha 37: exibe o valor do campo [Form].radio
  • linha 41: exibe o valor do campo [Form].checkbox
  • Linha 45: exibe os valores da matriz [Form].checkboxlist. Para isso, utiliza o método [Form].getCheckboxlistSelectedValues.
  • Linha 49: exibe o valor do campo [Form].hidden

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

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


  // valeurs sélectionnées dans champ select3
  public String getSelect3SelectedValues() {
    return getValue(select3);
  }
 
  // valeurs sélectionnées dans champ checkboxlist
  public String getCheckboxlistSelectedValues() {
    return getValue(checkboxlist);
  }
 
  // méthode utilitaire
  public String getValue(String[] values) {
    String result = "";
    for (String value : values) {
      result += " " + value;
    }
    return result;
}

Eles simplesmente concatenam os elementos das matrizes que precisam de exibir.

9.8. Testes

Quando o projeto NetBeans é executado, é exibida a seguinte página inicial:

Os valores apresentados em [1] e [2] provêm dos valores iniciais dos campos na 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 itens selecionados em [1B] e exibidos em [2B].

Se introduzirmos outros valores no formulário e o enviarmos, os valores introduzidos serão enviados para os campos correspondentes da ação [Form]. Em seguida, a vista [Form.jsp] será novamente apresentada. Estamos, portanto, na mesma situação que antes, exceto que os valores iniciais foram substituídos pelos valores enviados. Aqui está um exemplo:

Image

Image

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>

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{
 
  // constructor without parameters
  public Form1() {
  }
  // form fields
  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], exceto que a sua parte de modelo foi movida para uma classe separada, a classe [Data], na linha 11. A classe [Data] é a seguinte:


package example;
 
public class Data {
 
  // constructor without parameters
  public Data() {
  }
  
// form fields
  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;
 
  // values selected in select3 field
  public String getSelect3SelectedValues() {
    return getValue(select3);
  }
 
  // values selected in checkboxlist field
  public String getCheckboxlistSelectedValues() {
    return getValue(checkboxlist);
  }
 
  // utilitarian method
  public String getValue(String[] values) {
    String result = "";
    for (String value : values) {
      result += " " + value;
    }
    return result;
  }
 
  // getters and setters
....
 
}

Encontramos os campos previamente declarados 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 apresentada abaixo:


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

O campo de entrada está vinculado, tanto para leitura como para 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, os dois métodos get anteriores têm de existir.

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>

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 {
 
  // constructor without parameters
  public Form2() {
  }
  // action model
  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 na linha 12. Este método devolve uma instância da classe [Data] discutida anteriormente.

O facto de a ação [Form2] implementar a interface [ModelDriven] tem a seguinte consequência: a vista apresentada após a ação [Form2] tem acesso direto às propriedades do modelo da ação [Form2], que é devolvido pelo método getModel(). Assim, a vista [Form2.jsp] volta a ser o que era com a ação [Form] inicial:


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

Portanto, a linha 27, que anteriormente era:


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

passa a ser:


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

A vantagem de colocar o modelo fora da vista é que esta arquitetura delineia 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 Pessoa exista. Se uma ação fornecer um formulário de entrada para criar uma nova pessoa, o modelo para esta ação é óbvio: é a classe Pessoa.

Vimos como foi simples mudar 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 pode aplicar esta abordagem a qualquer ação descrita abaixo.