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? :-)

Tags: , , , , , , , , , , , , , , , , , , , ,

22 Responses to “Problema do rendered dinâmico com JSF”

  1. Rafael Souza Says:

    Ótimo Post Ponte.

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

  2. Alexandre Silveira Says:

    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. Pablo Says:

    Parabéns, post muito útil.

  4. Gabriel Carvalho Says:

    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.

  5. Washington Botelho Says:

    Ó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. (:

  6. Gilliard Cordeiro Says:

    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.

  7. Bruno Borges Says:

    Seria interessante mesmo se o JSF já fizesse isso automático, ou que não exigisse outro componente.

    Postei um artigo sobre como o Wicket trata Ajax pra fazer o que foi apresentado aqui.

    http://blog.ladoservidor.com/2010/12/apache-wicket-ainda-mais-divertido.html#more

  8. JSF: Problemas da renderização dinâmica : JornalJava Says:

    [...] http://www.rponte.com.br/2010/12/01/problema-do-rendered-dinamico-com-jsf/ Tag: jsf • render • web Click here to cancel reply. Name [...]

  9. Rodrigo Says:

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

  10. Revton Says:

    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.

  11. GPE - Gestão de Processos Estratégicos e Tecnologia da Informação LTDA. Says:

    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.

  12. Charles Says:

    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
    [code]

    [/code]

  13. Charles Says:

    ah, segue o código problemático:

    muito obrigado.

  14. Rafael Ponte Says:

    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.

  15. Charles Says:

    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

  16. Rafael Ponte Says:

    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á.

  17. Andre Souza Says:

    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

  18. Paulo Says:

    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…

  19. Nara Says:

    uf.. obrigadaaa! ajudou bstt!

  20. Lívia Silva Santos Says:

    Ó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! :D

    []s
    Lívia

  21. Felipe Says:

    Fiz conforme o post mas não deu certo você pode me ajudar?

    Postei o código no GUJ:

    http://www.guj.com.br/java/148603-jsf—rerender-nao-funciona/2#1379195

  22. Felipe Says:

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

Leave a Reply