4. Utilização de formulários dinâmicos
4.1. Declaração do formulário dinâmico
Vimos que o Struts utiliza objetos ActionForm para armazenar os valores dos formulários HTML processados pelos diferentes servlets da aplicação. Para cada formulário da nossa aplicação, temos de criar uma classe derivada de ActionForm. Isto pode tornar-se rapidamente bastante trabalhoso, uma vez que, para cada campo xx, temos de escrever dois métodos: setXx e getXx. O Struts oferece-nos a possibilidade de utilizar formulários
- cuja estrutura é declarada no ficheiro struts-config.xml, na secção <form-beans>
- e que são criados dinamicamente pelo ambiente Struts de acordo com a estrutura declarada
Assim, a classe utilizada para armazenar os valores «nome» e «idade» da aplicação strutspersonne poderia ser definida da seguinte forma:
<form-beans>
<form-bean name="frmPersonne" type="org.apache.struts.actions.DynaActionForm">
<form-property name="nom" type="java.lang.String" initial=""/>
<form-property name="age" type="java.lang.String" initial=""/>
</form-bean>
</form-beans>
Para cada campo do formulário, define-se uma baliza <form-property> com dois atributos:
- name: o nome do campo
- type: o seu tipo Java
Como os valores de um formulário enviados por um cliente web são cadeias de caracteres, os tipos mais utilizados são java.lang.String para campos com um único valor e java.lang.String[] para campos com vários valores (caixas de seleção com o mesmo nome, listas de seleção múltipla, etc.). Tanto a classe DynactionForm como a classe ActionForm têm um método validate que não faz nada. Para que verifique a validade dos parâmetros do formulário, é necessário derivá-la e escrever o método validate por conta própria. A declaração do formulário será, portanto, a seguinte:
<form-beans>
<form-bean name="frmPersonne" type="istia.st.struts.personne.PersonneDynaForm">
<form-property name="nom" type="java.lang.String" initial=""/>
<form-property name="age" type="java.lang.String" initial=""/>
</form-bean>
</form-beans>
A classe istia.st.struts.personne.PersonneDynaForm terá de ser criada por nós.
4.2. Criação da classe DynaActionForm associada ao formulário dinâmico
Acima, associamos a classe istia.st.struts.personne.PersonneDynaForm ao formulário (nome, idade). Vamos agora escrever esta classe:
package istia.st.struts.personne;
import javax.servlet.http.*;
import org.apache.struts.action.*;
public class PersonneDynaForm extends DynaActionForm {
// validação
public ActionErrors validate(ActionMapping mapping, HttpServletRequest request) {
// gestão de erros
ActionErrors erreurs = new ActionErrors();
// o nome não pode estar vazio
String nom = (String)this.get("nom");
if (nom == null || nom.trim().equals("")) {
erreurs.add("nomvide", new ActionError("personne.formulaire.nom.vide"));
}
// a idade não pode estar em branco
String age = (String)this.get("age");
if (age == null || age.trim().equals("")) {
erreurs.add("agevide", new ActionError("personne.formulaire.age.vide"));
}
else {
// a idade deve ser um número inteiro positivo
if (!age.matches("^\\s*\\d+\\s*$")) {
erreurs.add("ageincorrect", new ActionError("personne.formulaire.age.incorrect", age));
// apresenta-se a lista de erros
}
} //if
// retorna-se a lista de erros
return erreurs;
}
}//classe
Os pontos a ter em conta são os seguintes:
- a classe deriva de DynaActionForm
- o método validate da classe DynaActionForm foi reescrito. Quando executado pelo controlador Struts, o objeto PersonneDynaForm foi criado. Este contém um dicionário cujas chaves são os campos do formulário «nome» e «idade» e cujos valores são os valores desses campos. Para aceder a um campo nos métodos de DynaActionForm, utiliza-se o método Object get(String nomDuChamp). Se se pretender atribuir um valor a um campo, pode-se utilizar o método void set(String nomDuChamp, Object valor). Consulte a definição da classe DynaActionForm para obter uma definição completa.
- Depois de recuperados os valores dos campos «nome» e «idade» do formulário, o método «validate» não difere daquele que tinha sido escrito quando o formulário estava associado a um objeto ActionForm.
4.3. A nova classe FormulaireAction
O ficheiro de configuração define a seguinte ação /main:
...
<form-beans>
<form-bean name="frmPersonne" type="istia.st.struts.personne.PersonneDynaForm">
<form-property name="nom" type="java.lang.String" initial=""/>
<form-property name="age" type="java.lang.String" initial=""/>
</form-bean>
</form-beans>
....
<action
path="/main"
name="frmPersonne"
scope="session"
validate="true"
input="/erreurs.do"
type="istia.st.struts.personne.FormulaireAction"
>
<forward name="reponse" path="/reponse.do"/>
</action>
Esta definição é idêntica à da aplicação strutspersonne. A ação /main é executada por um objeto do tipo FormulaireAction. Este objeto recebia os valores do formulário frmPersonne num objeto do tipo FormulaireBean. Agora, recebe-os num objeto do tipo PersonneDynaForm. Por isso, é necessário reescrever a classe:
package istia.st.struts.personne;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import javax.servlet.ServletException;
import istia.st.struts.personne.PersonneDynaForm;
public class FormulaireAction extends Action {
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response) throws IOException,ServletException {
// temos um formulário válido; caso contrário, não teríamos chegado até aqui
PersonneDynaForm formulaire=(PersonneDynaForm)form;
request.setAttribute("nom",formulaire.get("nom"));
request.setAttribute("age",formulaire.get("age"));
return mapping.findForward("reponse");
}//executar
}
Os pontos a ter em conta são os seguintes:
- temos de importar a classe PersonneDynaForm, que contém os dados do formulário, para ter acesso à sua definição
- o método `execute` recupera o valor dos parâmetros do formulário através do método `[DynaActionForm].get`.
No que diz respeito à classe FormulaireAction da aplicação strutspersonne, apenas a forma de aceder aos valores do formulário mudou.
4.4. Implantação e teste da aplicação strutspersonne1
4.4.1. Criação do contexto
Chamámos a esta nova aplicação «strutspersonne1». Criamos uma nova definição no ficheiro <tomcat>\conf\serveur.xml do Tomcat 4.x:
Feito isto, é necessário reiniciar o Tomcat. É possível verificar a validade do contexto acedendo ao URL:
http://localhost:8080/strutspersonne1/

