Extjs e DWR

{ July 2nd, 2009 }


cmilfont

Autor: cmilfont

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


Assine os coment√°rios deste artigo.


5 Responses to “Extjs e DWR”

  1. 1
    Loiane

    √ďtimo tutorial!
    Valeu

  2. 2
    Rafael Ponte

    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.

  3. 3
    cmilfont

    @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.

  4. 4
    Eugenio

    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

  5. 5
    Transpar√™ncia in√©dita na sa√ļde p√ļblica - Milfont Consulting

    […] 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 […]

Leave a Reply