Customizando os Componentes

Vamos dizer que existe o seguinte código, para alterar o método da forma para “get”.


            Form form = new Form("form");

            //default é post
           
           form.setMethod("get");   
        

Para evitar que isto seja um trabalho repetitivo, pode-se criar um componente customizado, utilizando o conceito de herança. Isto perimite utilizar Orientação a Objetos na camada de visualização:


                public class GetForm extends Form {

                    public GetForm() {
              
                          super("form");
              
                          setMethod("GET");
              
                    }
              
              }  
            

Agora, quando for necessário criar um objeto form, basta escrever a seguinte linha de código:


            Form form = new GetForm(); 
            

Este foi um exemplo realmente simples, mas se aplicado da forma correta, pode ser muito útil e produtivo.

Customizando o HTML

Todos os componentes possuem o método toString(), responsável por gerar o HTML correspondente:

Abaixo um exemplo de componente customizado, onde o HTML gerado é utilizado para implementar um simples botão de voltar:


            public class BackButton extends Button {

 

                public BackButton() {
          
                      super("back");
          
                      setTitle("Retornar para a tela anterior");
          
                }
          
               
          
                public String toString() {
          
                      return "<a href='#' onClick='history.back();'> << back
          
                             </a>";
          
                }
          
          }
            

Ainda é possível escrever o HTML utilizando templates do Velocity, conforme visualizado abaixo:


            public class BackButton extends Button {

 

                public BackButton() {
          
                      super("back");
          
           
          
                      setTitle("Return to previous page");
          
                }
          
               
          
                     public String toString() {
          
                              Map model = new HashMap();
          
                              model.put("model", this);
          
                              return getContext().renderTemplate
          
                                                  ("/click/templates/voltar.htm", model);
          
                }
          
          }
            

Para isto basta criar um template “voltar.htm” na pasta /click/templates, no diretório Web da aplicação.

Layout Padrão para páginas HTML

Provavelmente você já ouviu falar do framework Tiles da Jakarta ou Sitemesh da Opensymphony. O Click oferece uma simples alternativa a estes frameworks, para reutilizar um template HTML em todas as páginas da aplicação.

O primeiro passo é escrever uma classe que sobrescreve o método getTemplate() e retorna o html que possui o layout que precisa ser reutilizado.


            public class BorderPage extends Page {
                public String getTemplate() {
                   return "border.htm";
                }
             } 
            

A página do layout “border.htm”, pode ser visualizada abaixo:


            <html>
                <head>
                  <title>Click</title>
                  <link rel="stylesheet" type="text/css" href="style.css" title="Style"/>
                </head> 
                <body>
                  <h2 class="title">$title</h2>
                  #parse($path)</body>
                </html> 
            

