Iniciando com Apache Wicket e Java
Instalação
Há várias maneiras de instalar o Wicket mas vou abordar a mais simples que é através do Maven:
mvn archetype:create \
-DarchetypeGroupId=org.apache.wicket \
-DarchetypeArtifactId=wicket-archetype-quickstart \
-DarchetypeVersion=1.4.16 \
-DgroupId=net.gustavohenrique \
-DartifactId=introwicket \
-DinteractiveMode=false
cd introwicket
mvn eclipse:eclipse
Os comandos acima servem para fazer o download a partir dos repositórios do Maven e criar a estrutura do projeto compatível com o eclipse.
Depois é preciso importar o projeto no eclipse (File\Import\General\Existing Projects into Workspace)
.
Uma vez importado, nos deparamos com um erro na IDE acusando que não foi possível encontrar o diretório M2_REPO
. Para corrigir basta criar a variável M2_REPO
em Java Build Path apontando para o repositório local do Maven (geralmente $HOME/.m2/repository
).
Configuração Inicial
Esse exemplo criado através do Maven possui 3 arquivos: WicketApplication.java
, HomePage.java
e HomePage.html
.
O primeiro é uma classe que extende de WebApplication
, responsável pelas configurações do Wicket tal como diretório dos HTMLs, montagem de URLs, etc. Os outros 2 constituem um exemplo simples de página.
Uma página é uma classe que extende de WebPage
e define via construtor quais os componentes inseridos via Java. O nome do arquivo HTML deve ter o mesmo nome da classe Java.
Por padrão, o Wicket procura o HTML da página no mesmo pacote da classe. Manter os .html junto aos .java é horrível e deixa o projeto desorganizado. Felizmente há como mudar isso.
Abra o WicketApplication.java
e faça alguns ajustes na configuração:
package net.gustavohenrique;
import java.net.MalformedURLException;
import java.net.URL;
import org.apache.wicket.WicketRuntimeException;
import org.apache.wicket.protocol.http.SecondLevelCacheSessionStore;
import org.apache.wicket.protocol.http.WebApplication;
import org.apache.wicket.protocol.http.pagestore.DiskPageStore;
import org.apache.wicket.session.ISessionStore;
import org.apache.wicket.util.resource.IResourceStream;
import org.apache.wicket.util.resource.UrlResourceStream;
import org.apache.wicket.util.resource.locator.ResourceStreamLocator;
import org.apache.wicket.util.string.Strings;
public class WicketApplication extends WebApplication {
public void init() {
getMarkupSettings().setDefaultMarkupEncoding("UTF-8");
getResourceSettings().setResourceStreamLocator(new MyOwnStreamLocator());
}
public Class getHomePage() {
return HomePage.class;
}
private class MyOwnStreamLocator extends ResourceStreamLocator {
public IResourceStream locate(Class<!--?--> clazz, String path) {
String extension = path.substring(path.lastIndexOf('.') + 1);
String simpleFileName = Strings.lastPathComponent(clazz.getName(), '.');
// "/" is webapp dir
String location = "/" + simpleFileName + "." + extension;
try {
// try to load the resource from the web context
URL url = WebApplication.get().getServletContext().getResource(location);
if (url != null) {
return new UrlResourceStream(url);
}
}
catch (MalformedURLException e) {
throw new WicketRuntimeException(e);
}
// resource not found; fall back on class loading
return super.locate(clazz, path);
}
}
}
O WicketApplication.java
agora está configurado para usar UTF-8 como padrão e procurar os arquivos HTML dentro de src/main/webapp
.
Brincando com Páginas
Uma página geralmente tem um contrutor que recebe os parâmetros via GET agrupados numa classe PageParameters
similar à um Map. É no construtor que são adicionados os componentes na página.
Modifique o arquivo HomePage.java
para adicionar um link externo e exibir o nome do autor e seu blog na tela de acordo com os parâmetros passados pela URL.
package net.gustavohenrique;
import org.apache.wicket.PageParameters;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.link.ExternalLink;
import org.apache.wicket.markup.html.WebPage;
public class HomePage extends WebPage {
public HomePage(final PageParameters parameters) {
String author = parameters.getString("author", "Gustavo Henrique");
add(new Label("author", author));
String blog = parameters.getString("blog", "http://gustavohenrique.net");
add(new ExternalLink("blog", blog));
}
}
Depois altere o arquivo HomePage.html
:
<h1>Introducao ao Wicket</h1>
<span>Texto daqui sera substituido</span>
<a>Clique aqui para acessar</a>
O componente Label pode ser convertido para vários elmentos HTML, não apenas um span. O texto no código HTML que estiver dentro de um Label
gerado pelo Wicket será ignorado no momento da renderização.
Quando se adiciona um componente, é obrigatório usar a propriedade wicket:id="id_do_componente"
, caso contrário será lançada uma Exception
.
Em modo de produção, o termo wicket:id
some (fica apenas id) e as Exceptions
lançadas são exibidas como erro 500.
Lidando com Formulários
Em Wicket quase tudo é componente. Para tirar melhor proveito do framework e evitar repetição de código, é preciso entender como “componentizar” as coisas. Imagine que você tenha 10 formulários e todos eles têm em comum os campos telefone, celular e email. A idéia de componentizar seria:
- Criar um form com esses componentes e todos os outros herdariam dele
- Criar um componente (Panel) agrupando esses componentes e todos os forms adicionariam um único componente (1 Panel contendo 3 TextFields)
- Juntar as duas soluções
Conseguiram entender a idéia de componentização? Abaixo um exemplo sobre o que estou falando:
public class ContactPanel extends Panel {
public ContactPanel(String id) {
super(id);
add(new TextField("phone"));
add(new TextField("mobile"));
add(new TextField("email"));
}
}
public class CustomerForm extends Form {
public CustomerForm() {
add(new ContactPanel());
}
}
Continuando nossa brincadeira com o framework… Crie um formulário para entrar com nome, idade, site e e-mail do autor:
package net.gustavohenrique;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.RequiredTextField;
import org.apache.wicket.markup.html.form.TextField;
import org.apache.wicket.markup.html.panel.FeedbackPanel;
import org.apache.wicket.model.Model;
public class AuthorForm extends Form {
private RequiredTextField name;
private TextField site;
private TextField email;
private TextField age;
private Label result;
public AuthorForm(String id) {
super(id);
add(name = new RequiredTextField("name", new Model("")));
add(age = new TextField("age", new Model("")));
add(site = new TextField("site", new Model("")));
add(email = new TextField("email", new Model("")));
add(result = new Label("result", new Model("")));
add(new FeedbackPanel("errors"));
}
protected void onSubmit() {
String message = "Olá " + name.getValue() +
", você tem " + age.getValue() +
", seu site é " + site.getValue() +
" e seu e-mail é " + email.getValue();
addOrReplace(result = new Label("result", new Model(message)));
}
}
Inclua o form no HTML:
<h1>Introducao ao Wicket</h1>
<span>Texto daqui sera substituido</span>
<a>Clique aqui para acessar</a>
<form method="POST">
<fieldset>
<label>Nome</label>
<input type="text" />
<label>Age</label>
<input type="text" />
<label>Site</label>
<input type="text" />
<label>E-mail</label>
<input type="text" />
<input type="submit" value="Go" />
</fieldset>
</form>
A novidade é o uso do RequiredTextField
, onSubmit
, Model
e FeedbackPanel
.
Wicket possui recursos para validações simples como preenchimento obrigatório, apenas números, número máximo e mínimo de caracteres etc. No caso o RequiredTextField
requer preenchimento obrigatório.
Mensagens de erro validação podem ser personalizas e definidas em um arquivo de properties
a parte. Quando ocorre um erro de validação, é chamado o método onError()
em vez do onSubmit()
. O FeedbackPanel
é um componente que automaticamente exibe as mensagens de erros de validação.
É uma boa prática usar Models Objects
para guardar valores em componentes. Model
é a classe que implementa IModel
que por sua vez é uma interface que abstrai o tipo de dado que o componente recebe como valor. Varia de acordo com a implementação de cada componente. Em componentes Label
e TextField
, o Model
pega o toString()
do objeto passado. Já em um ListView
é preciso que o objeto implemente uma lista java.util.List
.
Conclusão
Wicket é um framework baseado em componentes e seu uso depende muito do tipo de problema a ser resolvido. Nem sempre componentizar uma aplicação web vale a pena e mesmo com outros frameworks que não são exclusivos para componentização, é possível aplicar essa técnica. IMHO, prefiro Wicket do que JSF e Struts2 do que Wicket.