Home > JSF, JavaEE > view scope no JSF 2.0

view scope no JSF 2.0

October 24th, 2008

Eu uso JSF há um bom tempo e sempre fui um defensor de que JSF é muito bom, mas hoje em dia ainda não da pra usar ele sozinho, por isso mesmo uso JSF + Facelets + Seam. Acredito que com o que temos hoje usar JSF sozinho não é lá das coisas mais produtivas do mundo, e isso em alguns casos faz com que pessoas critiquem o JSF porque não querem adicionar mais dependências ao projeto e deixam Facelets e Seam de fora.

Pessoalmente eu acho isso quase o mesmo nível de querer fazer um projeto sem Hibernate pra não ter mais dependências no projeto. Tá certo que hoje o caso é diferente pois temos a JPA, mas se não a tivéssemos ainda, você faria um projeto de verdade só com JDBC só pra não ter essa dependência com Hibernate? Pois bem, muita gente faz isso com JSF. Mas assim como a JPA veio para deixar as pessoas mais tranquilas e menos apavoradas de ter um framework a mais na aplicação, o JSF 2.0 também vai dar uma forcinha nesse sentido. Com ele poderemos ter uma aplicação funcionando super bem sem adicionar outros frameworks para isso. E isso devido duas coisas: a inclusão do Facelets (já apresentada aqui), e o novo escopo view.

Erros de validação

O JSF faz um papel muito bem feito abstraindo a camada web. Conseguimos desenvolver uma aplicação com JSF sem colocar a mão em HttpServletRequest e outros Http* da vida. O serviço seria muito bem feito de ponta a ponta se não fosse por um detalhe: a falta de um escopo que abstraísse também o http como o restante faz. Até o JSF 1.2 parece que essa parte foi esquecida, e enquanto o Faces tratava os escopos já conhecidos de toda aplicação web, como request, session e application, deixava uma porta aberta para o surgimento de componentes como t:saveState do Tomahawk, e depois as famosas conversations, no Seam.

Mas para que conversation e saveState servem? Um dos maiores problemas do JSF é a quantidade de “erro de validação” que acabam “estourando” na cara do desenvolvedor. Claro que sabendo como o JSF trabalha nós conseguimos desenvolver sem problemas. Eu sempre comento que a regra número 1 é sempre sobrescrever o método equals. Seguir boas práticas nunca é demais. E com isso boa parte dos erros de validação simplesmente somem. Um dia eu paro pra explicar melhor isso. Mas existem erros que acontecem como os famosos combos em cascata e commandButtons/commandLinks que não executam poque estão dentro de dataTable’s que só são preenchidas depois de alguma ação.

Imagine que temos uma página de cadastro de endereço, onde existem dois combos, o primeiro para estado e o segundo para cidades. Mas inicialmente o combo de cidades vem vazio, somente depois que selecionamos o primeiro, uma ação é executada para buscar as cidades. Selecionamos então “MS”, acontece uma nova requisição, as cidades são mostradas, selecionamos “Campo Grande” e mandamos salvar. Nisso vem o famoso erro de validação no combo das cidades. Isso acontece porque pela natureza stateless do managed bean de escopo request que estamos usando, o JSF esquece que já foi feita a requisição que busca as cidades, e para ele quando clicamos em salvar é como se fosse a primeira requisição. Então o JSF pensa: “como esse cara tá submetendo uma cidade sendo que eu nem mostrei os valores desse combo ainda? Essa cara tá me sacaneando, vou tesourar essa requisição dele”. Mas como fugir disso então? Coloca tudo no escopo de sessão? A resposta para esse problema é a utilização de escopos mais refinados, não disponíveis (ainda) no JSF padrão.

Contextos mais “espertos”

