quarta-feira, 2 de agosto de 2006

[JSF] - DataTables, CommandLink's e CommandButton's

Aqueles que já tiveram o prazer de tentar fazer uma componente commandLink ou commandButtons funcionar dentro de uma componente ''dataTable'' utilizando o escopo request para o managed bean que armazena os dados exibidos pelo dataTable, sabem que não dá. O jeito é mudar o escopo do managed bean para session.

O problema com esta "solução" é que os dados perduram. Exemplificando: imagine que você está implementando um caso de uso que mostra uma lista de entidades que obedecem às restrições impostas por filtros de busca cujos valores são determinados pelo usuário. O comportamento esperado é que, ao acessar a página, não se mostre nenhum valor, e após o usuário ordenar a busca, os elementos sejam obtidos e exibidos. Simples assim. Com a "solução" acima, isto ocorre na primeira vez que o usuário acessa esta página. Porém, dentro da mesma sessão, nas próximas vezes que o usuário acessar a página, ele irá automaticamente visualizar os dados da última busca que ele realizou.

Um pouco de pesquisa, na tentativa de manter o escopo como request, me levou à seguinte descrição de bug (que no final mostrou não ser um bug) - javaserverfaces:issue 69.

Conforme entendi, o problema é que no momento do processamento da fase Apply request values, o managed bean com os dados utilizados pela dataTable ainda não foi inicializado (ele somente será na fase Render response). Desta forma, a decodificação desta componente não funcionará como esperado, e tampouco a decodificação das componentes filhas (incluindo os commandLink's e commandButton's). Por isso, nenhum método de ação associado a estes elementos será executado.

Chegamos então à seguinte conclusão: nossa aplicação só funciona se usarmos escopo session. Mas isto é necessário somente para os dados a serem exibidos pelo dataTable. Com isto, sugiro a seguinte solução:

  1. Crie um managed bean (requestBean) em escopo request para gerenciar os dados não associados ao dataTable, e para gerenciar as ações da sua página
  2. Crie um managed bean (sessionBean) em escopo session somente para armazenar os dados exibidos pelo dataTable
  3. Você deve atribuir um valor vazio para os dados do sessionBean no construtor do requestBean, e com o resultado da busca, sempre que o método que realiza a busca em requestBean for executado

Surge aí outro problema. Como alterar os dados de sessionBean a partir do requestBean? Basta usar o seguinte código dentro do requestBean:


FacesContext context = FacesContext.getCurrentInstance();
context.getApplication().
createValueBinding("#{SessionBean.data}").setValue( context, newData );


Você pode reparar que o EL #{SessionBean.data} é igual ao EL que você usa no seu JSP associado ao atributo value da sua componente dataTable (h:dataTable).

É uma solução trabalhosa. Muito melhor seria se o JSF funcionasse de forma um pouco mais intuitiva. Mas nada é perfeito ...

Nenhum comentário: