JSF 2 + PrimeFaces - Passando um objeto da visão para MB

Boa tarde a todos.

Pessoal, gostaria de saber qual seria a melhor forma que eu pudesse passar um objeto do JSF para o bean que tem escopo de view. Já tentei utilizar o <f:param> mas ele é utilizado apenas para tipos primitivos e String e portanto retornava null no objeto do bean. A segunda alternativa que vi foi utilizar o <f:setPropertyActionListener> porém tenho minhas duvidas se isso funcionaria com um bean de escopo de visão. A ultima alternativa que vejo seria utiliza o tomahawk(<t:saveState> porém isso seria somente em ultimo caso.

No caso da segunda opção, eu tenho no meu código algo como:

<f:setPropertyActionListener target="#{meuBean.meuObjeto}" value="#{flash.meuObjeto}" />

Sendo meu objeto com escopo flash.

Se alguém puder me dar uma dica sobre como seria a melhor forma de fazer isso agradeço.

 Estranho essa questão, se um objeto está no escopo View então quer dizer que enquanto estiver submetendo para aquela mesma página você ainda terá controle sobre tal. Assim imagino que uma preocupação deste tipo seria mais no escopo de requisição.
 Poderia explicar melhor a situação, exemplificando a situação.

Olá ericktb.

Não existe problema em utilizar o f:setPropertyActionListener com o bean em escopo de view. O que pode acontecer é que como o valor vem da flash (que dura, por default, somente uma requisição), seu bean não receba o valor pois ele já foi limpado.

Existe uma forma de solicitar que o flash dure por mais uma requisição, que é o flash.keep(), talvez resolva seu problema se for o caso.

Outra idéia é pegar o flash direto do bean, assim:

Object meuObjeto = (Object) FacesContext.getCurrentInstance().getExternalContext().getFlash().get("meuObjeto");

Não me lembro se a flash aceita somente primitivos, mas acho que aceita objetos também.

Existe também o f:attribute, que adiciona o atributo ao componente.

Você pode encontrar alguns exemplos de códigos desse tipo aqui:

http://www.oio.de/public/java/jsf-best-practices-javaserver-faces-session-tips.htm

Até +!

[quote=hsborges] Estranho essa questão, se um objeto está no escopo View então quer dizer que enquanto estiver submetendo para aquela mesma página você ainda terá controle sobre tal. Assim imagino que uma preocupação deste tipo seria mais no escopo de requisição.
Poderia explicar melhor a situação, exemplificando a situação.[/quote]

Olá hsborges. Agradeço a resposta.

O cenário é o seguinte. Tenho uma tela de listagem de objetos e ao clicar em editar devo ir para uma nova página (controlado por outro bean). Assim no primeiro bean, antes de redirecionar, guardo o meu objeto no escopo de flash e ao passar para a página de editar, gostaria de repassar esse objeto para o novo bean (esse objeto seria repassado para o bean de edição em um determinado evento do usuário) . O fato é que são dois beans diferentes com escopo de view. Espero que tenha compreendido melhor a situação e peço desculpas se deixei meu problema obscuro.

Note que existe a necessidade de se ter um bean para cada página, por isso o problema.

Obs: aceito sugestões se a maneira como estou fazendo não for a mais adequada.

[quote=vinnysoft]Olá ericktb.

Não existe problema em utilizar o f:setPropertyActionListener com o bean em escopo de view. O que pode acontecer é que como o valor vem da flash (que dura, por default, somente uma requisição), seu bean não receba o valor pois ele já foi limpado.

Existe uma forma de solicitar que o flash dure por mais uma requisição, que é o flash.keep(), talvez resolva seu problema se for o caso.

Outra idéia é pegar o flash direto do bean, assim:

Object meuObjeto = (Object) FacesContext.getCurrentInstance().getExternalContext().getFlash().get("meuObjeto");

Não me lembro se a flash aceita somente primitivos, mas acho que aceita objetos também.

Existe também o f:attribute, que adiciona o atributo ao componente.

Você pode encontrar alguns exemplos de códigos desse tipo aqui:

http://www.oio.de/public/java/jsf-best-practices-javaserver-faces-session-tips.htm

Até +![/quote]

Olá vinnysoft. Também agradeço pela ajuda.

Estou utilizando o escopo flash para um objeto e não tipo primitivo e funciona. A questão é como eu expliquei na resposta anterior. Tenho um objeto no bean de listagem que guardo no escopo de flash. Após redirecionar a página para a página de edição gostaria de repassar esse objeto flash da página de edição para o bean de edição. Primeiro tentei utilizar o f:param e não consegui.

Também tentei utilizar

Object meuObjeto = (Object) FacesContext.getCurrentInstance().getExternalContext().getFlash().get("meuObjeto");

mas sempre me retornou nulo e isso eu achei bem estranho e ainda estou tentando entender, ao que me parece ele dura durante o redirecionamento de uma tela e após ser utilizado, ele é limpo. Acredito que a saída seja realmente o flash.keep(). Devo utilizá-lo no EL ? #{flash.keep.meuObjeto}

Amigo vamos começa com um pouco de teoria.
View Scoped : O escopo view foi adicionado no JSF 2. A ideia é manter determinados dados enquanto o usuário
não mudar de tela. As instância dos managed beansemescopo view são eliminadas somente quando
há uma navegação entre telas.

Flash Scoped : Ele é responsável por guarda um objeto, para ser transportado a uma outra Bean.

Mas como está usando o View Scoped você terá que adicionar o Flash no get de seu objeto dessa forma.


// GET E SET DO FLASH
public Object getFlashParameter(String name){
		return FacesContext.getCurrentInstance().getExternalContext().getFlash().get(name);
	}
	
public void setFalshParameter(String name, Object value){
		FacesContext.getCurrentInstance().getExternalContext().getFlash().put(name, value);
	}

Agora o objeto que será setado com o Flash

// GET E SET DA PESSOA
public Pessoa getPessoa() {
		Pessoa pessoaFlash = (Pessoa)getFlashParameter("pessoa");
		if(pessoaFlash != null){
			pessoa = pessoaFlash;
		}
		return pessoa;
	}

public void setPessoa(Pessoa pessoa) {
		this.pessoa = pessoa;
	}

Amigo eu usei um exemplo com a classe Pessoa espero ter ajudado.

Olá ericktb.

Sim você pode utilizar o keep no EL dessa forma mesmo.

Se vc não conseguir utilizar o flash, outra idéia seria utilizar o mapa de parâmetros do escopo da sessão, que dura enquanto o usuário estiver com a sessão ativa. Então vc não passaria o objeto para o outro bean, mas sim o ID do seu objeto, e este outro bean trataria de receber o id no @PostConstruct e buscaria ele do seu BD/DAO/Negócio/etc para exibir na tela.

Ex (supondo que sua lista está em um dataTable):

Pagina da Lista


<h:commandLink value="Editar" action="listaBean.editar">
      <f:param name="id" value="#{varObjeto.id}"/>
</h:commandLink>

ListaBean

public String editar()
    {
        //Pega o id do objeto enviado no click do link "Editar"
        long id = Long.valueOf(FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("id"));
        //Insere o id no escopo de sessão
        FacesContext.getCurrentInstance().getExternalContext().getSessionMap().put("meuObjetoId", id);
        //Retorna o endereço da pagina de edicao para realizar a navegação...
        return "/paginaEdicao?faces-redirect=true";
    }

EdicaoBean


private Object meuObjeto;

    /**
     * Método executado na inicialização do Bean.
     */
    @PostConstruct
    public void inicializar()
    {
        try
        {
          if (FacesContext.getCurrentInstance().getExternalContext().getSessionMap().containsKey("meuObjetoId"))
            {
                //Recebe o ID informado
                long id = Long.parseLong(FacesContext.getCurrentInstance().getExternalContext().getSessionMap().get("meuObjetoId").toString());

                //Busca o objeto e preenche os valores na pagina
                meuObjeto = DAO.buscarMeuObjeto(id);

                //Remove o atributo da sessão para utilizar novamente.
                FacesContext.getCurrentInstance().getExternalContext().getSessionMap().remove("meuObjetoId");
            }
         }
        catch (Throwable ex)
        {
            //Tratamento de exceção...
        }
    }

//Getters and setters...

Espero ter ajudado!

Até +!

Edit 1:

Estive lendo sobre a flash do JSF 2, e me parece que é um pouco instável ainda. Por exemplo:

[list]Se vc estiver fazendo sua navegação entre os beans através de getApplication().getNavigationHandler().handleNavigation(), o valor da flash é limpado antes da hora (veja http://stackoverflow.com/questions/2874929/does-navigationhandler-handlenavigation-clear-the-flash).
[/list]
[list]Se vc estiver fazendo redirect entre páginas em diretórios diferentes, a flash não funciona também (veja http://stackoverflow.com/questions/9148798/object-in-flash-scope-is-not-available-after-redirect).
[/list]

Então, acho que poderia pensar em outro escopo.

Até +!

[quote=wesllhey]Amigo vamos começa com um pouco de teoria.
View Scoped : O escopo view foi adicionado no JSF 2. A ideia é manter determinados dados enquanto o usuário
não mudar de tela. As instância dos managed beansemescopo view são eliminadas somente quando
há uma navegação entre telas.

Flash Scoped : Ele é responsável por guarda um objeto, para ser transportado a uma outra Bean.

Mas como está usando o View Scoped você terá que adicionar o Flash no get de seu objeto dessa forma.


// GET E SET DO FLASH
public Object getFlashParameter(String name){
		return FacesContext.getCurrentInstance().getExternalContext().getFlash().get(name);
	}
	
public void setFalshParameter(String name, Object value){
		FacesContext.getCurrentInstance().getExternalContext().getFlash().put(name, value);
	}

Agora o objeto que será setado com o Flash

[code]
// GET E SET DA PESSOA
public Pessoa getPessoa() {
Pessoa pessoaFlash = (Pessoa)getFlashParameter(“pessoa”);
if(pessoaFlash != null){
pessoa = pessoaFlash;
}
return pessoa;
}

public void setPessoa(Pessoa pessoa) {
this.pessoa = pessoa;
}
[/code][/quote]

Olá wesllhey.

Tratam-se de dois beans diferentes , no primeiro eu dou um set no flash e no segundo bean eu quero dar um get no flash. Eu tentei utilizar o FacesContext.getCurrentInstance().getExternalContext().getFlash().get(name) em um método do segundo bean mas sempre me retornava nulo.

Você está dizendo que só funcionaria se eu colocasse no get do meu objeto por conta do escopo ser view ? Só funcionaria dessa maneira?

Agradeço a ajuda.

[quote=vinnysoft]Olá ericktb.

Sim você pode utilizar o keep no EL dessa forma mesmo.

Se vc não conseguir utilizar o flash, outra idéia seria utilizar o mapa de parâmetros do escopo da sessão, que dura enquanto o usuário estiver com a sessão ativa. Então vc não passaria o objeto para o outro bean, mas sim o ID do seu objeto, e este outro bean trataria de receber o id no @PostConstruct e buscaria ele do seu BD/DAO/Negócio/etc para exibir na tela.

Ex (supondo que sua lista está em um dataTable):

Pagina da Lista


<h:commandLink value="Editar" action="listaBean.editar">
      <f:param name="id" value="#{varObjeto.id}"/>
</h:commandLink>

ListaBean

public String editar()
    {
        //Pega o id do objeto enviado no click do link "Editar"
        long id = Long.valueOf(FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("id"));
        //Insere o id no escopo de sessão
        FacesContext.getCurrentInstance().getExternalContext().getSessionMap().put("meuObjetoId", id);
        //Retorna o endereço da pagina de edicao para realizar a navegação...
        return "/paginaEdicao?faces-redirect=true";
    }

EdicaoBean


private Object meuObjeto;

    /**
     * Método executado na inicialização do Bean.
     */
    @PostConstruct
    public void inicializar()
    {
        try
        {
          if (FacesContext.getCurrentInstance().getExternalContext().getSessionMap().containsKey("meuObjetoId"))
            {
                //Recebe o ID informado
                long id = Long.parseLong(FacesContext.getCurrentInstance().getExternalContext().getSessionMap().get("meuObjetoId").toString());

                //Busca o objeto e preenche os valores na pagina
                meuObjeto = DAO.buscarMeuObjeto(id);

                //Remove o atributo da sessão para utilizar novamente.
                FacesContext.getCurrentInstance().getExternalContext().getSessionMap().remove("meuObjetoId");
            }
         }
        catch (Throwable ex)
        {
            //Tratamento de exceção...
        }
    }

//Getters and setters...

Espero ter ajudado!

Até +!

Edit 1:

Estive lendo sobre a flash do JSF 2, e me parece que é um pouco instável ainda. Por exemplo:

[list]Se vc estiver fazendo sua navegação entre os beans através de getApplication().getNavigationHandler().handleNavigation(), o valor da flash é limpado antes da hora (veja http://stackoverflow.com/questions/2874929/does-navigationhandler-handlenavigation-clear-the-flash).
[/list]
[list]Se vc estiver fazendo redirect entre páginas em diretórios diferentes, a flash não funciona também (veja http://stackoverflow.com/questions/9148798/object-in-flash-scope-is-not-available-after-redirect).
[/list]

Então, acho que poderia pensar em outro escopo.

Até +!
[/quote]

Olá vinnysoft.

Eu já estou fazendo isso com o id do objeto no bean de listagem. Após clicar em editar, recupero o objeto a partir do seu id e o coloco no escopo de flash. Depois disso redireciono para a tela de edição e recupero o objeto na tela de edição com o flash. Já na tela de edição gostaria inserir um novo registro numa lista que esse objeto tem e para isso preciso recuperar a referência do objeto que estou editando no MB. Esse objeto está no escopo de flash e estou com dificuldade em recuperá-lo no bean de edição. Por algum motivo o flash está limpando.

Realmente eu já tinha lido sobre instabilidades do escopo de flash, mas esse escopo é necessário para que eu possa passar objetos durante o redirecionamento sem precisar usar escopo de sessão. Estou tendo dificuldades também por não estar utilizando nenhum framework como spring ou seam que abstraem alguns desses problemas.

Tentarei utilizar as estratégias que vocês me deram.

Agradeço a todos que se dispuseram a ajudar e colocarei aqui se o problema for resolvido assim que possível.