Struts Características Avançadas Agenda • • • • • • • • Tags do Struts DispatchAction Multiplos arquivos de configuração Command Pattern DynaActionForm Array’s Validation Framework JSP2.0 Expression Language html • • • • • • • • • • • html:submit html:cancel html:button html:hidden html:checkbox html:messages html:errors html:file html:form html:javascript html:image/img html • • • • • • • • • html:link html:messages html:multibox html:selection/option/options/optionsCollection html:radio html:reset html:rewrite html:text html:textarea bean • • • • • • • • • bean:cookie bean:header bean:parameter bean:define bean:include bean:message bean:page bean:resource bean:size bean • bean:struts • bean:write logic • • • • logic:empty logic:present logic:iterate logic:{...} Preparação do Ambiente • Crie dois projetos (SistemaDeNoticias e SistemaDeNoticiasWeb) • Configure o nome do contexto web para noticia • Adicione a lib struts-extras.jar. Utilize, claro, a versão mais recente • Crie as páginas Menu.jsp e index.jsp Menu.jsp <%@ page language="java" contentType="text/html; charset=UTF-8"%> <%@ taglib uri="/tags/struts-html" prefix="html"%> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>.: Menu :.</title> <link rel="stylesheet" href="css/Estilo.css" type="text/css" /> </head> <body> <table> <tr> <td><html:link page="/PaginaInicial.do">Página Inicial | </html:link></td> </tr> </table> </body> </html> index.jsp <body onLoad="window.location='PaginaInicial.do';"> DispatchAction • Subtipo de Action que implementa o padrão Command • Ao invés de ter várias classes Action, você centraliza todas as ações num único DispatchAction e seleciona a uma ação específica através de um parâmetro (comando) na url • Muito prático, economiza várias classes, pois uma mesma Action responde a várias solicitações NoticiaAction package net.noticias.action; import java.util.*; import javax.servlet.http.*; import net.noticias.form.*; import net.noticias.persistencia.*; import org.apache.struts.action.*; import org.apache.struts.actions.*; public class NoticiaAction extends DispatchAction { public ActionForward listar(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) { DAONoticia daoNoticia = new DAONoticia(); request.setAttribute("noticias", daoNoticia.consultaNoticias()); return mapping.findForward("MostrarPaginaDeConsulta"); } } Invocando um método do Dispatch • Essa é uma subclasse de DispatchAction • Todos os métodos devem ter a seguinte lista de parâmetros: – ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response • O nome do método (no nosso caso listar) será invocado pela url “Noticia.do?comando=listar” • A url completa é http://localhost:8080/noticia/Noticia.do?comando=listar Parâmetros do dispath • Noticia.do – Chama a ação respectiva no strutsconfig.xml, acionando a classe NoticiaAction – O método listar faz carrega uma coleção de notícias na variável noticias e chama o forward MostrarPaginaDeConsulta abrindo a página ConsultarNoticias.jsp • comando=listar – Parâmetro http utilizado para escolher o método NoticiaForm package net.noticias.form; import org.apache.struts.action.*; public class NoticiaForm extends ActionForm { private String id; private String texto; private String titulo; private String data; private String tipoDeNoticia; private String[] noticiasSelecionadas; {...} } Detalhes • Crie os métodos getter e setters • Temos um atributo do tipo array • Array’s são utilizados para recuperar valores de componentes como listas de seleção múltipla e checkboxes • Essa é uma característica do html, não do Struts DAONoticia package net.noticias.persistencia; import java.util.*; import net.noticias.form.*; public class DAONoticia { public Collection<NoticiaForm> consultaNoticias() { Collection<NoticiaForm> lista = new ArrayList<NoticiaForm>(); NoticiaForm n = new NoticiaForm(); n.setId("1"); n.setData("01-01-2001"); n.setTexto("Texto da notícia 1"); n.setTitulo("Título da notícia 1"); lista.add(n); n = new NoticiaForm(); n.setId("2"); n.setData("01-01-2002"); n.setTexto("Texto da notícia 2"); n.setTitulo("Título da notícia 2"); lista.add(n); n = new NoticiaForm(); n.setId("3"); n.setData("01-01-2003"); n.setTexto("Texto da notícia 3"); n.setTitulo("Título da notícia 3"); lista.add(n); return lista; } DAONoticia public NoticiaForm consultaNoticiaPeloId(String id) { NoticiaForm n = new NoticiaForm(); n.setId(id); n.setData("01-01-2001"); n.setTexto("Texto da notícia " + id); n.setTitulo("Título da notícia " + id); n.setTipoDeNoticia("3"); return n; } } web.xml <?xml version="1.0" encoding="iso-8859-1"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd"> <web-app> <display-name>Sistema de Notícias</display-name> <servlet> <servlet-name>action</servlet-name> <servlet-class> org.apache.struts.action.ActionServlet </servlet-class> <init-param> <param-name>config</param-name> <param-value> /WEB-INF/struts-config.xml,/WEB-INF/struts-config-form-beans.xml </param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> web.xml <!-- Standard Action Servlet Mapping --> <servlet-mapping> <servlet-name>action</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping> <taglib> <taglib-uri>/tags/struts-bean</taglib-uri> <taglib-location>/WEB-INF/lib/struts-bean.tld</taglib-location> </taglib> <taglib> <taglib-uri>/tags/struts-html</taglib-uri> <taglib-location>/WEB-INF/lib/struts-html.tld</taglib-location> </taglib> <taglib> <taglib-uri>/tags/struts-logic</taglib-uri> <taglib-location>/WEB-INF/lib/struts-logic.tld</taglib-location> </taglib> </web-app> Múltiplos arquivos de configuração • Arquivos de configuração tendem a crescer bastante, dificultando sua visualização • Nesse exemplo separamos os form-beans do resto da aplicação • Para isso, adicione os arquivos de configuração no parâmetro config separados por vírgula struts-config.xml <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.3//EN" "http://struts.apache.org/dtds/struts-config_1_3.dtd"> <struts-config> <global-forwards> <forward name="AbrirPaginaPrincipal" path="/PaginaInicial.do" /> </global-forwards> <action-mappings> <action path="/PaginaInicial" forward="/Menu.jsp" /> <action path="/Noticia" type="net.noticias.action.NoticiaAction" name="noticiaForm" parameter="comando"> <forward name="MostrarPaginaDeEdicao" path="/EditarNoticia.jsp" /> <forward name="MostrarPaginaDeConsulta" path="/ConsultarNoticias.jsp" /> </action> </action-mappings> <message-resources parameter="MessageResources" /> </struts-config> Command Pattern • A ação Noticia deve ter um parâmetro • O nome padrão é comando • Esse parâmetro será usado para escolher o método a ser executado Forwards globais • São forwards que podem ser utilizados por todos os actions <global-forwards> <forward name="AbrirPaginaPrincipal" path="/PaginaInicial.do" /> </global-forwards> struts-config-form-beans.xml <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.3//EN" "http://struts.apache.org/dtds/struts-config_1_3.dtd"> <struts-config> <form-beans> <form-bean name="noticiaForm" type="net.noticias.form.NoticiaForm" /> <form-bean name="tipoDeNoticiaForm" type="net.noticias.form.TipoDeNoticiaForm" /> </form-beans> </struts-config> ConsultarNoticias.jsp <%@ page language="java" contentType="text/html; charset=UTF-8"%> <%@ taglib uri="/tags/struts-html" prefix="html"%> <%@ taglib uri="/tags/struts-bean" prefix="bean"%> <%@ taglib uri="/tags/struts-logic" prefix="logic"%> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>.: Consulta Notícias :.</title> <link rel="stylesheet" href="css/Estilo.css" type="text/css" /> </head> <body> <table width=90% border> <tr> <td colspan=4 align=center> <h2>Consulta Notícias</h2> </td> </tr> <tr> <td align=center>Data</td> <td>Título</td> <td>Texto</td> <td>Editar</td> ConsultarNoticias.jsp </tr> <logic:iterate id="noticia" name="noticias"> <tr> <td align=center><bean:write name="noticia" property="data" /></td> <td><bean:write name="noticia" property="titulo" /></td> <td><bean:write name="noticia" property="texto" /></td> <td><html:link page="/Noticia.do?comando=editar" paramId="idDaNoticia" paramName="noticia" paramProperty="id"> <html:image src="imagens/edit.gif" /> </html:link> </tr> </logic:iterate> <tr> <td colspan=4 align=center><html:button onclick="window.location='PaginaInicial.do'" property="btnVoltar" value="Sair" /></td> </table> </body> </html> html:link e html:button html:link <html:link page="/Noticia.do?comando=editar" paramId="idDaNoticia" paramName="noticia" paramProperty="id"> • • • • /Noticia.do?comando=editar – chama a mesma action (NoticiaAction) executando dessa vez o comando editar paramId – nome do parâmetro passado na url paramName – nome do bean paramProperty – valor da propriedade html:button <html:button onclick="window.location='PaginaInicial.do'" property="btnVoltar" value="Sair" /> • Botão com javascript para voltar à página inicial Novo Menu.jsp <%@ page language="java" contentType="text/html; charset=UTF-8"%> <%@ taglib uri="/tags/struts-html" prefix="html"%> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>.: Menu :.</title> <link rel="stylesheet" href="css/Estilo.css" type="text/css" /> </head> <body> <table> <tr> <td><html:link page="/PaginaInicial.do">Página Inicial | </html:link></td> <td><html:link page="/Noticia.do?comando=listar">Notícia | </html:link></td> </tr> </table> </body> </html> Tela de Consulta Comando editar • Esse novo comando consulta uma notícia pelo parâmetro idDaNoticia • Teremos um combobox com os tipos de notícia, para montá-lo precisamos da coleção de tipos • Em seguida é só chamar o forward MostrarPaginaDeEdicao NoticiaAction public ActionForward editar(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) { String id = request.getParameter("idDaNoticia"); DAONoticia daoNoticia = new DAONoticia(); DAOTipoDeNoticia daoTipo = new DAOTipoDeNoticia(); Collection<TipoDeNoticiaForm> tipos = daoTipo.consultaTiposDeNoticia(); NoticiaForm n = daoNoticia.consultaNoticiaPeloId(id); request.setAttribute("noticia", n); request.setAttribute("tipos", tipos); return mapping.findForward("MostrarPaginaDeEdicao"); } EditarNoticia.jsp <%@ page language="java" contentType="text/html; charset=UTF-8"%> <%@ taglib uri="/tags/struts-html" prefix="html"%> <%@ taglib uri="/tags/struts-bean" prefix="bean"%> <%@ taglib uri="/tags/struts-logic" prefix="logic"%> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>.: Editar Notícia :.</title> <link rel="stylesheet" href="css/Estilo.css" type="text/css" /> </head> <body> <table width=90% border> <html:form action="/Noticia?comando=confirmar"> <tr> <td colspan=3 align=center> <h2>Editar Notícia</h2> EditarNoticia.jsp </td> </tr> <tr> <td align=center>ID</td> <td><html:text name="noticia" property="id" /></td> </tr> <tr> <td align=center>Data</td> <td><html:text name="noticia" property="data" /></td> </tr> <tr> <td align=center>Título</td> <td><html:text name="noticia" property="titulo" /></td> </tr> <tr> <td align=center>Texto</td> <td><html:text name="noticia" property="texto" /></td> </tr> <tr> <td colspan=3> <tr> EditarNoticia.jsp <td align=center>Tipo de notícia: <td colspan=2> <html:select name="noticia" property="tipoDeNoticia"> <html:optionsCollection name="tipos" value="id" label="descricao" /> </html:select></td> </tr> <tr> <td colspan=3 align=center> <html:submit value="Confirmar" /> <html:button onclick="action='Noticia.do?comando=listar';submit()" value="Sair" property="btnSair" /></td> </tr> </html:form> </table> </body> </html> html:form/select <html:form action="/Noticia?comando=confirmar"> • Os dados dessa página serão enviados para o método confirmar através da NoticiaForm <html:select name="noticia" property="tipoDeNoticia"> <html:optionsCollection name="tipos" value="id" label="descricao" /> </html:select> • html:select – tag para montar uma combobox, inclusive de seleção múltipla – • name, property – nome/propriedade do bean html:optionsCollection – valores do combobox – – – – name – nome da collection value – valor de cada option (id de cada tipoDeNoticia) label – valor que será mostrado ao usuário Se o id do bean for igual ao id da combo, essa opção virá selecionada html:submit <html:submit value="Confirmar" /> • Submete o form para a ação do form <html:button onclick="action='Noticia.do?comando=listar';submit()" value="Sair" property="btnSair" /> • Muda o valor da ação através de javascript e a submete Tela de Edição Confirmando a ação • O método confirmar recebe os dados do formulário de edição e faz a persistência dos dados • Nossos exemplos não têm bancos de dados, mas um DAO acessando o Hibernate poderia fazer isso facilmente NoticiaAction public ActionForward confirmar(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) { NoticiaForm f = (NoticiaForm) form; System.out.println("Dados informados pelo usuário"); System.out.println("ID: " + f.getId()); System.out.println("Data: " + f.getData()); System.out.println("Título: " + f.getTitulo()); System.out.println("Texto: " + f.getTexto()); System.out.println("ID do Tipo: " + f.getTipoDeNoticia()); return mapping.findForward("AbrirPaginaPrincipal"); } Camadas de Persistência e Negócio • O Struts não fornece nenhuma API para a camada de negócio • Seguindo as melhores práticas, a Action deveria acessar um fachada • No nosso exemplo temos acessamos um DAO diretamente • O próximo passo seria acessar efetivamente o banco relacional TipoDeNoticiaForm package net.noticias.form; import org.apache.struts.action.*; public class TipoDeNoticiaForm extends ActionForm { private String id; private String descricao; {...} } Alternativa • A classe TipoDeNoticia é bastante simples, tem apenas código e descrição • Esse tipo de classe é forte candidata a se transformar em formulário dinâmico, discutido posteriormente struts-config.xml • Acrescente a ação respectiva no arquivo de configuração (siga o padrão da ação Noticia) • Já é possível notar a quantidade reduzida de classes Action <action path="/TipoDeNoticia" name="tipoDeNoticiaForm" type="net.noticias.action.TipoDeNoticiaAction" parameter="comando"> <forward name="MostrarPaginaDeEdicao" path="/EditarTipoDeNoticia.jsp" /> <forward name="MostrarPaginaDeConsulta" path="/ConsultarTipoDeNoticia.jsp" /> </action> TipoDeNoticiaAction package net.noticias.action; import java.util.*; import javax.servlet.http.*; import net.noticias.form.*; import net.noticias.persistencia.*; import org.apache.struts.action.*; import org.apache.struts.actions.*; public class TipoDeNoticiaAction extends DispatchAction { public ActionForward editar(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) { String id = request.getParameter("idDoTipo"); DAOTipoDeNoticia dao = new DAOTipoDeNoticia(); TipoDeNoticiaForm tipo = dao.consultaTipoDeNoticiaPeloId(id); request.setAttribute("tipo", tipo); return mapping.findForward("MostrarPaginaDeEdicao"); } TipoDeNoticiaAction public ActionForward listar(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) { DAOTipoDeNoticia d = new DAOTipoDeNoticia(); Collection<TipoDeNoticiaForm> lista = d.consultaTiposDeNoticia(); request.setAttribute("tipos", lista); return mapping.findForward("MostrarPaginaDeConsulta"); } public ActionForward confirmar(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) { TipoDeNoticiaForm f = (TipoDeNoticiaForm) form; System.out.println("Dados informados pelo usuário"); System.out.println("ID: " + f.getId()); System.out.println("Data: " + f.getDescricao()); return mapping.findForward("AbrirPaginaPrincipal"); } } DAOTipoDeNoticia package net.noticias.persistencia; import java.util.*; import net.noticias.form.*; public class DAOTipoDeNoticia { public Collection<TipoDeNoticiaForm> consultaTiposDeNoticia() { Collection<TipoDeNoticiaForm> lista = new ArrayList<TipoDeNoticiaForm>(); TipoDeNoticiaForm n = new TipoDeNoticiaForm(); n.setId("1"); n.setDescricao("Tipo 1"); lista.add(n); n = new TipoDeNoticiaForm(); n.setId("2"); n.setDescricao("Tipo 2"); lista.add(n); n = new TipoDeNoticiaForm(); n.setId("3"); n.setDescricao("Tipo 3"); lista.add(n); return lista; } public TipoDeNoticiaForm consultaTipoDeNoticiaPeloId(String id) { TipoDeNoticiaForm tipo = new TipoDeNoticiaForm(); tipo.setId(id); tipo.setDescricao("Tipo " + id); return tipo; } } TipoDeNoticia • Apesar de simples, temos vários elementos utilizados em ConsultarTipoDeNoticia.jsp <%@ page language="java" contentType="text/html; charset=UTF-8"%> <%@ taglib uri="/tags/struts-html" prefix="html"%> <%@ taglib uri="/tags/struts-bean" prefix="bean"%> <%@ taglib uri="/tags/struts-logic" prefix="logic"%> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>.: Consulta Tipos de Notícia :.</title> <link rel="stylesheet" href="css/Estilo.css" type="text/css" /> </head> <body> <table width=90% border> <tr> <td colspan=4 align=center> <h2>Consulta Tipo de Notícia</h2> </td> </tr> <tr> <td align=center>ID</td> <td>Descrição</td> <td>Editar</td> </tr> ConsultarTipoDeNoticia.jsp <logic:iterate id="tipo" name="tipos"> <tr> <td align=center><bean:write name="tipo" property="id" /></td> <td><bean:write name="tipo" property="descricao" /></td> <td><html:link page="/TipoDeNoticia.do?comando=editar" paramId="idDoTipo" paramName="tipo" paramProperty="id"> <html:img src="imagens/edit.gif" border="0" /> </html:link> </tr> </logic:iterate> <tr> <td colspan=4 align=center><html:button onclick="window.location='PaginaInicial.do'" property="btnVoltar" value="Sair" /></td> </table> </body> </html> Consultas • As consultas seguem o mesmo princípio: a Action salva uma coleção de objetos numa variável (geralmente) de sessão e através do logic:iterate a gente escreve os vários valores na tela EditarTipoDeNoticia.jsp <%@ page language="java" contentType="text/html; charset=UTF-8"%> <%@ taglib uri="/tags/struts-html" prefix="html"%> <%@ taglib uri="/tags/struts-bean" prefix="bean"%> <%@ taglib uri="/tags/struts-logic" prefix="logic"%> <html:html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>.: Editar Tipo de Notícia :.</title> <link rel="stylesheet" href="css/Estilo.css" type="text/css" /> </head> <body> <table width=90% border> <html:form action="/TipoDeNoticia?comando=confirmar" onsubmit="return validateTipoDeNoticiaForm(this);"> <tr> <td colspan=3 align=center> <h2>Edição de Tipo de Notícia</h2> </td> </tr> EditarTipoDeNoticia.jsp <tr> <td align=center>ID</td> <td><html:text name="tipo" property="id" /></td> </tr> <tr> <td align=center>Descrição</td> <td><html:text name="tipo" property="descricao" /></td> </tr> <tr> <td colspan=3 align=center><html:submit value="Confirmar" /><html:button onclick="action='TipoDeNoticia.do?comando=listar';submit()" value="Sair" property="btnSair" /></td> </tr> </html:form> </table> </body> </html:html> Edição • O id de um dos objetos da lista é enviado pela url para a Action, executando o comando de edição • A aplicação deve recuperar o objeto do banco e guardar seus valores em uma variável de sessão • A página de edição espera essa variável e escreve seus valores através das tags html DynaActionForm • Formulário dinâmico baseado na interface java.util.Map • Não é necessário criar uma classe ActionForm • Configurado no struts-config como um simples <form-bean> • Grande flexibilidade • Não utiliza reflexão struts-config-form-beans.xml • Adicione o form-bean para Fonte <form-bean name="fonteForm" type="org.apache.struts.action.DynaActionForm"> <form-property name="id" type="java.lang.String" /> <form-property name="descricao" type="java.lang.String" /> </form-bean> • Os atributos podem usar todas as classes wrapper, collections e arrays • Não é necessário criar a classe! struts-config.xml • Acrescente a Action para o novo form-bean • Veja a diferença: nenhuma <action path="/Fonte" type="net.noticias.action.FonteAction" name="fonteForm" parameter="comando"> <forward name="MostrarPaginaDeEdicao" path="/EditarFonte.jsp" /> <forward name="MostrarPaginaDeConsulta" path="/ConsultarFontes.jsp"> </forward> </action> FonteAction package net.noticias.action; import javax.servlet.http.*; import net.noticias.persistencia.*; import org.apache.struts.action.*; import org.apache.struts.actions.*; public class FonteAction extends DispatchAction { public ActionForward listar(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) { DAOFonte dao = new DAOFonte(); request.setAttribute("fontes", dao.consultaFontes()); return mapping.findForward("MostrarPaginaDeConsulta"); } } Poucas diferenças • o método para listar as fontes não difere dos outros • A principal diferença fica por conta do DAO, pois os formulários dinâmicos não têm classes concretas • Todos são implementados através de mapas DAOFonte package net.noticias.persistencia; import java.util.*; public class DAOFonte { public Collection<Map<String, String>> consultaFontes() { Collection<Map<String, String>> lista = new ArrayList<Map<String, String>>(); Map<String, String> mapa = new HashMap<String, String>(); mapa.put("id", "1"); mapa.put("descricao", "Fonte 1"); lista.add(mapa); mapa = new HashMap<String, String>(); mapa.put("id", "2"); mapa.put("descricao", "Fonte 2"); lista.add(mapa); mapa = new HashMap<String, String>(); mapa.put("id", "3"); mapa.put("descricao", "Fonte 3"); lista.add(mapa); return lista; } public Map<String, String> consultaFontePeloId(String id) { Map<String, String> mapa = new HashMap<String, String>(); mapa.put("id", id); mapa.put("descricao", "Tipo " + id); return mapa; } } Tudo é Map • Como não há classe concreta, nosso valores ficam encapsulados em mapas • Para fazer a persistência num banco relacional, você deve implementar utilitários que mapeiem os mapas para seus DTO’s ConsultarFontes.jsp <%@ page language="java" contentType="text/html; charset=UTF-8"%> <%@ taglib uri="/tags/struts-html" prefix="html"%> <%@ taglib uri="/tags/struts-bean" prefix="bean"%> <%@ taglib uri="/tags/struts-logic" prefix="logic"%> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>.: Consulta Notícias :.</title> <link rel="stylesheet" href="css/Estilo.css" type="text/css" /> </head> <body> <table width=90% border> <tr> <td colspan=4 align=center> <h2>Consulta Fontes</h2> </td> </tr> <tr> <td align=center>ID</td> <td>Descrição</td> <td>Editar</td> </tr> ConsultarFontes.jsp <logic:iterate id="fonte" name="fontes"> <tr> <td align=center><bean:write name="fonte" property="id" /></td> <td><bean:write name="fonte" property="descricao" /></td> <td><html:link page="/Fonte.do?comando=editar" paramId="idDaFonte" paramName="fonte" paramProperty="id"> <html:image src="imagens/edit.gif" /> </html:link> </tr> </logic:iterate> <tr> <td colspan=4 align=center><html:button onclick="window.location='PaginaInicial.do';submit()" property="btnVoltar" value="Sair" /></td> </tr> </table> </body> </html> Consulta com DynaActionForm • A página de consulta é idêntica a todas as outras • Atenção ao DAO, pois temos que usar apenas Map’s FonteAction public ActionForward editar(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) { DAOFonte dao = new DAOFonte(); String id = request.getParameter("idDaFonte"); request.setAttribute("fonte", dao.consultaFontePeloId(id)); return mapping.findForward("MostrarPaginaDeEdicao"); } EditarFonte.jsp <%@ page language="java" contentType="text/html; charset=UTF-8"%> <%@ taglib uri="/tags/struts-html" prefix="html"%> <%@ taglib uri="/tags/struts-bean" prefix="bean"%> <%@ taglib uri="/tags/struts-logic" prefix="logic"%> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>.: Editar Fonte :.</title> <link rel="stylesheet" href="css/Estilo.css" type="text/css" /> </head> <body> <table width=90% border> <html:form action="/Fonte?comando=confirmar"> <tr> <td colspan=3 align=center> <h2>Editar Fonte</h2> </td> </tr> EditarFonte.jsp <tr> <td align=center>ID</td> <td><html:text name="fonte" property="id" /></td> </tr> <tr> <td align=center>Descrição</td> <td><html:text name="fonte" property="descricao" /></td> </tr> <tr> <td colspan=3 align=center><html:submit value="Confirmar" /> <html:button onclick="action='Fonte.do?comando=listar';submit()" value="Sair" property="btnSair" /></td> </tr> </html:form> </table> </body> </html> Edição com DynaActionForm • Para preencher a página de edição precisamos fornecer um mapa com os atributos da fonteForm public Map<String, String> consultaFontePeloId(String id) { Map<String, String> mapa = new HashMap<String, String>(); mapa.put("id", id); mapa.put("descricao", "Tipo " + id); return mapa; } A utilização de array’s • Em várias situações precisamos utilizar estruturas complexas, mas quando trabalhamos com ambientes web, temos uma série de limitações • Não é interessante, por exemplo, transmitir objetos entre as requisições de usuário. Esse tipo de recurso deve ser evitado ao máximo • Os valores transmitidos entre as páginas são sempre do tipo String e quando temos vários componentes em uma requisição com o mesmo nome, o request encapsula seus valores em um array • E trabalhar com array sempre foi razoavelmente complicado struts-config.xml • Abaixo está a Action dessa nova funcionalidade • O escopo está configurado para request. Depois do primeiro teste, mude para session • O parâmetro attribute=“noticia” cria a variável noticia e a passa para a página seguinte, preenchida com os dados da página atual <action path="/SelecaoMultiplaDeNoticias" type="net.noticias.action.SelecaoMultiplaDeNoticiasAction" name="noticiaForm" attribute="noticia" scope="request" parameter="comando"> <forward name="MostrarPaginaDeConsulta" path="/SelecaoMultiplaDeNoticias.jsp" /> <forward name="MostrarNoticiasSelecionadas" path="/ResultadoDaSelecaoMultiplaDeNoticias.jsp" /> </action> SelecaoMultiplaDeNoticiasAction package net.noticias.action; import javax.servlet.http.*; import net.noticias.persistencia.*; import org.apache.struts.action.*; import org.apache.struts.actions.*; public class SelecaoMultiplaDeNoticiasAction extends DispatchAction { public ActionForward mostrarTodasAsNoticias(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) { DAONoticia d = new DAONoticia(); request.setAttribute("noticias", d.consultaNoticias()); return mapping.findForward("MostrarPaginaDeConsulta"); } public ActionForward mostrarAsNoticiasSelecionadas(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) { return mapping.findForward("MostrarNoticiasSelecionadas"); } } Página de seleção • A Action simplesmente envia uma coleção de notícias para a página de seleção que mostra seus valores com uma tag html:selection SelecaoMultiplaDeNoticias.jsp <%@ page language="java" contentType="text/html; charset=UTF-8"%> <%@ taglib uri="/tags/struts-html" prefix="html"%> <%@ taglib uri="/tags/struts-bean" prefix="bean"%> <%@ taglib uri="/tags/struts-logic" prefix="logic"%> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>.: Consulta Notícias :.</title> <link rel="stylesheet" href="css/Estilo.css" type="text/css" /> </head> <body> <table width=90% border> <html:form action="/SelecaoMultiplaDeNoticias.do?comando=mostrarAsNoticiasSelecionadas"> <bean:size id="quantidadeDeNoticias" name="noticias" /> <tr> <td colspan=2 align=center> <h2><bean:write name="quantidadeDeNoticias" /> Notícia(s) Cadastrada(s)</h2> </td> SelecaoMultiplaDeNoticias.jsp </tr> <tr> <td align=center>Notícias: <td><html:select name="noticia" property="noticiasSelecionadas" multiple="true"> <html:optionsCollection name="noticias" value="id" label="titulo" /> </html:select></td> </tr> <tr> <td colspan=2 align=center><html:button onclick="submit()" property="btnSelecionar" value="Selecionar" /></td> </tr> </html:form> </table> </body> </html> html:select e bean:size <html:select name="noticia" property="noticiasSelecionadas" multiple="true"> <html:optionsCollection name="noticias" value="id" label="titulo" /> </html:select> • multiple=“true” – Indica que esse select aceitará seleção múltipla – Os itens selecionados serão armazenados em um array do formBean <bean:size id="quantidadeDeNoticias" name="noticias" /> • Cria a variável quantidadeDeNoticias com a quantidade de itens do bean noticias <bean:write name="quantidadeDeNoticias" /> • Escreve o valor da variável quantidadeDeNoticias Tela de seleção ResultadoDaSelecaoMultiplaDeNoticias.jsp <%@ page language="java" contentType="text/html; charset=UTF-8"%> <%@ taglib uri="/tags/struts-html" prefix="html"%> <%@ taglib uri="/tags/struts-bean" prefix="bean"%> <%@ taglib uri="/tags/struts-logic" prefix="logic"%> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>.: Seleção Múltipla de Notícia :.</title> <link rel="stylesheet" href="css/Estilo.css" type="text/css" /> </head> <body> <table width=90% border> <bean:size id="quantidadeDeNoticiasSelecionadas" name="noticia" property="noticiasSelecionadas" /> <tr> <td colspan=2 align=center> <h2><bean:write name="quantidadeDeNoticiasSelecionadas" /> Notícia(s) Selecionada(s)</h2> </td> ResultadoDaSelecaoMultiplaDeNoticias.jsp </tr> <tr> <td align=center>ID</td> </tr> <logic:iterate id="idDaNoticiaSelecionada" property="noticiasSelecionadas" name="noticia"> <tr> <td align=center><bean:write name="idDaNoticiaSelecionada" /></td> </tr> </logic:iterate> <tr> <td colspan=2 align=center><html:button onclick="window.location='SelecaoMultiplaDeNoticias.do?comando=mostrarTodasAsNoticias';submit()" property="btnSelecionar" value="Voltar" /><html:button onclick="window.location='PaginaInicial.do';submit()" value="Sair" property="btnSair" /></td> </tr> </table> </body> </html> bean:size e logic:iterate <bean:size id="quantidadeDeNoticiasSelecionadas" name="noticia" property="noticiasSelecionadas" /> • Cria a variável quantidadeDeNoticiasSelecionadas com a quantidade itens da property noticiasSelecionadas do bean noticia <bean:write name="quantidadeDeNoticiasSelecionadas" /> • Escreve a quantidadeDeNoticiasSelecionadas <logic:iterate id="idDaNoticiaSelecionada" property="noticiasSelecionadas" name="noticia"> • Cria a variável idDaNoticiaSelecionada com o valor de cada item do array <bean:write name="idDaNoticiaSelecionada" /> • Escreve o id na tela Tela de resultado Agora é sua vez • O Struts entrega os dados, mas o processamento quem faz é você • Seguindo as boas práticas, agora é a vez consultar as notícias pelo id, através de uma fachada/DAO • Nunca coloque regras de negócio em suas Actions Validation Framework • • • • • A lógica de validação é escrita em arquivos XML Originado do Validator Framework do Jakarta Incluído no Struts a partir da versão 1.1 Permite validação declarativa para vários campos Valida datas, números, email, cartão de crédito, código postar (USA), tamanhos, range e expressões regulares Regras de Validação • Regras são definidas para campos específicos de um form • Já dispõe de vários validadores prontos – required, minLength, maxLength, date, integer, mask • Extensível: você pode criar seus próprios validadores Validação do ActionForm • Para usar o Validator, torne seus ActionForm’s subclasses de ValidatorForm ou ValidatorActionForm • Se estiver usando DynaActionForm, passe a extender de DynaValidatorForm ou DynaValidatorActionForm NoticiaForm package net.noticias.form; import org.apache.struts.validator.*; public class NoticiaForm extends ValidatorActionForm { private String id; private String texto; private String titulo; private String data; private String tipoDeNoticia; private String[] noticiasSelecionadas; {...} } validation.xml • Específico da sua aplicação • Configuração das regras aplicadas a cada campo do seu formulário • Torna desnecessário o método validate() do ActionForm validator-rules.xml • Fornecido pelo Struts • Regras que já fazem parte do Validator • Fica dentro da lib struts-core.jar, no pacote org.apache.struts.validator Plugin Validator • Para começar a usar o Validator, acrescente o plugin no struts-config.xml <plug-in className="org.apache.struts.validator.ValidatorPlugIn"> <set-property property="pathnames" value="/org/apache/struts/validator/validator-rules.xml, /WEB-INF/validation.xml" /> </plug-in> Validação no Cliente • O Validator pode fazer a validação no lado do cliente • Para tanto, acrescente a tag seguinte logo após o <head> • Essa tag irá gerar todo o código javascript para validar seu formulário <head> <html:javascript formName="noticiaForm" /> Validação no Cliente • É gerado automaticamente um método javascript validateXxx, onde Xxx é o nome do seu formulário • Atualize a tag html:form para seus formulários <html:form action="/Noticia?comando=confirmar" onsubmit="return validateNoticiaForm(this)"> MessageResources # erro.tipodenoticia.id.requerido={0} é requerido erro.tipodenoticia.descricao.requerido={0} é requerida # tipodenoticia.id=ID tipodenoticia.descricao=Descrição # noticia.id=ID noticia.data=Data # errors.required={0} is required. errors.minlength={0} can not be less than {1} characters. errors.maxlength={0} can not be greater than {1} characters. errors.invalid={0} is invalid. errors.byte={0} must be a byte. errors.short={0} must be a short. errors.integer={0} must be an integer. errors.long={0} must be a long. errors.float={0} must be a float. errors.double={0} must be a double. errors.date={0} is not a date. errors.range={0} is not in the range {1} through {2}. errors.creditcard={0} is an invalid credit card number. errors.email={0} is an invalid e-mail address. Mensagens Personalizadas • O Validator já tem várias mensagens configuradas (em inglês) • Você pode definir suas próprias mensagens validation.xml <?xml version="1.0" encoding="iso-8859-1"?> <!DOCTYPE form-validation PUBLIC "-//Apache Software Foundation//DTD Commons Validator Rules Configuration 1.3.0//EN" "http://jakarta.apache.org/commons/dtds/validator_1_3_0.dtd"> <form-validation> <formset> <form name="tipoDeNoticiaForm"> <field property="id" depends="required,integer"> <arg key="tipodenoticia.id" /> </field> <field property="descricao" depends="required"> <msg name="required" key="erro.tipodenoticia.descricao.requerido" /> <arg key="tipodenoticia.descricao" /> </field> </form> validation.xml <form name="noticiaForm"> <field property="id" depends="required,integer"> <arg key="noticia.id" /> </field> <field property="data" depends="required,date"> <arg key="noticia.data" /> <var> <var-name>datePattern</var-name> <var-value>dd/MM/yyyy</var-value> </var> </field> </form> </formset> </form-validation> Regras de Validação <form name="tipoDeNoticiaForm"> • Nome do formulário definido no struts-config.xml <field property="id" depends="required,integer"> • Nome da propriedade e suas dependências (definidas no validator-rules.xml) <arg key="tipodenoticia.id" /> • Essa é a mensagem que substitui o parâmetro {0} no MessageResources caso o campo não tenha um valor válido <field property="descricao" depends="required"> <msg name="required" key="erro.tipodenoticia.descricao.requerido" /> • Essa é a mensagem personalizada para o validador required <var-name>datePattern</var-name> <var-value>dd/MM/yyyy</var-value> • Padrão para data. Veja a lista na documentação da SimpleDateFormat Exemplo de Popup’s • Ao lado você pode ver alguns exemplos de mensagens • No primeiro você pode ver uma mensagem padrão (em inglês) e uma personalizada • Para entender o mecanismo de troca de mensagens, veja todos os arquivos envolvidos Exercícios • Crie as regras de validação para o Formulário FonteForm JSP2.0 EL • Linguagem padrão da Sun para a camada de apresentação • Acesso conciso, rápido e prático • Acessa subpropriedades dos beans • Sintaxe incrivelmente simples • É da Sun... Concorrentes • jsp:useBean e jsp:getProperty – Não pode acessar subpropriedades do bean – Complexo e nem um pouco prático • bean:write – Também não pode acessar subpropriedades do bean – Não é a saída mais prática • Elementos de script JSP – Resulta em um código JSP impossível de manter – Destrói o principal propósito do MVC Instalação da EL • Para instalar você só precisa alterar o cabeçalho do web.xml • Já faz parte dos containers mais novos, como Tomcat 5 • Parte integrante do JSP2.0 web.xml <?xml version="1.0" encoding="ISO-8859-1"?> <web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee web-app_2_4.xsd" version="2.4"> {...} </web-app> Invocando a EL • Forma básica – ${expressão} – ${bean.propriedade} • Você pode combiná-la com as demais notações – • <jsp:include page="${expr1}blah${expr2}" /> Exemplo com Struts <%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %> <UL> <LI>First name: <bean:write name="contactFormBean" property="firstName"/> <LI>Last name: <bean:write name="contactFormBean" property="lastName"/> <LI>Email address: <bean:write name="contactFormBean" property="email"/> <LI>Fax number: <bean:write name="contactFormBean" property="faxNumber"/> </UL> Agora com EL <UL> <LI>First name: ${contactFormBean.firstName} <LI>Last name: ${contactFormBean.lastName} <LI>Email address: ${contactFormBean.email} <LI>Fax number: ${contactFormBean.faxNumber} </UL> Exercícios • No formulário de consulta notícias, combine as tags Struts com a JSP EL