Problema do rendered dinâmico com JSF

Desde o surgimento dos conjuntos de componentes Ajax passou-se a aproveitar melhor as features do JSF e destes componentes para trazer ao usuário uma UI mais rica e na medida do possível mais leve. Graças a estes componentes ficou mais fácil desenvolver estes tipos de UI quase que transparentemente da mesma forma convencional (sem Ajax) que estávamos acostumados a escrever nossas páginas.

Entre tantos benefícios dessa abordagem apareceram alguns problemas que também não estávamos acostumados a lidar, como saber o que enviar e repintar antes de uma requisição Ajax, ou saber limpar um formulário adequadamente quando necessário ou mesmo repintar determinados componentes de exibição dinâmica.

Pegando este último problema como tema deste post, o que eu tenho notado é que ainda hoje em dia ele é um dos problemas mais comuns de acontecer e ao mesmo tempo um dos mais discutidos em fóruns e listas de discussão. Sendo, nada mais justo do que explana-lo aqui no blog e facilitar a vida de muitos, assim como a minha.

Problema

Este problema, que muitos ainda desconhecem, é comumente conhecido como “problema do rendered dinâmico” (bem, ao menos é como eu o chamo!) e só acontece quando trabalhamos com JSF e Ajax – independente do conjunto de componentes – mais especificamente após tentarmos repintar (ou rerenderizar, como você queira chamar) um componente com o atributo rendered dinâmico, como este:


    

Este componente poderia ser repintado após algum evento Ajax disparado pelo usuário na tela, como por exemplo ao selecionar o tipo de cadastro entre pessoa física ou jurídica, algo similar a isto:


    
    
    

Aparentemente não há qualquer problema com os trechos de código acima, principalmente se o componente iniciar visível para o usuário (ou seja, a EL ${myBean.pessoaFisica} do rendered estiver sendo avaliada para true).

Após mudar para o tipo pessoa jurídica é disparado um evento Ajax, o componente é repintado e desaparece da tela, como era de se esperar, claro. Contudo se você alterar a opção para o tipo pessoa física o componente não reaparecerá para o usuário. E é aí onde está o problema!

Se você for esperto, após alguns minutos analisando o problema, notará que a requisição Ajax foi disparada pelo componente tipoDePessoa (Firebug, plugin para Firefox, pode te ajudar aqui), o estado do managed bean foi alterado e o ciclo de vida do Faces foi processado sem qualquer erro.

E a partir daí, depois de refazer e analisar todos os passos básicos e sensatos, perde-se inúmeras horas tentando descobrir onde está de fato o problema neste simples cenário. A primeira vista parece ser um bug do JSF ou do conjunto de componentes, mas na verdade não é.

Solução

Sendo direto e sem rodeios: você não pode rerenderizar um componente na qual possua o atributo rendered dinâmico, ou seja, o componente não pode possuir EL para definir seu estado de visibilidade para o usuário.

Para resolver isso, repinte o componente parent do componente que possui o rendered dinâmico. Lembrando que este componente parent não pode ter o atributo rendered dinâmico, ou seja, ele sempre precisa ser renderizado.

Componentes como a4j:outputPanel ou mesmo h:panelGroup são excelentes componentes para servirem como componente parent. Sabendo disto, o código apropriado seria algo como:


    
        
    

E o componente responsável pelo evento Ajax deve repintar, no caso acima, o a4j:outputPanel (componente parent sem rendered dinâmico) e não mais o h:panelGroup.

Entendendo o problema

Para quem ainda não sabe, o atributo rendered (que por padrão é true) dos componentes é responsável por tornar o componente visível ou não para o usuário, ou seja, sempre que este atributo for true o componente poderá gerar código XHTML para ser exibido na página. Caso ele seja false, normalmente, nenhum código XHTML é gerado. Vale lembrar que mesmo o rendered sendo false o componente ainda existe na árvore de componentes no lado servidor.

Não é interessante tentar rerenderizar um componente com rendered dinâmico pois quando o atributo rendered é avaliado para false o componente (código XHTML na verdade) não aparece na árvore DOM da página. Sendo, após repintar este mesmo componente (agora com rendered avaliado para true) o código JavaScript do Richfaces responsável por atualizar o bloco de código onde se encontraria o componente na árvore DOM não consegue achar a posição exata para repintar na página (para injetar o novo código XHTML), já que o código XHTML do componente não existe mais. Então, no final, o Javascript do Richfaces acaba simplesmente não fazendo nada.

