quinta-feira, 22 de abril de 2010

O componente rowSelector do IceFaces

A intenção de utilizar rowSelector é que quando o internauta selecionar uma linha do dataTable, o objeto correspondente seja editado automaticamente. Podemos fazer isso preparando o dataTable já existente para funcionar dentro de um panelTabSet. Essa estratégia permitirá que adicionemos um formulário de edição em uma das abas do panelTabSet e enviemos para ele os dados da linha que for selecionada.
No backing bean (ControladorBean.java) serão necessários dois novos atributos:

private int abaSelecionada = 0;
private Person pessoaSelecionada;

Sem esquecer que os gets e sets também serão necessários. Em seguida, é preciso um método que seja disparado quando uma linha for selecionada no dataTable:

    public void selecionouLinha(RowSelectorEvent linha) {
        int linhaSelecionada = linha.getRow();
        lista.setRowIndex(linhaSelecionada);
        pessoaSelecionada = (Person)lista.getRowData();
        abaSelecionada = 1;
    }

Para gravar as alterações, um método que se comunica com a camada de persistência:

    public void atualizar() {
        boolean gravou = dao.crud(pessoaSelecionada, "3");
        abaSelecionada = 0;
    }

E também um método para cancelar a alteração em curso e voltar para a lista de pessoas:

    public void cancelar() {
        pessoaSelecionada = new Person();
        abaSelecionada = 0;
    }

Finalmente, o código completo e atualizado da página (pessoas.xhtml)

