Renderizando automaticamente componentes JSF em requisições AJAX
Aprenda como repintar componentes JSF em requisições AJAX de forma programática
Disparar requisições AJAX com JSF 2 é uma mera questão de adicionar a tag f:ajax
nos componentes. O desenvolvedor não precisa sequer escrever uma linha de JavaScript ou se preocupar com a compatibilidade entre navegadores. Graças a esta simplicidade conseguimos melhorar a usabilidade das aplicações e diminuir o tráfego de dados na rede, o que melhora significativamente a performance das páginas, principalmente na percepção do usuário.
Quando disparamos uma requisição AJAX em JSF nós temos duas preocupações básicas, a primeira é definir o que será submetido e processado no lado servidor, enquanto a segunda é definir o que será repintado após a requisição AJAX ser concluída. Por exemplo, para submeter um formulário e logo em seguida repintá-lo, bastaria ter um código do tipo:
<h:commandButton value="Gravar" action="#{bean.grava}">
<f:ajax execute="@form" render="@form" />
</h:commandButton>
Mas muitas vezes, além de repintar o formulário, nós também precisamos repintar as mensagens de erro e sucesso da aplicação para que elas sejam exibidas para o usuário. No caso acima, se as mensagens estiverem ao lado de cada input dentro do formulário nós não teremos problema, mas em alguns layouts de sistemas é comum que elas fiquem no topo da página, ou seja, fora do formulário:
<html ...>
<h:head>...</h:head>
<h:body>
<!-- componente de mensagens fora do formulário -->
<h:messages id="mensagens" errorClass="erro" />
<h:form>
<!-- conteúdo do formulário -->
</h:form>
</h:body>
</html>
Para resolver isso, podemos indicar na tag f:ajax
que um determinado componente também deve ser repintado, bastando apenas passar o ID do componente no atributo render
da tag, como a seguir:
<f:ajax execute="@form" render="@form :mensagens" />
O problema é que as mensagens existem em praticamente todas as páginas de um sistema, e o desenvolvedor teria que lembrar de informar o ID do componente de mensagens em todas as requisições AJAX da aplicação. Se por algum motivo a forma como as mensagens são exibidas mude, nós teríamos que revisar todas as páginas do sistema para ajustar a tag f:ajax
- some a isso a possibilidade do desenvolvedor esquecer de repintar as mensagens e temos um problema maior ainda.
O ideal é que as mensagens sempre fossem repintadas automaticamente a cada requisição AJAX, dessa forma o desenvolvedor não teria que se preocupar com elas. A maneira mais fácil de resolver isso é utilizando algum conjunto de componentes, como o Primefaces, por exemplo. Com ele podemos usar o componente p:outputPanel
juntamente com o atributo autoUpdate
setado para true
, como no código abaixo:
<p:outputPanel autoUpdate="true">
<h:messages id="mensagens" errorClass="erro" />
</p:outputPanel>
Se formos esperto, nós podemos inclusive usar o componente p:messages
ou p:growl
do Primefaces, na qual já possuem suporte a atualização automática:
<p:messages autoUpdate="true" />
E se eu não uso ou não posso usar Primefaces ou tenho um mecanismo de exibição de mensagens bem peculiar?
Repintando as mensagens programaticamente
A atualização de mensagens ou mesmo de qualquer componente da página também pode ser obtida de forma programática através da API do próprio JSF 2 ou de algum conjunto de componentes. No caso do JSF 2, nós podemos usar a API de atualização parcial de páginas PartialViewContext
, como a seguir:
PartialViewContext pvc = facesContext.getPartialViewContext();
pvc.getRenderIds().add(":mensagens");
Basicamente, invocamos o método getRenderIds
para obter a coleção dos IDs dos componentes que serão atualizados na requisição atual e, logo em seguida, adicionamos o ID do nosso componente de mensagens. No fim, todos estes componentes serão atualizados na última fase do ciclo de vida. De forma similar, também podemos definir quais componentes serão processados durante o ciclo de vida através da coleção retornada pelo método getExecuteIds
.
Com a API, podemos decidir o que repintar na página de forma dinâmica dentro das actions do managed bean, tirando assim esta decisão de dentro das páginas, por exemplo. Este mesmo código poderia ser colocado numa classe FacesUtils
para ter uma melhor reutilização de código.
Também conseguimos atualizar componentes programaticamente com a API do Primefaces:
RequestContext.getCurrentInstance().update(":mensagens");
Ou com a API do OmniFaces:
Ajax.update(":mensagens");
O conceito de atualização parcial de páginas programaticamente não é de hoje nem do JSF 2, ele já existia e era muito bem implementado na biblioteca de componentes MyFaces Trinidad, ainda na época do JSF 1.2.
Repintando automaticamente as mensagens
Para que a atualização das mensagens ocorra de forma automática sempre que houver uma requisição AJAX, podemos implementar um Phase Listener que se encarregará de adicionar o ID do componente h:messages
no PartialViewContext
logo após um evento na página. Portanto, o código do nosso listener não seria muito diferente deste:
public class AtulizacaoAutomaticaDeMensagens implements PhaseListener {
@Override
public void beforePhase(PhaseEvent event) {
FacesContext.getCurrentInstance().getPartialViewContext()
.getRenderIds().add(":mensagens");
}
@Override
public void afterPhase(PhaseEvent event) {
}
@Override
public PhaseId getPhaseId() {
return PhaseId.RENDER_RESPONSE;
}
}
Dessa forma, sempre que uma requisição AJAX for disparada por algum componente da página o listener vai garantir que as mensagens da aplicação serão atualizadas automaticamente, sem a necessidade de usar alguma biblioteca de componentes.
Como podemos ver, com um pouco de conhecimento da nova API do JSF 2, de recursos como Phase Listener e um pouco do entendimento do ciclo de vida , nós podemos criar soluções simples dentro da nossa aplicação que podem gerar um ganho considerável de produtividade e evitar problemas comuns do dia a dia. Se quiser conhecer mais um pouco sobre AJAX, Phase Listener e principalmente entender de ponta-a-ponta o ciclo de vida do JSF, não perca o nosso curso de JSF 2 com Spring.
E você, como tem feito a atualização automática das mensagens da sua aplicação ou dos componentes das páginas?
Desenvolvedor e instrutor na TriadWorks