Por isso, quando você repinta um componente parent qualquer (que sempre tem o atributo rendered avaliado para true) o Richfaces consegue encontrar a posição exata deste componente na página e injetar o novo código XHTML, e como o componente com rendered dinâmico é um nó (filho) do componente parent ele também é repintado.

Concluindo

O problema é simples e a solução mais simples ainda! Nada como ler a documentação do seu conjunto de componentes favorito para entender e evitar este problema. Vale salientar que este problema ocorre com praticamente todos os conjuntos de componentes Ajax.

Acho que para um assunto simples como este eu falei mais do que devia. Espero que tenha ficado claro ou ao menos tenha dado uma luz para quem está enfrentando essa dor de cabeça. Caso queria saber mais sobre o assunto você poderá consultar a documentação do Richfaces, mais especificamente os tópicos 5.5 e 5.6.1.

Enfim, estou feliz por ter tirado mais um post antigo da minha lista de drafts! Atualmente minha rotina diária não tem me permitido investir o tempo que eu gostaria para blogar, mas cá estamos nós novamente, certo? 🙂

45 Replies to “Problema do rendered dinâmico com JSF”

  1. Ótimo Post Ponte.

    Essa dúvida com certeza está no “TOP 10” da lista de discussão Javasf.

  2. Concordo com o Rafael Souza. Confesso que já tive a ideia de criar um post sobre isso, mas acabei relaxando e ficou pra trás.

    Já deixei nos favoritos para, vez ou outra, responder com o link.

    Parabéns.

  3. Tive este mesmo problema e não imaginava que fosse isso. Ótimo post.
    Tinha contornado este problema usando o readonly do componente e deu certo.

  4. Ótima explicação Ponte!

    Se o camarada não estiver usando o Firebug ficará de cabelo branco com esse problema.

    Fico feliz de ver você blogando denovo.
    Sem dúvida aqui é uma ótima fonte de conhecimento.

    Abraço. (:

  5. Legal Rafael! Junto com esse problema que você comentou, um outro que eu vejo acontecer com frequência, e que também é bem simples é o desenvolvedor esquecer de repintar um modalPanel com conteúdo dinãmico antes de exibí-lo.

    Aí o modal é mostrado desatualizado pois o cara esquece que ele já estava renderizado antes da ação.

    Ou então ele até faz um reRender, mas abre o modal no onclick e entao o modal aparece desatualizado e depois atualiza na cara do usuário, sendo que mandar abrir no oncomplete resolve o problema.

    Esse problema que você comentou, mais esquecer de atualizar, ou atualizar o modal depois de aberto são os errinhos mais bestas mas que tomam tempo de quem está começando.

  6. Cara, muito obrigado, eu realmente estou fazendo aplicações muito melhores. Parabéns pelo conteúdo

  7. Boa Rafael, muito boa a explicação, comecei a pouco tempo a programar com JSF e estava tendo muito problema nas renderizações, com esse post, consegui entender melhor como posso solucionar esses problemas.
    Parabéns.

  8. Olá Rafael,

    Já faz algum tempo que acompanhamos o seu Blog. Assim como esse último post, conseguimos encontrar dicas úteis aqui. Parabéns pela organização e qualidade do conteúdo!
    “TEAM = Together Everyone Achieves More”
    Esse é o ponto…
    De tanto usar Desenvolvimento Ágil, criamos uma ferramenta web de Scrum brasileira!
    Demos o nome de Scrumhalf (www.scrumhalf.com.br) e gostaríamos muito de contar com a sua opinião – existe um plano gratuito que dá para testar a vontade.
    Qualquer dúvida ou problema é só falar!
    Ok?

    Um abraço,
    Equipe GPE.

  9. Bom dia Rafael.

    Parabéns, mais um ótimo post! Português claro, conciso e direto! =D

    Estou com um problema e talvez você possa me ajudar novamente 😉

    Minha aplicação é baseada no teu esquema de “layout” (Utilizando AJAX com JSF de maneira eficiente) e ela tem um selectOnMenu q após escolhido deveria repintar via ajax somente um input mas esta repitando todo o form, ou seja, tudo é enviado ao server.

    Você tem alguma ideia para resolver isso?

    Muito obrigado, novamente. =S

    []’s

    
       
       
    
    
       
          
          
       
    
    
  10. ah, segue o código problemático:

    muito obrigado.

  11. Olá Charles,

    Provavelmente existe um componente a4j:outputPanel ao redor da página (ou form) com o atributo ajaxRendered como true. Ou seja, após qualquer evento AJAX esse componente irá sempre se auto-repintar.

    Tente verificar se este não é o problema.

    Um abraço.

  12. Obrigado novamente pela resposta/ajuda Rafael.

    Realmente há mas queria saber se você tinha alguma saida para utilizar teu exemplo com a juntamente com componentes ajax para área e/ou itens.

    Então, o excelente exemplo/utilização demonstrada em teu artigo: “Utilizando AJAX com JSF de maneira eficiente”, não é adequado para situações onde há a utilização de a4j:region, a4j:support, etc pois sempre será atualizada todo o form e não só a área/objeto desejado.

    Muito obrigado.
    []’s

  13. Olá Charles,

    O artigo deu um modelo simples de projeto para servir como prova de conceito apenas. No artigo não me preocupei com detalhes nem casualidades que poderiam vir a acontecer.

    Mas como solução, se você pretende manter o a4j:outputPanel, é você limitar os componentes que devem ser repintados após qualquer evento Ajax. Você pode usar o atributo limitToList=true, assim todos os componentes com atributo ajaxRendered=true serão ignorados.

    No mais, dá um lida na documentação para melhor entendimento. Tem tudo o que você precisa lá.

  14. Tem como passar pelo rendered do JSF 2.0 um STRING “ADMIN USER VIEW” ate o bean e este checar se esta string contains(“VIEW”) tornando assim o componente visivel ou nao ? Aqui tenho um arquivo de propriedade que possui uma chave chamada ROLE , gostaria de passar ela para um Bean onde este tem meu usuario com seu devido nivel de acesso , assim se um contiver o outro retorno verdadeiro tornando o menu visivel ou nao ? Pode me ajudar? Obrigado, André Souza

  15. Fala Rafael, excelente artigo me ajudou muito, mas ainda estou com o seguinte problema (rsrs):

    Preciso que um componente renderize ele mesmo… visualmente tudo funciona, mas meus botões param de funcionar… No caso é um fileUpload, que está dentro de um modal. Gostaria que ao anexar um arquivo aparecesse as opções “visualizar” e “remover” no lugar do componente do fileupload.

    obs.: já tentei retirar o rendered={} dos componentes e colocar em panelGroups, mas não resolveu… Obrigado…

  16. Ótimo post. Eu mesma já tive um problema parecido e na época resolvi lendo o manual do Richfaces, que explicava basicamente isso.

    Parabéns pelo blog, é mto importante pra comunidade que (ainda) usa jsf! 😀

    []s
    Lívia

  17. Resolvi amigo na verdade tinha feito algo errado muito obrigado!!!

  18. Muito obrigado,

    Depois de uma pesquisa acabei achando esta comunidade e seguidamente esta resposta. Já sabia que devia fazer da forma que disse mas nunca tinha compreendido o problema, agora fiz a ponte 😉 .

    Mais uma vez, obrigado.

    Cumps.

  19. Mano!!!
    Você salvou a minha vida.
    Tu num tem idéia o quanto procurei essa resposta.
    Muito obrigado mesmo.
    Obrigado Pai, pela existencia desse irmão.
    Que Deus o abençoe muito e te faça prosperar.

  20. Rafael, veja se vc pode me ajudar.

    Tenho um formulario, que dentro dele possui um e dentro desse panel tenho varios campos (nome, cpf_cnpj, etc). Dentro desse panel também tenho o código abaixo:

    Ainda dentro do form, mas fora do panel, tenho:

    Meu objetivo é, quando alternar entre pessoa física e jurídica, o campo razao social ser exibido ou não. Bom, a aparência funciona normalmente, ou seja, o inputText razão social é exibido e ocultado normalmente. Contudo, quando ele está sendo exibido, eu preencho os dados nele e clico num botão que tem o seguinte codigo para salvar os dados no banco:

    Neste ponto, todos as informações são salvar no banco, exceto o que foi digitado em razao social (o valor dele vai null pro banco). Já quebrei a kbça aqui e nao achei o motivo ainda. Será que vc, ou alguém ai, pode me ajudar??

  21. Opa, Rafael!
    Cara, o meu problema é o seguinte: Estou com um problema de Re-Renderização nas demais vezes, ou seja, o usuário informa (num combobox) a versão e num campo data (rich:calendar) deve preencher (automaticamente) com a data prevista de liberação do sistema, está acontecendo que a primeira versão que informa a data é populada corretamente mas se resolver trocar a versão a data não é mais alterada. Ex.: versão 6.00 data 15/11/2012 e versão 6.01 data 30/11/2012, na primeira alteração do combo setando com a versão 6.00 a data é preenchida com data correspondente, mas se alterar para 6.01 permanece a mesma data.

    Sabe me dizer onde estou errando?

    Valeu!

  22. Muitooo obrigada!
    Vc salvou minha vida!
    Valeu mesmo!

  23. Vez ou outra eu apanhava com isso e acaba resolvendo meio que por acaso. Agora, lição aprendida. 🙂
    Valeu!!

  24. Bom dia,

    Estou com um problema e o seu post acredito que se enquadre no meu caso.

    Criei em uma página um cadastro onde ao alterar um selectOneMenu ele preenche informações em outro selectOneMenu via ajax, até então a página estava funcionando normalmente.

    Alterei esse cadastro que está montado na estrutura do panelGrid e eu estou utilizando o rendered do panelGrid para esconder ao entrar na página e fazer aparecer somente quando o usuário clique em um botão “novo” para cadastrar, mas fazendo dessa forma ao alterar o selectOneMenu ele não aciona o código ajax, provavelmente porque o código não foi gerado por causa da do rendered, se eu não esconder ele ao criar a página funciona normalmente.

    Não tenho como testar no momento, mas saberia me dizer se essa sua solução funcionaria no meu caso?

    Agradeço desde já ! Abraço

  25. Amigo se você estivesse aqui perto eu te daria um abraço, muito Obrigado !

  26. Cara, estava tentando resolver esse problema faz dois dias, e você simplesmente clareou tudo aqui para mim… Top demais….

  27. Seu post é de 2010 e estamos em 2015. mesmo assim resolveu meu problema com o PrimeFaces. Parabéns.

  28. Muito bem explicado, já estava perdendo minhas esperanças..

  29. Que bom, Alisson!

    Fico feliz que alguns posts antigos ainda ajudam muitos desenvolvedores nas entre-linhas do JSF.

    Um forte abraço!

  30. Estou com o mesmo problema do Paulo. A visualização do componente funciona, mas carregar a tela novamente o link não chama a função no bean.

    Meu código.

    Não sei como resolver.

  31. Olá Rafael, creio que eu tenho um problema de alternar a atualização de paineis “a4j:outputPanel” na mesma página , ou seja ao clicar no primeiro “commandLinkPreviewPictures” “ok” mas ao clicar no segundo não exibe nada, mas se clicar no segundo novamente atualiza ok parece que tem um delay.
    Primeiro a4j:commandLink:
    <a4j:commandLink id="commandLinkPreviewPictures" action="#{homeController.atualizarPreviewPictures}" reRender="pnlPreviewPictures"
    para atualizar o componente
    a4j:outputPanel:

    Segundo a4j:commandLink:
    <a4j:commandLink id="commandLinkPreviewPosts" action="#{homeController.atualizarPreviewPosts}" reRender="pnlPreviewPosts"
    para atualizar o componente
    a4j:outputPanel:

    mas não fui feliz, será que entendi alguma coisa errado? teria alguma luz?
    Muito Obrigado

  32. Olá,

    O código do componente a4j:outputPanel não apareceu no seu comentário. De qualquer forma, qual o escopo do seu managed bean?

  33. Rafael,
    Agora entendi a sua explicação eu tinha feito um painel com 0}”>, ai foi só retirar e funcionou:
    Antes:

    0}”>……
    Depois:

    …..
    Muito Obrigado

    Obs: a minha minha session=@SessionScoped se tiver alguma observação eu agradeceria.

  34. Cara, tenho um certo gosto por JSF, mas a cada dia que estudo mais, entendo o por que tantos o difamam. É necessário um alto conhecimento dos por menores da implementação e na era da agilidade e “é pra ontem” muitos não tem tempo para aprender a fundo o funcionamento.

    Não vou desistir de aprender! Agradeço e conto com sua ajuda com estes post de ótima qualidade e riqueza de conteúdo.

    Parabéns!!!

  35. Oi Vinicius,

    Não se engane, trocar JSF por outro framework web MVC vai te exigir tanto conhecimento quanto, principalmente com relação a HTTP, HTML, CSS e JavaScript – fora os detalhes do framework em si. Desenvolver para web é muito dificil comparado a mobile ou desktop, somos obrigados a saber e muitas vezes dominar inúmeras linguagens, conceitos e tecnologias. Não é moleza não!

    De qualquer forma, eu entendo seu ponto e até concordo. Continue com os estudos e aprenda as particularidades do JSF, como o ciclo de vida e escopos, que você já saberá mais do que muitos no mercado.

  36. Obrigado Rafael foi de grande valia sua orientação sobre o reRender, ajudou e muito no meu trabalho aqui! Um abraço amigo!

Deixe um comentário