Note que o código #parse($path), vai executar o template da página atual. Agora todas as “Pages” devem herdar de BorderPage, por exemplo:


            public class CadastroUsuarioPage extends BorderPage { ...
                

Além da simplicidade desta solução, aind é possível alterar o template “border.htm” em tempo de execução, uma vez que tudo é controlado no código Java. Isto é possível no Sitemesh através de “Decorators”, mas não no Tiles.

Integrando o Spring

Para integrar o Spring ao Click, e utilizar alguns recursos como Injeção de Dependências, é necessário utilizar o SpringClickServlet, ao invés do ClickServlet, no web.xml.


            <servlet>

                <servlet-name>click-servlet</servlet-name>
         
                <servlet-class>net.sf.click.extras.spring.SpringClickServlet</servlet-class>
         
                <init-param>
         
                  <param-name>spring-path</param-name>
         
                  <param-value>/applicationContext.xml</param-value>
         
                </init-param>
         
                <load-on-startup>0</load-on-startup>
         
             </servlet>
        

O arquivo applicationContext.xml é bem conhecido pelos usuários do Spring, ele contém os beans do Spring, e deve ficar na pasta WEB-INF/classes.

O último passo é criar uma nova Page, chamada SpringPage, para recuperar os serviços declarados no arquivo applicationContext.xml.


            public class SpringPage extends BorderPage implements ApplicationContextAware {     protected ApplicationContext applicationContext;     public void setApplicationContext(ApplicationContext applicationContext)  {
                this.applicationContext = applicationContext;
            }     public Object getBean(String beanName) {
                return applicationContext.getBean(beanName);
            }     public UsuarioService getUsuarioService() {
                return (UsuarioService) getBean("usuarioService ");
            }
       }
        

As Pages que precisam usufruir dos recursos oferecidos pelo Spring, precisam herdar de SpringPage:


            public class CadastroUsuarioPage extends SpringPage { ...
        

A qualquer momento o método getUsuarioService(), ou outro, pode ser chamado para recuperar a classe de negócios (e suas dependências) adequada para a página atual.

Também é possível utilizar o Spring para criar as Pages, mas isto está fora do escopo deste artigo.

Exemplo, layout HTML + Componente + Listener

Vamos criar um exemplo de página que possui uma Tabela de Pessoas, utilizando um layout manual apenas com Velocity, porém usufruindo de um componente ActionButton no lado do servidor, o qual possui um listener que responde aos eventos da tela.

Crie uma classe Pessoa:


            Crie uma classe Pessoa:

 

            public class Pessoa {
            
                  private int id;
            
                  private String nome;
            
            // TODO: get / sets
            
            }
            
             
            
            Þ      pessoa.htm
            
            <table id="table">
            
            <tr>
            
            <td>Id</td>
            
            <td>Nome</td>
            
            </tr>
            
            #foreach($p in $pessoas)
            
            <tr>
            
            <td>$p.id</td>
            
            <td>$p.nome</td>
            
            <td><a href="$edit.getOnClick($p.id)" >Editar</a></td>
            
            </tr>
            
            #end
            
            </table>
            
             
            
            $!msg
            
             
            
            Þ      PessoaPage.java
            
            public class PessoaPage extends Page {
            
             
            
                  public ActionButton edit = new ActionButton(this,"onEdit");
            
             
            
                  public void onGet() {
            
                        Collection list = new ArrayList();
            
                        list.add(new Pessoa(1,"Pessoa 1"));
            
                        list.add(new Pessoa(2,"Pessoa 2"));
            
                        list.add(new Pessoa(3,"Pessoa 3"));
            
             
            
                        addModel("pessoas", list);
            
                  }
            
             
            
                  public boolean onEdit(){
            
                        addModel("msg","Vc clicou na pessoa: " + edit.getValueInteger());
            
                        return true;
            
                  }
            
            }        

Note que o código é muito simples. O código na classe PessoaPage apenas define um ActionButton e o método “onEdit” que será o listener.

No html, nao existe nenhuma novidade, apenas Velocity e um loop para mostrar as pessoas. O método “getOnClick” é chamado passando como argumento o “id” da Pessoa atual. O “id” pode ser recuperado facilmente na classe Page, apenas utilizando o próprio componente ActionButton.

Este exemplo mostra como um modelo orientado a componentes e eventos

é produtivo, mesmo quando o layout HTML é manual.

Exemplo de aplicação CRUD

Neste exemplo, vamos construir uma página de cadastro de Pessoas, que possui um formulário e uma tabela para visualizar todos os registros, conforme a imagem abaixo.

Página de cadastro de Pessoas, que possui um formulário e uma tabela para visualizar todos os registros

Vamos criar a classe "PessoaService”, para armazenar a lista de Pessoas, e simular o banco de dados. Um variável estática é utilizada para manter a lista em memória.


            Þ      PessoaService

            public class PessoaService {
            
             
            
                  private static Map pessoas = new LinkedHashMap();
            
             
            
                  public static void addPessoa(Pessoa p){
            
                        pessoas.put(new Integer(p.getId()),p);
            
                  }
            
             
            
                  public static void remove(Integer id){
            
                        pessoas.remove(id);
            
                  }
            
             
            
                  public static List getPessoas(){
            
                        return new ArrayList(pessoas.values());
            
                  }
            
             
            
                  public static Pessoa get(Integer id) {
            
                        return (Pessoa) pessoas.get(id);
            
                  }
            
            }    
            

A página “pessoas.htm”, pode ser definida com apenas 2 linhas (onde Form e Table são componentes do Click):


            Þ      pessoas.htm

            $form
            
             
            
            $table
        

A classe PessoaPage que possui os componentes Form e Table do Click, assim como a lógica de negócio, pode ser visualizada abaixo:



            public class PessoaPage extends Page {

 

                public Form form   = new Form();
          
                public Table table = new Table();
          
           
          
                public ActionLink edit = new ActionLink(this,"onEdit");
          
                public ActionLink delete = new ActionLink(this,"onDelete");
          
           
          
                public PessoaPage(){
          
                      //Form
          
                      form.add(new IntegerField("id",true));
          
                      form.add(new TextField("nome",true));
          
                      form.add(new Submit("salvar","Salvar Pessoa"));
          
           
          
                      //Table
          
                      table.addColumn(new Column("id"));
          
                      table.addColumn(new Column("nome"));
          
           
          
                      //Links Editar e Deletar
          
                      Column column = new Column("Action");
          
                  ActionLink[] links = new ActionLink[]{edit, delete};
          
                  column.setDecorator(new LinkDecorator(table, links, "id"));
          
                  table.addColumn(column);
          
                }
          
           
          
                //listener: ao clicar no link "edit"
          
                public boolean onEdit(){
          
                      Pessoa p = PessoaService.get(edit.getValueInteger());
          
                      form.copyFrom(p);
          
                      return true;
          
                }
          
           
          
                //listener: ao clicar no link "delete"
          
                public boolean onDelete(){
          
                      PessoaService.remove(delete.getValueInteger());
          
                      return true;
          
                }
          
               
          
                //ao clicar em Salvar
          
                public void onPost() {
          
                      if(form.isValid()){
          
                            Pessoa p = new Pessoa();
          
                            form.copyTo(p);
          
           
          
                            PessoaService.addPessoa(p);
          
                      }
          
                }
          
           
          
                //atualiza a tabela após
          
                public void onRender() {
          
                      table.setRowList(PessoaService.getPessoas());
          
                }
          
          }
        

Note que o código é extremamente simples. Na classe são definidos os componentes Form e Table. Para o Table, são definidos as colunas e também os links “editar” e “deletar”.

A classe LinkDecorator é um Decorator (pattern) que cria os links para os métodos (listeners) “onEdit” e “onDelete”, os quais são automaticamente chamados pelo Click ao clicar nos links.

O método “onPost” é chamado automaticamente ao clicar em “Salvar Pessoa”, uma vez que isto gera uma request do tipo “post”. Note como um objeto Pessoa é criado e populado automaticamente com o método “copyTo”. Isto copia as informações da Form para o objeto Pessoa. O inverso também existe, copiar as informações da Pessoa para a Form, como é feito no método “onEdit”, utilizando o método “copyFrom”.

O método “onRender” é utilizado para atualizar a tabela. Este método é executado depois de qualquer listener (onEdit,onDelete), e dos métodos (onGet/onPost).