Click - Programação Web orientada a Componentes e Eventos – Parte III
Neste artigo será apresentado o Click, um framework Orientado a Páginas e Componentes, e ainda possui um modelo de programação b...
Customizando os Componentes
Vamos dizer que existe o seguinte código, para alterar o método da forma para "get".
//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");
return "<a href='#' onClick='history.back();'> << back
Ainda é possível escrever o HTML utilizando templates do Velocity, conforme visualizado abaixo:
setTitle("Return to previous page");
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.
A página do layout "border.htm", pode ser visualizada abaixo:
#parse($path)</body>
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.
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) {
} public Object getBean(String 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:
// TODO: get / sets
Þ pessoa.htm
#foreach($p in $pessoas)
<td>$p.id</td>
<td>$p.nome</td>
<td><a href="$edit.getOnClick($p.id)" >Editar</a></td>
Þ PessoaPage.java
public class PessoaPage extends Page {
public ActionButton edit = new ActionButton(this,"onEdit");
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());
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.
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
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){
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
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 ActionLink edit = new ActionLink(this,"onEdit");
public ActionLink delete = new ActionLink(this,"onDelete");
public PessoaPage(){
form.add(new IntegerField("id",true));
form.add(new TextField("nome",true));
form.add(new Submit("salvar","Salvar Pessoa"));
table.addColumn(new Column("nome"));
//Links Editar e Deletar
Column column = new Column("Action");
ActionLink[] links = new ActionLink[]{edit, delete};
//listener: ao clicar no link "edit"
Pessoa p = PessoaService.get(edit.getValueInteger());
form.copyFrom(p);
//listener: ao clicar no link "delete"
public boolean onDelete(){
PessoaService.remove(delete.getValueInteger());
//ao clicar em Salvar
form.copyTo(p);
PessoaService.addPessoa(p);
//atualiza a tabela após
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).