Olá Pessoal, tudo bom?
No artigo de hoje iremos descrever um erro que ocorre quando o JSF não consegue localizar determinado ID em uma página xhtml. Especificamente estamos falando da exception javax.faces.FacesException: Cannot find component with expression “id” referenced from “panel”.
Descrição do Erro
Observe as telas representadas na Figuras 01. A primeira parte da Figura 01 representa um “filtro” de pesquisa, o qual possui um botão Pesquisar, que, de acordo com a segunda parte da Figura 01, irá mostrar um Data Table com as informações de cada Herói. E a terceira parte da Figura 01 nos mostra que ao selecionar um Herói na data table, irá aparecer o nome deste em um panel abaixo da data table.
Código – Versão Inicial
Para representar essa tela o desenvolvedor construiu o seguinte código para a página testePComponent.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 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
<!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 p:component </f:facet> <p:commandButton value="Pesquisar" action="#{testePComponentMB.pesquisar}" update="resultados" /> </p:panel> <p:panel id="resultados" style="border:0px;"> <p:dataTable value="#{testePComponentMB.vingadores}" var="vingador" rendered="#{not empty testePComponentMB.vingadores}" rowKey="#{vingador.id}" selectionMode="single" selection="#{testePComponentMB.selecionado}"> <p:ajax event="rowSelect" update="posResultados" /> <f:facet name="header"> Vingadores </f:facet> <p:column headerText="Heroi"> #{vingador.nome} </p:column> </p:dataTable> </p:panel> <p:panel id="posResultados" style="border:0px;"> <p:panel rendered="#{testePComponentMB.selecionado ne null}"> <h:outputText value="O Heroi selecionado foi #{testePComponentMB.selecionado.nome}"/> </p:panel> </p:panel> </h:form> </h:body> </html> |
Repare que para que todos os painéis representados nessa tela, nós já utilizamos do conceito apresentado nesse artigo. Assim não temos problema do componente não estar representado na arvore DOM e podemos re renderizá-lo corretamente.
Outro aspecto aqui importante é que nós utilizamos um componente, de nome p:ajax, dentro da data table para que possamos selecionar determinada linha e exibir a última parte que é uma frase representado qual foi o herói escolhido no data table.
Para que essa tela funcione foi implementado um Managed Bean (MB) de nome TestePComponentMB, que possui o escopo View, indicando que o Managed Bean permanecerá vivo, não será destruído pelo JSF, enquanto ele permanecer nesta página. O código deste MB está representado abaixo:
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 57 58 59 60 61 62 63 64 65 66 67 68 69 |
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; import javax.faces.bean.ViewScoped; @ManagedBean @ViewScoped public class TestePComponentMB implements Serializable{ ///////////////////////////////////// // Atributos ///////////////////////////////////// private static final long serialVersionUID = 1L; private Collection<Vingador> vingadores; private Vingador selecionado; ///////////////////////////////////// // Construtores ///////////////////////////////////// public TestePComponentMB() { } @PostConstruct private void init(){ vingadores = new ArrayList<Vingador>(); } ///////////////////////////////////// // Actions ///////////////////////////////////// //Action de Pesquisar a partir do filtro public void pesquisar() { if(vingadores.isEmpty()){ Long i = 0L; String[] nomes = {"Hulk", "Capitão América", "Homem de Ferro", "Thor", "Viuva Negra", "Gavião Arqueiro"}; for(String nome : nomes){ Vingador v = new Vingador(); v.setId(i++); v.setNome(nome); vingadores.add(v); } } } ///////////////////////////////////// // Getters and Setters ///////////////////////////////////// public Collection<Vingador> getVingadores() { return vingadores; } public Vingador getSelecionado() { return selecionado; } public void setSelecionado(Vingador selecionado) { this.selecionado = selecionado; } } |
Por fim, ambos os códigos utilizam-se de uma classe Vingador, representada abaixo:
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 |
package br.com.mauda.model; import java.io.Serializable; public class Vingador implements Serializable { ///////////////////////////////////// // Atributos ///////////////////////////////////// private static final long serialVersionUID = 1L; private Long id; private String nome; ///////////////////////////////////// // Getters and Setters ///////////////////////////////////// public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getNome() { return nome; } public void setNome(String nome) { this.nome = nome; } } |
Rodando o código da Versão Inicial… e ERRO!
Ao rodar o código, você conseguirá entrar na tela. Mas, ao clicar no botão Pesquisar, ele não fará nada na tela, mas se você observar o console da IDE verificará a seguinte mensagem:
GRAVE [javax.enterprise.resource.webcontainer.jsf.context] (http-localhost-127.0.0.1-8080-3) javax.faces.FacesException: Cannot find component with expression “posResultados” referenced from “j_idt6:j_idt10”.
Vamos pensar na arvore DOM desta página. O nodo <h:form> possui 3 filhos do elemento <p:panel>. E dentro de um desses filhos existe um filho do elemento <p:dataTable>. Aí que o problema começa…
p:dataTable ((p:dataTable – API – link)) implementa a interface javax.faces.component.NamingContainer((javax.faces.component.NamingContainer – API – link)). Essa interface define um novo padrão de id, o qual deve ser mencionado em outras referencias. Dessa forma quando está em um novo NamingContainer, não é possível acessar outros NamingContainer definidos na página senão forem referenciados diretamente. Em um futuro irei descrever um pouco mais sobre NamingContainer já que é um assunto que confunde a maioria dos desenvolvedores.
Código utilizando o método component
Para corrigir esse erro então é necessário referenciar diretamente o NamingContainer. Mas isso pode ser um pouco maçante ou mesmo difícil de ser feito, assim se você estiver utilizando a biblioteca JSF do primefaces existe um método que trabalha para localizar itens. Esse método é o p:component ((p:component – API – link)). Para utilizá-lo você deverá passar qual é o componente que o primefaces deverá localizar, no caso posResultados. O primefaces irá substituir essa linha, pelo caminho completo até o componente. O exemplo pode ser visualizado na linha 32 do código abaixo.
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 |
<!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 p:component </f:facet> <p:commandButton value="Pesquisar" action="#{testePComponentMB.pesquisar}" update="resultados" /> </p:panel> <p:panel id="resultados" style="border:0px;"> <p:dataTable value="#{testePComponentMB.vingadores}" var="vingador" rendered="#{not empty testePComponentMB.vingadores}" rowKey="#{vingador.id}" selectionMode="single" selection="#{testePComponentMB.selecionado}"> <p:ajax event="rowSelect" update=":#{p:component('posResultados')}" /> <f:facet name="header"> Vingadores </f:facet> <p:column headerText="Heroi"> #{vingador.nome} </p:column> </p:dataTable> </p:panel> <p:panel id="posResultados" style="border:0px;"> <p:panel rendered="#{testePComponentMB.selecionado ne null}"> <h:outputText value="O Heroi selecionado foi #{testePComponentMB.selecionado.nome}"/> </p:panel> </p:panel> </h:form> </h:body> </html> |
Um detalhe importante é que se o componente estiver debaixo do mesmo form, deve ser inserido os dois pontos (“:”) antes da chamada do método.
finnaly{
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