Nesse artigo eu pretendo trabalhar dois conceitos principais de uso do Extjs, extensão/customização de componentes e acesso a dados server-side com base em experiência recente em um projeto que desenvolvemos. Esse projeto em questão é um ERP que tinha a necessidade de manter a usabilidade similar a sua versão antiga, feita em Delphi, para o desktop.
Para suprir essa necessidade de usabilidade tivemos que adotar alguns conceitos, como ser totalmente stateless e modificar a arquitetura MVC2 para o MVC3. No server-side trabalhamos com um domain model baseado em Hibernate, Spring e Facades e Services com DWR. Nada de frameworks MVC2, não nos preocupamos com renderização e sim com a API. No lado cliente usamos Extjs com algumas modificações que fiz para integrar de forma suave com o DWR.
Primeiro precisamos entender como o Extjs trabalha com herança. Basicamente há um método no objeto Ext que faz esse trabalho de extensão dos componentes, funciona da seguinte maneira:
//Formato: var NovoComponente = Ext.extend(velhoComponente, { /* metodos e propriedades que serão reescritas */ }); //Exemplo: var MilfontGridPanel = Ext.extend(Ext.grid.GridPanel, { //novo construtor constructor: function(config) { // Seu preprocessamento vai aqui MilfontGridPanel.superclass.constructor.apply(this, arguments); // Seu pos-processamento vai aqui }, NovoMethod: function() { // algum novo método que você queira criar para o novo componente } });
Para esse projeto, criei um Ext.data.DataProxy (como visto no artigo passado) especialista para o DWR, criativamente denominado DWRProxy. A idéia é modificar o comportamento de buscar os dados para usar um Creator do DWR.
Definimos primeiro o objeto e suas propriedades necessárias:
Ext.ux.data.DWRProxy = function(dwr_facade, dwr_filter, dwr_errorHandler){ Ext.ux.data.DWRProxy.superclass.constructor.call(this); /* Propriedade que receberá a classe Java configurada como Creator */ this.data = dwr_facade; /* * Propriedade que receberá uma classe java configurada como converter * que servirá como filtro de busca */ this.dwr_filter = dwr_filter; /** * * Propriedade para fazer paginação, indica que deve cachear a consulta de * total de elementos o controlador [fachada] deve implementar a logica de * negocios adequada, quando for false consulta o total, quando for true * consulta apenas a listagem e repete o total */ this.dwr_total_cache = false; this.dwr_errorHandler = dwr_errorHandler; };
Após isso extendemos do Ext.data.DataProxy :
Ext.extend(Ext.ux.data.DWRProxy, Ext.data.DataProxy, { /** * Método Load do Ext.data.DataProxy overrided */ load : function(params, reader, callback, scope, arg) { /** * Escopo "this" mapeado para a variável "s" porque dentro do callback do * DWR o escopo "this" não pertence ao objeto Ext.ux.data.DWRProxy. */ var s = this; params = params || {}; if(params.cache != undefined) { this.dwr_total_cache = params.cache; } if(params.filter != undefined) { this.dwr_filter = params.filter; } var result; try { this.data(this.dwr_filter, params.start, params.limit, this.dwr_total_cache, { callback:function(response) { //aqui passamos o retorno do DWR // que chamei de response, para o extjs result = reader.readRecords(response); callback.call(scope, result, arg, true); }, errorHandler:function(a, e) { scope.fireEvent("loadexception", s, arg, null, e); s.dwr_errorHandler(a); }, timeout:100000 }); this.dwr_total_cache = true; } catch(e) { this.fireEvent("loadexception", this, arg, null, e); callback.call(scope, null, arg, false); return; } } });
A fachada DWR é uma classe comum, segue um exemplo de uso com Hibernate:
//classe para satisfazer o transporte para o Extjs public final class DataTransferObject { private int total; private List<? extends Object> results; //sets e gets } public class AjaxFacade { //injeta um repositorio, whatever private Repository repository = null; public DataTransferObject find(Object filter, int start, int limit, boolean cache, HttpSession session) { DataTransferObject dto = new DataTransferObject(); //verifica se o Proxy está passando true // indicando que está paginando if (cache) { Integer total = (Integer) session.getAttribute("totalObject"); dto.setTotal(total); } else { session.removeAttribute("totalObject"); Integer total = repository.count(filter); dto.setTotal(total); session.setAttribute("totalObject", total); } List<Object> retorno = (List<Object>) repository.find(filter, start, limit); dto.setResults(retorno); return dto; }
Para o Grid visto no artigo passado, basta instanciar assim no javscript:
var store = new Ext.data.Store({ proxy: new Ext.ux.data.DWRProxy( AjaxFacade.find, {$dwrClassName:"Project"}, errorHandler ), reader: new Ext.data.JsonReader({ root: 'results',totalProperty: 'total',id: 'id' }, ['id', 'name', 'manager.name', 'manager.address.country'] ) });
Para entender o {$dwrClassName:”Project”} visite esse post.
Dessa forma o DWR se torna um proxy para todos os componentes do Extjs.
Código fonte da modificação do javascript eu coloquei aqui no github e uma aplicação demo aqui. No próximo vou integrar o DWR com o Rails, aguardem que sai logo… ou não.
Categories: Ajax, DWR, Ext, Frameworks, Java, JavaScript, JEE, JSON, Model 3, RIA, web2.0 ~ ~ Trackback
July 2nd, 2009 at 12:52 pm
Ótimo tutorial!
Valeu
July 2nd, 2009 at 4:05 pm
Muito bom o artigo!
Só não vi a real necessidade de utilizar o “cache” em session para evitar uma nova requisição ao banco [count(*)], principalmente quando você caminha para algo stateless.
Acho que utilizar a session no código está atuado como “glue code”. Eu, particularmente, não o utilizaria. Principalmente se eu estivesse com o cache level 2 do Hibernate ligado, pois ele sim deveria se preocupar com isso, e não você.
Enfim, excelente post.
July 3rd, 2009 at 3:49 am
@Rafael Ponte, nesse projeto especÃfico o cache não pode ser usado por motivos que não tenho permissão de expor aqui mas você deve imaginar já que conhece o projeto.
Mas fica a excelente dica para quem puder usar o cache do Hibernate ou outra tecnologia como Ibatis [que nem sei se tem cache de segundo nÃvel].
Sobre a session eu estou resolvendo com o Spring [intercepta e faz o trabalho sujo], mas para não complicar e o pessoal entender de forma fácil eu fiz assim no exemplo.
August 31st, 2009 at 3:53 am
Christiano,
Antes de mais, parabéns pelo artigo.
Tenho andado à procura de como fazer um proxy em ExtJS e este é o artigo que encontrei mais semelhante daquilo que pretendo.
O que queria era apenas ser eu a definir como fazer o put e get de informação numa página web mas sem usar XML ou JSON, uma vez que a página usa uma especificação própria e é necessário parsing.
Eu estava a pensar fazer as funções de parsing em JavaScript e delegar o put e get nessas funções. Será que me podes dar uma ideia de como será este proxy?
Obrigado
March 7th, 2010 at 6:44 am
[...] Ajax com DWR. O sistema é totalmente ajax e utiliza o Extjs seguindo a filosofia model 3. Fizemos algumas customizações no Extjs para se integrar ao DWR de forma [...]