Na minha opinião, uma das melhores coisas do Seam é a conversação. Um conversação é um “escopo” maior que um request e menor que a sessão do usuário, e que tem um início e fim definidos por nós. Quando iniciamos uma conversação o contexto “vira” statefull até que seja finalizada, quando então “vira” stateless novamente. Claro que o Seam é muito mais que isso, mas para o assunto do post isso é o mais relevante. Fora Conversation, o Seam tem um escopo Page, cujo funcionamento é basicamente o mesmo do saveState. Dessa forma, o contexto é statefull enquanto submetemos para a mesma página. Uma conversação é algo mais refinado, que pode envolver várias telas, mas para a grande maioria dos casos essas várias requisições que precisamos manter o estado são feitas para mesma tela, como nos exemplos dos combos de estado e cidade e da dataTable resultando de filtro com uma action dentro.

View Scope

Exatamente nesse caso que o escopo view vem suprir essa carência do JSF padrão. Um managed bean com esse escopo permanece na memória enquanto submetemos para a mesma tela. Não quero dizer que você nunca mais vai precisar de outras coisas, ou que o Seam nada mais é do que fazer de outra forma o que um saveState já fazia antes, mas sim que agora para a maioria das aplicações já poderemos usar apenas JSF 2 sem ter que usar outras ferramentas para nos auxiliar. Claro que um conjunto de componentes ricos sempre será bem vindo, e que certamente iremos continuar querendo usar algumas coisas mais específicas que o Seam/WebBeans nos oferecem. Mas o grande ganho é que para o básico não precisamos de mais nada, coisa que infelizmente hoje, com JSF 1.2, ainda não acontece.

Como já escrevi bastante, vamos a um exemplo. O nosso managed bean de escopo view ficaria assim:

@ManagedBean(name="enderecoBean")
@ViewScoped
public class EnderecoBean {
 
    @PostContruct
    public void construct() {
        //chamado só quando o managed bean é colocado no escopo view,
        //e não a cada requisição como acontecia com o escopo request
    }
 
    @PreDestroy
    public void destroy() {
        //chamado quando outra view for chamada através do UIViewRoot.setViewId(String viewId)
    }
 
}

Com esse escopo o JSF cria o managed bean na primeira requisição onde ele for preciso (se for lazy), e só o remove da memória quando for chamado o método UIViewRoot.setViewId(String viewId) para indicar que iremos trabalhar com outra página.