4.4.2. As vistas
Vamos copiar a pasta «vues» da aplicação «strutspersonne» para a pasta da aplicação «strutspersonne1». Com efeito, as vistas não sofreram alterações.

4.4.3. Compilação das classes
Temos duas classes para criar: PersonneDynaForm e FormulaireAction, sendo que a última utiliza a primeira. Podemos criá-las e compilá-las utilizando um projeto do JBuilder:

4.4.4. A pasta WEB-INF
Vamos copiar a pasta WEB-INF da aplicação strutspersonne para a pasta da aplicação strutspersonne1. Alguns ficheiros são alterados:

O ficheiro de configuração struts-config.xml passa a ter o seguinte conteúdo:
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE struts-config PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 1.1//EN"
"http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd">
<struts-config>
<form-beans>
<form-bean name="frmPersonne" type="istia.st.struts.personne.PersonneDynaForm">
<form-property name="nom" type="java.lang.String" initial=""/>
<form-property name="age" type="java.lang.String" initial=""/>
</form-bean>
</form-beans>
<action-mappings>
<action
path="/main"
name="frmPersonne"
scope="session"
validate="true"
input="/erreurs.do"
type="istia.st.struts.personne.FormulaireAction"
>
<forward name="reponse" path="/reponse.do"/>
</action>
<action
path="/erreurs"
parameter="/vues/erreurs.personne.jsp"
type="org.apache.struts.actions.ForwardAction"
/>
<action
path="/reponse"
parameter="/vues/reponse.personne.jsp"
type="org.apache.struts.actions.ForwardAction"
/>
<action
path="/formulaire"
parameter="/vues/formulaire.personne.jsp"
type="org.apache.struts.actions.ForwardAction"
/>
</action-mappings>
<message-resources parameter="ressources.personneressources"/>
</struts-config>
Este ficheiro é idêntico ao da aplicação strutspersonne, com exceção da definição dinâmica do formulário (parte emoldurada).
Na pasta WEB-INF/classes, colocar-se-ão as classes compiladas pelo JBuilder:

Na pasta WEB-INF\classes\ressources, deve-se colocar o ficheiro de mensagens. Este não sofreu alterações.
personne.formulaire.nom.vide=<li>Vous devez indiquer un nom</li>
personne.formulaire.age.vide=<li>Vous devez indiquer un age</li>
personne.formulaire.age.incorrect=<li>L'âge [{0}] est incorrect</li>
errors.header=<ul>
errors.footer=</ul>

4.5. Testes
Estamos prontos para os testes. Seguem-se algumas capturas de ecrã que o leitor é convidado a reproduzir.
Pede-se o URLhttp://localhost:8080/strutspersonne1/formulaire.do:

Utiliza-se o botão [Envoyer] sem preencher os campos:

Repetimos o processo com um erro no campo «age»:

Obtém-se a seguinte resposta:

Vamos tentar novamente, desta vez introduzindo valores corretos:

Obtém-se a seguinte resposta:

4.6. Conclusão
A utilização de formulários dinâmicos facilita a criação de classes do tipo ActionForm, encarregadas de armazenar os valores dos formulários. É possível ir um pouco mais longe na simplificação da gestão dos formulários. Na aplicação strutspersonne1, criámos uma classe PersonneDynaForm para verificar a validade dos valores (nome, idade) do formulário. Na prática, algumas verificações repetem-se frequentemente: campo não vazio, campo que verifica uma determinada expressão regular, campo inteiro, campo de data, ... Este tipo de verificação padrão pode, então, ser definido no ficheiro de configuração struts-config.html. Se todas as verificações a realizar forem «padrão», então já não é necessário criar nenhuma classe para o formulário. É isso que vamos ver agora.