16 de jun de 2014

Modal Panels no JSF: como fazer.

          
Quando se fala em usabilidade de sistemas, as modal panels, ou também conhecidas como pop-ups, ou simplesmente modais, se tornam essenciais para facilitar pequenas e rápidas ações do usuário ou para o carregamento de novas informações à partir de alguma ação do sistema. No entanto, quando se fala de criar tais recursos utilizando JSF, de maneira a obter boa performance e fácil utilização, para a grande maioria programadores a tarefa se torna um tanto quanto complexa.

Este post visa, portanto, mostrar como fazer estas modais de uma maneira simples e prática.

Os principais desafios enfrentados pelos programadores são: 
  1. o carregamento das modais é lento;
  2. a abertura da modal só ocorre após a requisição feita ao servidor;
  3. após a requisição, para a abertura da modal, é necessário antes que toda a árvore de objetos do JSF seja atualizada;
  4. pouco feedback (mensagens de retorno) ao usuário sobre o carregamento ou gravação dos dados.


A solução:


Visando sanar a todos estes problemas, eis aqui uma solução passo-a-passo a que cheguei à conclusão, após pesquisar alguns métodos. Darei um exemplo de alteração de uma determinada data, mediante digitação da justificativa pelo usuário:



Passo 1: incluir a chamada no evento que abrirá a modal da seguinte forma:


<a4j:commandLink value="#{msgs.comando_geral_alterar}"
       actionListener="#{meuManagedBean.exibirAlteracao}"
       onclick="Richfaces.showModalPanel('painelAlteracaoDados');"
       reRender="painelDadosAlteracao"
       ajaxSingle="true">
</a4j:commandLink>


Neste caso estou chamando o Managed Bean, como "meuManagedBean", no qual terá a ação ajax a ser executada, confome colocado no atributo "actionListener ".  A modal abrirá instantaneamente com o evento onclick, que chamará um javascript para abrir a modal que chamei de "painelAlteracaoDados". O atributo reRender, renderizará o painel que está dentro da modal "painelDadosAlteracao " com o resultado da busca ajax. Conforme código do próximo passo abaixo.
  

Passo 2: criar a modal como um componente:


<?xml version="1.0" encoding="UTF-8" ?>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"

   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml"
 xmlns:ui="http://java.sun.com/jsf/facelets"
 xmlns:h="http://java.sun.com/jsf/html"
 xmlns:f="http://java.sun.com/jsf/core"
 xmlns:a4j="http://richfaces.org/a4j"
 xmlns:rich="http://richfaces.org/rich">
<ui:composition>

       <rich:modalPanel id="painelAlteracaoDados" autosized="true"
              zindex="2000">
              <f:facet name="header">
                     <h:panelGroup>
                             <h:outputText value="#{msgs.nome_tela_altera_dados}" />
                     </h:panelGroup>
              </f:facet>
              <f:facet name="controls">
                     <h:panelGroup>
                             <a4j:ajaxCommandButton id="hideImageAlteracaoDados" immediate="true"
                                    ajaxSingle="true" image="/imagens/close.png"
                                    action="#{CotacaoBean.alterarDisplayModalAlteracaoDados}" />
                             <rich:componentControl for="painelAlteracaoDados"
                                    attachTo="hideImageAlteracaoDados" operation="hide" event="onclick" />
                     </h:panelGroup>
              </f:facet>
              <rich:panel id="painelDadosAlteracaoDados" rendered="${rendered}">
                     <h:form id="formularioAlteracaoDados">
                             <a4j:region id="processamentoModalDados">

                                    <h:panelGrid columns="2" width="100%">
                                           <a4j:calendar id="dataAlteracao"
                                                  value="#{meuManagedBean.data}"
                                                  datePattern="dd/MM/yyyy HH:mm" enableManualInput="true" />
                                           <a4j:inputTextarea id="justificativaAlteracaoDados"
                                                  value="#{meuManagedBean.justificativa}" />
                                    </h:panelGrid>

                                    <rich:messages style="color:darkred"
                                           id="mensagemErroAlteracaoDados" />
                                    <h:inputHidden id="idAlteracao" value="#{meuManagedBean.id}" />
                    
                                    <a4j:ajaxCommandButton id="saveButtonAlteracaoDados"
                                           reRender="painelDadosAlteracaoDados, mensagemModal"
                                           value="#{msgs.comando_geral_salvar}"
                                           action="#{meuManagedBean.alterarDados}" ajaxSingle="true"
                                           process="dataAlteracao, justificativaAlteracaoDados, idAlteracao" />
                                   
                                    <a4j:ajaxCommandButton id="hideButtonAlteracaoDadosCancelar"
                                           value="#{msgs.comando_geral_fechar}"
                                           action="#{meuManagedBean.alterarDisplayModalAlteracaoDados}" ajaxSingle="true"
                                           immediate="true" />
                                   
                                    <rich:componentControl for="painelAlteracaoDados"
                                           attachTo="hideButtonAlteracaoDadosCancelar" operation="hide"
                                           event="onclick" />
                                    <a4j:status>
                                           <f:facet name="start">
                                                  Salvando...
                                           </f:facet>
                                    </a4j:status>
                             </a4j:region>
                     </h:form>
              </rich:panel>
       </rich:modalPanel>
</ui:composition>
</html>


Repare que estou criando a modal como um "ui:composition" para que a mesma seja um componente do Facelets. A renderização da data a ser alterada, se dará na região demarcada pela tag  "a4j:region". Isto indica ao JSF que não é necessário carregar toda a árvore de componentes uma vez que a modal já está aberta, mas somente deve recarregar o que nos interessa: os campos que foram populados pela requisição ajax.

Foi utilizado também a tag "a4j:status" para que, enquanto o salvamento das informações é processado, exiba a mensagem "salvando..." ao usuário.

Repare que o controle do carregamento das informações e fechamento da modal se dá pelo atributo "rendered" colocado no painel que circunda todo o conteúdo da modal. O que indicará então se a modal recarregará os dados (rendered="true") ou se fechará após tudo ser salvo com sucesso (rendered="false") é uma variável booleana que o método ajax do Managed Bean alterará. O código do método do Managed Bean está no passo 4.

O outro componente citado pelo reRender, "mensagemModal", nada mais é que outra modal criada apenas para exibir mensagens de sucesso ao usuário após esta modal principal se fechar. Você pode substituí-la por uma região da página principal (a chamadora) que exibirá sua mensagem.


Passo 3: incluir o seguinte código que monta o componente modal, na mesma tela do evento chamador: 


<componente:alteraDadosModal rendered="${meuManagedBean.displayModalAlteracaoDados}" />
  

O "displayModalAlteracaoDados " é a variável booleana de controle da exibição, de que comentei anteriormente. Ela é controlada pelos métodos seguintes.


Passo 4:  os métodos de abertura e processamento do Managed Bean:


             public void exibirAlteracaoDados(ActionEvent event) {
              setData(dataService.buscarData());

              this.setDisplayModalAlteracaoVencimento(true);
                  }

                 public void alterarDados(ActionEvent event) {

        dataService.salvarAlteracao(id, data, justificativa);
        this.setMensagem(TIPO_MENSAGEM_MODAL, ALTERACAO_VENCIMENTO_SUCESSO);

      this.setDisplayModalAlteracaoVencimento(false);
}


            

   Bom, é isto. Espero que este exemplo e explicações sejam úteis aos programadores que estiverem necessitando utilizar este bom recurso do JSF.

Fiquem à vontade para comentar ou postar dúvidas! =)