Gilliard Cordeiro JSF, JavaEE , ,

  1. Rafael Souza
    October 29th, 2008 at 22:27 | #1

    Ótimo post Gilliard, muito obrigado por trazer as novidades do JSF 2 para nós!

  2. Rafael Rossignol Felipe
    October 30th, 2008 at 13:29 | #2

    Muito legal o post, gostei da linguagem do server pensando
    rs

  3. November 1st, 2008 at 10:01 | #3

    Excelente post Gilliard,

    A falta de escopos de conversação na especificação Jsf sempre foi um problema, a utilização de mecanismos para soluciona-lo sempre existiram, mas sempre nos obrigou a adicionar um novo framework/componente ao projeto para resolver um problema que não deveria existir na especificação.

    A falta dele causa vários problemas, entre os mais comuns como você mesmo citou tem o problema das combos aninhadas (ou não), dos commandLinks de uma datatable entre vários outros, contudo são solucionáveis mesmo com o escopo de request e um razoável conhecimento do ciclo de vida do Jsf.

    A idéia do Jsf é encapsular toda a complexidade do http através de componentes e eventos, nos dando a impressão de um ambiente stateful, mas isso não é nada fácil, e piora mais ainda quando tentamos integrar Jsf a um framework de persistência ORM, aí sim as coisas pioram.

    O Seam não só resolve o problema de conversação entre managed beans, mas sim a conversação de escopo de persistência também, fazendo assim a integração do Jsf e JPA mais simples e prática! Isso é formidável!

    O problema de validação é um dos problemas mais comuns que vejo entre os novos desenvolvedores que usam Jsf, eu já bloguei sobre isso faz algum tempo,
    http://www.rponte.com.br/2008/02/01/selectonemenu-converter-erro-de-validacao/

    Enfim, gostei do post e principalmente de ter a certeza que JSF2.0 trará no mínimo um escopo “page” que será útil em praticamente quase todas as aplicações, principalmente se as páginas forem construídas de maneira não tradicional.

    Abraços e parabéns pelo post.

  4. November 3rd, 2008 at 07:03 | #4

    Obrigado a todos,
    e valeu pelo link Rafael, com ele eu nem preciso mais escrever aquela parte que mencionei no post que escreveria depois, pois era pra descrever exatamente o caso que você escreveu no seu post, muito bom!

    A parte de conseguir resolver tudo com JSF entendendo o ciclo de vida é complicado. Infelizmente cheguei a conclusão que poucas pessoas que desenvolvem com JSF realmente entendem o seu ciclo de vida, o que acaba causando vários problemas. Eu acabei atropelando um pouquinho essa parte para não alongar demais o post pois pretendia escrever melhor depois, mas graças ao teu post não vou precisar mais :D

    Mas a grande vantagem do JSF 2.0, na minha opinião, é que ele já virá mais completo de série, possibilitando que seja possível fazer uma aplicação razoavelmente bem feita com JSF sem conhecer tanto assim do cilco de vida. Mas sobrescrever equals e hashCode continua sendo necessário. Como eu falei no post, nunca é demais seguir boas práticas, pois elas acabam nos livrando de problemas que nem sabíamos que iríamos passar sem elas ;)

  5. Wiliam Witter
    January 30th, 2009 at 14:06 | #5

    E ai Gilliard pesquisando aqui no Google sobre JSF 2.0 axei seu blog [:D], gostei… valeu.

  6. hugo
    July 17th, 2009 at 23:58 | #6

    Obrigado pelo blog Gilliard.
    Eu tô com dificuldades no ViewScoped. Em qual momento exatamente ele remove o bean? tem como Exemplificar?
    Pois mesmo depois de tudo feito e saído da página, se eu acessá-la diretamente pela url o formulário volta todo preenchido.

  7. Marcelo
    January 19th, 2010 at 08:03 | #7

    Gilliard, estou tendo o mesmo problema que o amigo hugo. Configurei um bean com @ViewScoped e dois metodos com @PostConstruct e @PreDestroy, acessando o bean sempre do mesmo xhtml, e retornando para o mesmo, o JSF continua tratando o bean como request e recebo a seguinte informação no console:
    INICIANDO BEAN: com.controle.TesteBean
    19/01/2010 08:56:15 javax.faces.component.UIViewRoot$ViewMap put
    WARNING: Definindo valor de atributo não serializável em ViewMap

    Implementei serializable no bean para testar, mas ainda continua tratando como request. Ao mudar para @SessionScoped, funciona como deve, dentro do escopo de sessão.

    Any thoughts? :)

  8. March 17th, 2010 at 17:05 | #8

    Marcelo, como faz muito tempo que você postou essa dúvida, queria saber se ainda está com esse problema. Se estiver vou dar uma testada aqui e te falo, se não, qual era o problema?

  9. Vanessa Schissato
    June 1st, 2010 at 10:36 | #9

    Também não estou conseguindo usar o escopo view. Me dá uma mensagem de que deve ser serializável, mas se torno serializável, não funciona mesmo assim.

  10. June 1st, 2010 at 18:10 | #10

    @Vanessa Schissato
    Qual versão do JSF você está usando? O que exatamente acontece? Como você está fazendo para testar? Essa mensagem que ele dá não seria somente um warn? Se for uma exception, você chegou a ver na stack se não tem outro motivo?
    São muitas perguntas, eu sei, mas como não estou vendo você testar tenho que perguntar.

    Atualmente estou utilizando o mojarra 2.0.2.FCS com primefaces 2.0.2 e o escopo funciona normalmente. O que pude perceber de problema é com a paginação ajax do componente dataTable do primefaces, que chama o método anotado com @PreDestroy… mas mesmo assim não terde os valores do managed bean.

  1. February 15th, 2009 at 00:05 | #1
  2. October 19th, 2009 at 14:39 | #2