13 outubro 2007

Combos com Ajax no Wicket

Fiquei com medo quando tive que alterar uma tela para que ela não fizesse refresh, pra atualizar os dados de uma combo quando uma anterior tinha sua seleção alterada. Qual foi minha surpresa, quando... o código do método refresh era, na verdade, maior que o código
que utiliza o método Ajax?!?! Sensacional!!

HTML
<select id="estados">
<option>Bar</option>
</select>

<select id="cidades">
<option>Foo</option>
</select>

Java
Map cidades = listCidades(); // Mapa de cidades para RJ, SC e SP
List estados = Arrays.asList(new String[]{"RJ", "SC", "SP"});
DropDownChoice ddCidades = new DropDownChoice("cidades", Collections.EMPTY_LIST);

Quem deve receber notificações de seleção para atualizar a combo ddCidades, é a combo ddEstados, criada logo abaixo. Veja o comparativo, entre o código que executa um refresh completo, e o segundo, que utiliza Ajax.

* Código que utiliza Synchronous Server Roundtrip (refresh no browser)
DropDownChoice ddEstados = new DropDownChoice("estados", estados) {
protected boolean wantOnSelectionChangedNotifications() {
return true;
}

protected void onSelectionChanged(final Object newSelection) {
String estado = (String) newSelection;
ddCidades.setChoices((List) cidades.get(estado));
}
};


O método wantOnSelectionChangedNotification deve retornar true, para que o Wicket possa saber que deve gerar um Javascript no HTML de saída, para que seja feito um submit do formulário, sem executar o ciclo de vida do Form. O framework nem atualiza os Models dos componentes, mas mantém o estado da página. Por fim executa o metodo onSelectionChanged(Object) do objeto que sofreu a alteração.

* Código que utiliza Asynchronous Server Roundtrip (AJAX)
DropDownChoice ddEstados = new DropDownChoice("estados", estados);
ddEstados.add(new AjaxFormComponentUpdatingBehavior("onchange") {
protected void onUpdate(AjaxRequestTarget target) {
String estado = (String) ddEstado.getModelObject();
ddCidades.setChoices((List) cidades.get(estado));
target.add(ddCidades);
}
});


Aqui é dito que o componente ddEstados recebe um IBehavior, que será disparado no evento onchange do componente. É importante lembrar que o onchange é do Javascript, e não algo específico do framework.
Quando o Wicket submete a chamada Ajax, ele atualiza a nova seleção, antes de executar o método onUpdate. Por esta razão, é possível acessar o novo valor pelo getModelObject(). Feito isto, as cidades daquele estado são filtradas e a nova lista, atualizada na combo de Cidades. A chave para esta funcionalidade, está na linha
target.add(ddCidades);
Esta chamada indica que este componente deve ser renderizado e atualizado, via Ajax, no HTML. O próprio framework se encarrega de todo o trabalho! Qual a dificuldade? Conhecer a API do Framework... :)

Notaram que... o código HTML não sofreu alteração? ;)

Vejamos alguns números:
  • Código HTML: não sofreu alteração.
    6 linhas escritas pelo programador (LEPP).
  • Código JavaScript: gerado pelo framework; (programador não precisa conhecer JS).
  • Código Java: plain Java. Exige apenas conhecimento da linguagem Java, POO e a API do Framework (como qualquer outro framework)
    Non-Ajax: 9 LEPPs
    Ajax: 8 LEPPs
  • Código XML: zero
  • Uso de Tag Libraries: zero. Wicket não usa essa tecnologia. =)
  • Uso de outros frameworks: zero. Desenvolver Ajax com Struts, por exemplo, geralmente exige integração com DWR, ou outros frameworks similares.

"Welcome to Wicket... Plain Java, Plain HTML... this is Web Development!"

Um comentário:

david disse...

Muito bom o artigo cara, o usei hoje. heheheh.
Tava com um problema ao fazer isso com radio button porque ele perdia todos os outros valores quando eu submetia via ajax.
Depois que entendi como o framework funciona consegui resolver.

Contato

Email:bruno.borges(at)gmail.com

LinkedIn: www.linkedin.com/in/brunocborges
Twitter: www.twitter.com/brunoborges
Comprei e Não Vou
Rio de Janeiro, RJ Brasil
Oracle
São Paulo, SP Brasil