001 <?xml version='1.0' encoding='UTF-8' ?>
002 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
003     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
004 <html xmlns="http://www.w3.org/1999/xhtml" 
005       xmlns:ui="http://java.sun.com/jsf/facelets"
006       xmlns:f="http://java.sun.com/jsf/core"
007       xmlns:h="http://java.sun.com/jsf/html"
008       xmlns:ice="http://www.icesoft.com/icefaces/component">
009     <head>
010         <ice:outputStyle href="./xmlhttp/css/rime/rime.css" />
011     </head>
012     <body>
013         <ice:form>
014             <div align="center">
015                 <h1><ice:outputText value="Pessoas" /></h1>
016                 <ice:panelTabSet id="painel" selectedIndex="#{controlador.abaSelecionada}">
017                     <ice:panelTab id="panelTab1" label="Lista">
018                         <ice:dataPaginator for="tabelaPessoas"
019                                            paginator="true"
020                                            fastStep="5"
021                                            paginatorMaxPages="3">
022                             <f:facet name="first">
023                                 <ice:graphicImage
024                                     url="./xmlhttp/css/xp/css-images/arrow-first.gif"
025                                     style="border:none;"
026                                     title="Início"/>
027                             </f:facet>
028                             <f:facet name="last">
029                                 <ice:graphicImage
030                                     url="./xmlhttp/css/xp/css-images/arrow-last.gif"
031                                     style="border:none;"
032                                     title="Final"/>
033                             </f:facet>
034                             <f:facet name="previous">
035                                 <ice:graphicImage
036                                     url="./xmlhttp/css/xp/css-images/arrow-previous.gif"
037                                     style="border:none;"
038                                     title="Anterior"/>
039                             </f:facet>
040                             <f:facet name="next">
041                                 <ice:graphicImage
042                                     url="./xmlhttp/css/xp/css-images/arrow-next.gif"
043                                     style="border:none;"
044                                     title="Próxima"/>
045                             </f:facet>
046                             <f:facet name="fastforward">
047                                 <ice:graphicImage url="./xmlhttp/css/xp/css-images/arrow-ff.gif"
048                                                   style="border:none;"
049                                                   title="Avanço rápido"/>
050                             </f:facet>
051                             <f:facet name="fastrewind">
052                                 <ice:graphicImage url="./xmlhttp/css/xp/css-images/arrow-fr.gif"
053                                                   style="border:none;"
054                                                   title="Retrocesso rápido"/>
055                             </f:facet>
056                         </ice:dataPaginator>
057                         <ice:dataTable id="tabelaPessoas"
058                                        value="#{controlador.lista}"
059                                        var="item" border="1"
060                                        rows="3"
061                                        sortColumn="#{controlador.colunaOrdenada}"
062                                        sortAscending="#{controlador.ordemInversa}">
063                             <ice:column>
064                                 <ice:rowSelector selectionListener="#{controlador.selecionouLinha}" />
065                                 <f:facet name="header">
066                                     <ice:commandSortHeader columnName="Nome" arrow="true" >
067                                         <ice:outputText value="Nome"/>
068                                     </ice:commandSortHeader>
069                                 </f:facet>
070                                 <ice:outputText value="#{item.name}" />
071                             </ice:column>
072                             <ice:column>
073                                 <f:facet name="header">
074                                     <ice:commandSortHeader columnName="Tratamento" arrow="true" >
075                                         <ice:outputText value="Tratamento"/>
076                                     </ice:commandSortHeader>
077                                 </f:facet>
078                                 <ice:outputText value="#{item.jobtitle}" />
079                             </ice:column>
080                             <ice:column>
081                                 <f:facet name="header">
082                                     <ice:commandSortHeader columnName="Atualizado em" arrow="true">
083                                         <ice:outputText value="Atualizado em"/>
084                                     </ice:commandSortHeader>
085                                 </f:facet>
086                                 <ice:outputText value="#{item.lastupdated}">
087                                     <f:convertDateTime pattern="dd/MM/yyyy" />
088                                 </ice:outputText>
089                             </ice:column>
090                         </ice:dataTable>
091                     </ice:panelTab>
092                     <ice:panelTab label="Editar">
093                         <ice:panelGrid columns="2">
094                             <ice:outputText value="Nome:" />
095                             <ice:inputText value="#{controlador.pessoaSelecionada.name}" />
096                             <ice:outputText value="Tratamento:" />
097                             <ice:inputText value="#{controlador.pessoaSelecionada.jobtitle}" />
098                             <ice:outputText value="Atualizado em:" />
099                             <ice:inputText value="#{controlador.pessoaSelecionada.lastupdated}" >
100                                 <f:convertDateTime pattern="dd/MM/yyyy" />
101                             </ice:inputText>
102                             <ice:commandButton value="Gravar" action="#{controlador.atualizar}" />
103                             <ice:commandButton value="Cancelar" action="#{controlador.cancelar}" />
104                         </ice:panelGrid>
105                     </ice:panelTab>
106                 </ice:panelTabSet>
107             </div>
108         </ice:form>
109     </body>
110 </html>
 
A linha 64 mostra a utilização do rowSelector, e as linhas 92-105 mostram o formulário de edição. Na linha 16, há a propriedade selectedIndex do panelTabSet que o backing bean utiliza para mudar a aba selecionada, por meio do atributo abaSelecionada do controlador. A seguir, duas imagens que mostram o comportamento do panelTabSet e o formulário de edição recebendo os dados.


Resta somente a perfumaria - mensagens de interação com o usuário, opções para excluir, inserir, filtrar, etc. , que poderão ser assunto de outro post.

quarta-feira, 21 de abril de 2010

Colunas ordenadas no DataTable

Utilizar o componente commandSortHeader do IceFaces pode parecer um pouco confuso no início, mas é um componente muito útil e oferece bastante controle de suas características.
Para utilizá-lo, começamos por modificar as colunas do componente dataTable, dessa maneira:

<ice:column>
   <f:facet name="header">
      <ice:commandsortheader arrow="true" columnname="Nome">
         <ice:outputtext value="Nome"/>
      </ice:commandsortheader>
      <ice:outputtext value="#{item.name}"/>
   </f:facet>
</ice:column>

O valor da propriedade columnname deve ser sempre o título da coluna. Para que o componente dataTable entenda essas mudanças, devemos adicionar a ele algumas propriedades:

<ice:dataTable id="tabelaPessoas"
               value="#{controlador.lista}"
               var="item" border="1"
               rows="3"
               sortColumn="#{controlador.colunaOrdenada}"
               sortAscending="#{controlador.ordemCrescente}">

As duas últimas propriedades é que fazem a diferença. A boa notícia é que o componente se encarrega de enviar as mudanças de coluna que o usuário fizer aos atributos do backing bean (colunaOrdenada e ordemCrescente). Esses atributos serão utilizados para determinar como deve ser feita a ordenação do atributo lista do backing bean, e podem ser declarados como segue, no controlador:
private boolean ordemCrescente = true;
private String colunaOrdenada = "Nome";

É importante perceber que ao ser instanciado, o DataTabe vai ficar ordenado pela coluna "Nome", e qualquer seleção feita pelo usuário será transmitida para o atributo colunaOrdenada. Agora, a má notícia: deve ser implementado um método específico para a ordenação. Eu optei por utilizar um comparator e o método sort da classe java.util.Collections. A seguir, o método utilizado para a ordenação:

01     protected void ordenar(List relacao) {
02         Comparator comparator = new Comparator() {
03 
04             public int compare(Object o1, Object o2) {
05                 Person c1 = (Person) o1;
06                 Person c2 = (Person) o2;
07                 if (colunaOrdenada.equals("Nome")) {
08                     return ordemCrescente
09                             ? (c1.getName()).compareTo((c2.getName()))
10                             : (c2.getName()).compareTo((c1.getName()));
11                 }
12                 if (colunaOrdenada.equals("Tratamento")) {
13                     return ordemCrescente
14                             ? (c1.getJobtitle()).compareTo((c2.getJobtitle()))
15                             : (c2.getJobtitle()).compareTo((c1.getJobtitle()));
16                 }
17                 if (colunaOrdenada.equals("Atualizado em")) {
18                     return ordemCrescente
19                             ? (c1.getLastupdated()).compareTo((c2.getLastupdated()))
20                             : (c2.getLastupdated()).compareTo((c1.getLastupdated()));
21                 }
22                 return 0;
23             }
24         };
25         Collections.sort(relacao, comparator);
26     }

Fica bem claro que para cada coluna, será necessário um tratamento para que o Comparator possa executar as comparações. É importante observar a utilização dos atributos colunaOrdenada e ordemCrescente, que são alterados pela página, conforme as escolhas do usuário. Este método deve ser colocado em algum local onde seja executado quando houver mudança de coluna ou mudança da ordenação. Um bom local é o método que retorna a lista para o dataTable:

    public ListDataModel getLista() {
        List relacao = dao.crud("from Person");
        ordenar(relacao);
        lista = new ListDataModel(relacao);
        return lista;
    }

Para finalizar, é necessário declarar os atributos e criar os métodos get e set para cada um deles. A seguir, um screenshot com a ordenação funcionando, e observamos que ela respeita a paginação, o que em muitas linguagens não funciona de maneira adequada.


terça-feira, 20 de abril de 2010

As extensões .iface, .jspx, .html

Às vezes parece um pouco complicado aprender a modificar o comportamento das páginas criadas com o framework IceFaces, para que as extensões sejam mostradas como .html.
O primeiro passo é modificar o arquivo web.xml, especificamente nesta seção:

    <servlet-mapping>
        <servlet-name>Persistent Faces Servlet</servlet-name>
        <url-pattern>*.html</url-pattern>
    </servlet-mapping>

Dessa maneira, mesmo que as páginas tenham a extensão .xhtml, elas serão acessadas no navegador como .html.
Para facilitar os testes, podemos alterar mais esta seção do web.xml:

    <welcome-file-list>
        <welcome-file>pessoas.html</welcome-file>
    </welcome-file-list>

O nome do arquivo deve ser o da página inicial do sistema, que no meu caso é pessoas.html (no projeto continua como pessoas.xhtml). Se houver necessidade, podemos modificar uma propriedade do projeto para que já inicie com a página correta: