Olá Pessoal, tudo bom?
No artigo de hoje iremos descrever um erro que ocorre quando combinamos Ajax com o Atributo rendered de um panel, dataTable ou qualquer outro componente que possua esse atributo. O desenvolvedor faz tudo “corretamente”, mas o componente desejado não aparece na tela e o pior não aparece nenhum erro no console do Eclipse. O que fazer então?
Contexto do Problema – Documentação
Observe as telas representadas nas Figuras 01 e 02. A Figura 01 representa um “filtro” de pesquisa, o qual possui um botão Pesquisar, que, de acordo com a Figura 02, irá mostrar um Data Table com as informações de cada Herói.
Dessa forma o desenvolvedor irá desenvolver um código que crie uma tela que tenha esse padrão, um filtro de pesquisa, um botão que realiza uma ação de trazer dados de uma camada Business e que apresenta esses dados em uma data table na tela.
Código da Solução – Versão Inicial
Primeira observação: O código mostrado aqui é a titulo de compreensão do erro. Você não irá criar um filtro de pesquisa sem campos, ou uma busca que traz resultados estáticos. Aqui é apenas para demonstrar o erro. Dito isso vamos para os códigos.
Para atender a documentação descrita acima, o desenvolvedor gera o código para a página testeDataTableNaoAparece.xhtml:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html" xmlns:p="http://primefaces.org/ui"> <h:head> <title>Vingadores</title> </h:head> <h:body> <h:form> <p:panel> <f:facet name="header"> Teste DataTable Que Não Aparece </f:facet> <p:commandButton value="Pesquisar" action="#{testeDataTableNaoApareceMB.pesquisar}" update="resultados" /> </p:panel> <p:dataTable value="#{testeDataTableNaoApareceMB.vingadores}" var="vingador" rendered="#{not empty testeDataTableNaoApareceMB.vingadores}" rowKey="#{vingador.id}" id="resultados" > <f:facet name="header"> Vingadores </f:facet> <p:column headerText="Heroi"> #{vingador} </p:column> </p:dataTable> </h:form> </h:body> </html> |
Repare que o componente p:dataTable, possui preenchido o atributo rendered, onde somente com a lista com informações será exibida em tela e possui também um id, resultados, que é chamado pelo atributo update do componente p:commandButton.
Além disso o desenvolvedor fez o Managed Bean TesteDataTableNaoApareceMB:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
package br.com.mauda.view.mb; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import javax.annotation.PostConstruct; import javax.faces.bean.ManagedBean; @ManagedBean public class TesteDataTableNaoApareceMB implements Serializable{ ///////////////////////////////////// // Atributos ///////////////////////////////////// private static final long serialVersionUID = 1L; private Collection<String> vingadores; ///////////////////////////////////// // Construtores ///////////////////////////////////// public TesteDataTableNaoApareceMB() { } @PostConstruct private void init(){ vingadores = new ArrayList<String>(); } ///////////////////////////////////// // Actions ///////////////////////////////////// //Action de Pesquisar a partir do filtro public void pesquisar() { if(vingadores.isEmpty()){ vingadores.add("Hulk"); vingadores.add("Capitão América"); vingadores.add("Homem de Ferro"); vingadores.add("Thor"); vingadores.add("Viuva Negra"); vingadores.add("Gavião Arqueiro"); } } ///////////////////////////////////// // Getters and Setters ///////////////////////////////////// public Collection<String> getVingadores() { return vingadores; } } |
Esse Managed Bean é bem simples, com a ressalva que o método pesquisar() está inserindo valores estaticamente. Isso não deve ocorrer em um código do mundo real 🙂
Rodando o código da Versão Inicial… e ERRO!
Rode o projeto JSF e acesse a página construída. O sistema irá exibir a tela com o filtro. Agora clique no botão Pesquisar. Não irá aparecer o DataTable… Olhe no console do Eclipse e aí alguma coisa? Nada… Pô! que droga! o Código está correto! (Pelo menos na sua visão :-))
Relembrando… DOM Tree
Para contextualizar vamos lembrar um pouco sobre DOM tree a partir desse artigo. Agora com essa explanação vamos pensar no código da página xhtml. Repare no seguinte trecho:
1 2 3 4 |
<p:dataTable value="#{testeDataTableNaoApareceMB.vingadores}" var="vingador" rendered="#{not empty testeDataTableNaoApareceMB.vingadores}" rowKey="#{vingador.id}" id="resultados" > |
Observe o que faz a linha 2. O atributo rendered deste componente somente o renderiza se ele for verdadeiro. Vamos pensar no primeiro momento da página, quando realizamos o acesso a esta. Esse componente p:dataTable NÃO SERÁ RENDERIZADO, pois não existe nenhum elemento dentro da collection vingadores. Agora juntando essa informação com a informação sobre DOM Tree, temos que no primeiro acesso a página não existirá um nodo para o p:dataTable.
Como dentro do componente p:commandButton, temos a parte de re renderização (update) de uma requisição Ajax apontando para o ID do componente p:dataTable, temos a seguinte pergunta: Qual ID essa requisição Ajax irá atualizar? Pense que esse ID não existe na Arvore DOM do documento HTML. Toda requisição Ajax utiliza o metodo JavaScript document.getElementById(). Assim se o ID não está descrito na Arvore DOM, logo esse método não recuperará o Element, ocasionando o Erro.
Lógico não descarto aqui que deveria ser informado, ao menos no console da IDE, que o JSF não identificou determinado ID. Isso auxiliaria a encontrar o erro.
Código da Solução – Versão Final
Para solucionar esse erro, nós devemos ter um componente que já esteja renderizado no DOM Tree já no primeiro acesso a página. Uma forma simples de realizar isso é criar um panel que englobe toda a região de desejamos re renderizar. Dessa forma a linha 23 cria um p:panel que contém o ID que iremos colocar no update e, para não causar erros de ids duplicados, removemos o id que estava dentro do componente p:dataTable. O style css adicionado ao p:panel é apenas para que não seja renderizada uma borda de cor preta na tela. Veja a solução completa no código abaixo, as linhas destacadas são as que foram acrescidas:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html" xmlns:p="http://primefaces.org/ui"> <h:head> <title>Vingadores</title> </h:head> <h:body> <h:form> <p:panel> <f:facet name="header"> Teste DataTable Que Não Aparece </f:facet> <p:commandButton value="Pesquisar" action="#{testeDataTableNaoApareceMB.pesquisar}" update="resultados" /> </p:panel> <p:panel id="resultados" style="border:0px;"> <p:dataTable value="#{testeDataTableNaoApareceMB.vingadores}" var="vingador" rendered="#{not empty testeDataTableNaoApareceMB.vingadores}" rowKey="#{vingador.id}"> <f:facet name="header"> Vingadores </f:facet> <p:column headerText="Heroi"> #{vingador} </p:column> </p:dataTable> </p:panel> </h:form> </h:body> </html> |
Com esse código ao clicar no botão Pesquisar, a Data Table desejada será renderizada em tela.
finnaly{
Uma observação aqui que se faz necessária, é que esse erro ocorre devido a combinação Ajax + rendered. Assim não importa se você está renderizando um data table ou panel ou até mesmo um botão. Caso este não esteja presente na DOM tree, o JSF não conseguirá realizar a renderização. Pense também em soluções alternativas como utilizar o disabled, pois nesse caso o componente já estaria na DOM tree, mas desabilitado em tela. Isso pode ajudar a evitar esses problemas.
Duvidas ou sugestões? Deixe seu feedback! Isso ajuda a saber a sua opinião sobre os artigos e melhorá-los para o futuro! Isso é muito importante!
Até um próximo post!
Leave a Reply