Category Archives: JavaScript

Extjs e DWR

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 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 retorno = (List) 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.

Typically chemist’s shop can sale to you with discreet treatments for various soundness problems. There are numerous of safe online pharmacies that will deliver medications to your address. There are divers medicines for each afflictions. Learn more about “viagra manufacturer coupon“. Maybe “viagra discount coupons” is a highly complicated question. Matters, like “coupons for viagra“, are connected numerous types of health problems. If you need to take recipe medications, ask your pharmacist to check your testosterone levels before. Sometimes the treatment options may include erectile malfunction remedies or a suction device that helps get an erection. Keep in mind web-site which is ready to sell erectile dysfunction drugs like Viagra without a prescription is fraudulent. When you purchase from an unknown web-site, you run the risk of getting counterfeit remedies.

Introdução ao Ext

Eu trabalho com Extjs desde que ele era uma extensão para o YUI, ainda hoje há aplicação no ar usando essa antiga tecnologia [por problema causado por algum idiota, você provavelmente será redirecionado para outro site do governo, dá uma olhada no canto esquerdo superior e clique em “IR PARA A SEPLAG“]. Para ver o Extjs no tempo que ele se chamava ext-yui, vá no link de pesquisa avançada, preencha o input descrição em “dados da matéria” com “secretaria de cultura” por exemplo e clique no botão pesquisar.

Esse tutorial tem o objetivo de preparar o conhecimento para outros posts que estou escrevendo e achei necessário uma introdução apenas nos conceitos do Extjs para não confundir com as tecnologias que uso em conjunto como DWR ou no modelo REST com o RubyOnRails.

O Extjs é um framework javascript de propósito geral, ou seja, tem um conjunto de funcionalidades que tratam Ajax, um conjunto de Widgets bem elaborados [componentes visuais como Grid e TabPanel], manipulação de DOM [Document Object Model] e BOM [Browser Object Model], tratamento de eventos, animações como Fade In e Fade Out, parser de JSON, entre outras coisas. Seus componentes são construídos com técnicas modernas de orientação a objetos no javascript e manipulação de Scripttag para recursos remotos que não suportam Ajax.

Preparação

Após baixar e descompactar o framework [estou trabalhando na versão 2.x que é estável nessa data], recomendo que deixa a disposição das pastas conforme se encontra e coloque no seu projeto de forma que seja acessível via web, já vem com documentação e exemplos que você deve e vai usar durante o desenvolvimento. Temos a opção de montar o Extjs [marcando a opção “Make build available via CacheFly” ] no CacheFly como um servidor CDN para otimizar o tráfego de sua aplicação principalmente se ela será disponibilizada na internet e não apenas na intranet.

É necessário importar o CSS global, o adapter e o Javascript global conforme mostrado abaixo:




Pode usar outros temas para o Extjs, estarão na pasta “resources/css“, assim como podemos internacionalizar os componentes com arquivos que se encontram em “build/locale“. Exemplo com o tema “Gray” e i18n em português do Brasil:






Internacionalização é algo pensado no Extjs de forma a facilitar a criação de arquivos de linguagem aproveitando a estrutura da linguagem [dinâmica e fracamente tipada], todas as propriedades de mensagens e textos são públicas para facilitar a reescrita como mostrado abaixo na i18n do componente de DataPicker:

if(Ext.DatePicker){
   Ext.apply(Ext.DatePicker.prototype, {
      todayText         : "Hoje",
      minText           : "Esta data é anterior a menor data",
      maxText           : "Esta data é posterior a maior data",
      disabledDaysText  : "",
      disabledDatesText : "",
      monthNames        : Date.monthNames,
      dayNames          : Date.dayNames,
      nextText          : 'Próximo Mês (Control+Direita)',
      prevText          : 'Mês Anterior (Control+Esquerda)',
      monthYearText     : 'Escolha um Mês (Control+Cima/Baixo para mover entre os anos)',
      todayTip          : "{0} (Espaço)",
      format            : "d/m/Y",
      okText            : " OK ",
      cancelText        : "Cancelar",
      startDay          : 0
   });
}

Se você usar o CacheFly gerado pela página do Extjs, ele incluir o adapter e o global em um mesmo arquivo, vai ser algo como:




Adapter

Quando o Extjs passou a ser um framework independente do YUI, passou a adotar outros frameworks como base para funções básicas de manipulação de DOM e Ajax, hoje suporta trabalhar em conjunto com YUI, JQuery, Prototype e totalmente independente. Se o projeto já tem Jquery ou outro framework que trabalha com o Extjs, a utilização dos dois é muito fácil e indicada, principalmente para usar os widgets que são provavelmente os mais poderosos hoje em dia em um framework opensource.

//Exemplos de adapters permitidos


Widgets

O principal apelo do Extjs que conquista os desenvolvedores é o layout bem trabalhado dos componentes visuais que são de fácil parametrização. Basicamente todos os componentes funcionam da mesma forma, você o instancia passando um objeto literal de configuração com mostrado abaixo:

//Exemplo do grid
var grid  = new Ext.grid.GridPanel({
autoShow:true, width:750,height:250 //mais parametros 
});
//Exemplo de uma Window
var window = new Ext.Window({
autoShow:true, width:750,height:250 //mais parametros 
});
//Exemplo de um Painel
var panel = new Ext.Panel({
autoShow:true, width:750,height:250 //mais parametros 
});

Vamos usar o GRID para exemplificar como trabalhamos com o Extjs, o mesmo comportamente se repete em todos os componentes.
A documentação do Extjs é muito bem feita e praticamente vai ser a única coisa que você vai precisar depois de entender como os componentes são formados, afinal não vale a pena decorar todas as propriedades de todos os componentes, concentre-se apenas em entender os conceitos.

A GRID é o Widget mais famoso desse framework e é formado basicamente por um objeto “Ext.data.Store” [que é a fonte de dados da GRID] e um objeto “Ext.grid.ColumnModel” [que é a definição das colunas], como mostrado abaixo:

var grid  = new Ext.grid.GridPanel({
    autoShow:true, width:750,height:250, //parametros de configuração de layout
    cm: new Ext.grid.ColumnModel({/*configuração*/}),
    store: new Ext.data.Store({}), //Ou especialização de um Store
    sm: new Ext.grid.RowSelectionModel({singleSelect:true}), //ou outra especialização de um AbstractSelectionModel
});

O objeto ColumnModel é a definição de colunas do Grid, possui propriedades para definição de layout como largura e altura, título da coluna como vai ser exibida e formatação do texto:

var colModel = new Ext.grid.ColumnModel([
    { header: "Ticker", width: 60, sortable: true},
    { header: "Company Name", width: 150, sortable: true},
    { header: "Market Cap.", width: 100, sortable: true},
    { header: "$ Sales", width: 100, sortable: true, renderer: money},
    { header: "Employees", width: 100, sortable: true, resizable: false}
 ]);

A propriedade cm [ColumnModel] pode ser também substituída pela propriedade “columns” que funciona como um “alias”, dessa forma o Grid cria automaticamente um objeto ColumnModel:

var grid = new Ext.grid.GridPanel({
    columns: [
        {id:'id', header: "id", width: 200, sortable: true, dataIndex: 'id'},
        {header: "Nome", width: 120, sortable: true, dataIndex: 'name'}
    ]
});

Caso haja necessidade de formatar o conteúdo da célula, você pode usar uma função como “renderer” para tratar esse conteúdo:

var grid = new Ext.grid.GridPanel({
    columns: [
        {id:'id', header: "id", width: 200, sortable: true, dataIndex: 'id'},
        {header: "Nome", width: 120, sortable: true, dataIndex: 'name'},
        {header: "Criado em", width: 135, sortable: true, renderer: function(value) {
                    return Date.parseDate(value, 'Y-m-d\\TH:i:s\\Z').format('d/m/Y H:i:s');
                    //2009-06-14T12:51:07Z
        }, dataIndex: 'created_at'}
    ]
});

A propriedade “store” da GRID é uma especialização do componente “Ext.data.Store” que é formado basicamente por um “proxy” e um “reader“:

var store = new Ext.data.Store({
    proxy: new Ext.data.DataProxy(), //ou uma especialização
    reader: new Ext.data.DataReader() //ou uma especialização
});

O “proxy” é o componente que obterá os dados e o “reader” o componente que fará a leitura desses dados para um formato comum a todos os componentes do Extjs na forma de um objeto denominado “Ext.data.Record“. O objeto “Record” representa um registro de dados e é usado seja para GRID, para um Form ou qualquer componente que trabalhe com dados editáveis.

Dessa forma podemos usar uma combinação de Proxy e Reader como HttpProxy e JsonReader:

var store = new Ext.data.Store({
    proxy: new Ext.data.HttpProxy({
        url: 'projects.json'
    }),
    reader: new Ext.data.JsonReader({
        totalProperty:'total',
        root:'results',id:'id'
    }, Ext.data.Record.create([
        {name:'id', mapping:'id'},
        {name:'name', mapping:'name'},
        {name:'created_at', mapping:'created_at'},
        {name:'updated_at', mapping:'updated_at'}
    ]))
});

Devido o costume do uso do HttpProxy, o componente Store possui uma propriedade chamada “url” que estando presente cria um HttpProxy automaticamente como mostrado abaixo:

var store = new Ext.data.Store({
    url: 'projects.json',
    reader: new Ext.data.JsonReader({
        totalProperty:'total',
        root:'results',id:'id'
    }, Ext.data.Record.create([
        {name:'id', mapping:'id'},
        {name:'name', mapping:'name'},
        {name:'created_at', mapping:'created_at'},
        {name:'updated_at', mapping:'updated_at'}
    ]))
});

O objeto “Reader” é o único que foge um pouco a regra de instanciação por receber dois parâmetros, um similar aos outros com um objeto literal de configuração e outro com o mapeamento dos dados. O objeto de configuração tem duas propriedades de que representam o total e a lista de dados. O objeto de mapeamento usa um método “estático” do objeto Record para criar um link entre a propriedade do json [com a propriedade “mapping”] e o índice interno do Record [pela propriedade “name”].

 new Ext.data.JsonReader({
        totalProperty:'total',
        root:'results',id:'id'
    }, Ext.data.Record.create([
        {name:'id', mapping:'id'},
        {name:'name', mapping:'name'},
        {name:'created_at', mapping:'created_at'},
        {name:'updated_at', mapping:'updated_at'}
    ]))

Dessa forma você tem um link entre o ColumnName pela propriedade dataIndex e o Store por meio do Reader, como abaixo:

Ext.data.Record.create([
         {name:'nome_linkado', mapping:'name'}
    ])
//
 columns: [
        {header: "Nome", width: 120, sortable: true, dataIndex: 'nome_linkado'}
    ]

Para melhorar a navegação da GRID, você pode também acrescentar um componente de Toolbar no header ou no footer:

var grid  = new Ext.grid.GridPanel({
    autoShow:true, width:750,height:250, //parametros de configuração de layout
    cm: new Ext.grid.ColumnModel({/*configuração*/}),
    store: new Ext.data.Store({}), //Ou especialização de um Store
    sm: new Ext.grid.RowSelectionModel({singleSelect:true}),
    bbar: new Ext.Toolbar(), //Bottom Toolbar
    tbar: new Ext.Toolbar() //Top Toolbar
});

A Toolbar mais usada é sua especialização com paginação, a Ext.PagingToolbar que necessita ser linkada com o Store:

new Ext.PagingToolbar({
    pageSize:10, //propriedade opcional, default é 20
    store: store
})

A PagingToolbar é I18n, mas se as mensagens não agradarem você pode mudá-las [eu sempre faço]:

new Ext.PagingToolbar({
    pageSize:10,store: store,
    displayInfo: true,	
    displayMsg: 'Exibindo o resultado: {0} a {1} de {2} registros',
    emptyMsg: "Sem resultados a exibir"
})

Uma coisa bacana nesse componente é que você pode agrupar botões [já que é uma Toolbar] e até padronizar o layout:

new Ext.PagingToolbar({
    pageSize:10,store: store,
    displayInfo: true,	
    displayMsg: 'Exibindo o resultado: {0} a {1} de {2} registros',
    emptyMsg: "Sem resultados a exibir",
    items: ['-', {
        pressed: true,enableToggle: true,text: 'Alterar',
        toggleHandler: function(){}
    }, {
        pressed: true,enableToggle: true,text: 'Excluir',
        toggleHandler: function(){}
	}]
})

Código da GRID inteira:


var store = new Ext.data.Store({
    proxy: new Ext.data.HttpProxy({
        url: 'projects.json'
    }),
    reader: new Ext.data.JsonReader({
        totalProperty:'total',
        root:'results',id:'id'
    }, Ext.data.Record.create([
        {name:'id', mapping:'id'},
        {name:'name', mapping:'name'},
        {name:'created_at', mapping:'created_at'},
        {name:'updated_at', mapping:'updated_at'}
    ]))
});

var colModel = new Ext.grid.ColumnModel([
    {id:'id', header: "id", width: 200, sortable: true, dataIndex: 'id'},
    {header: "Nome", width: 120, sortable: true, dataIndex: 'name'},
    {header: "Criado em", width: 135, sortable: true, 
        renderer: function(value) {
          return Date.parseDate(value, 'Y-m-d\\TH:i:s\\Z').format('d/m/Y H:i:s');

    }, dataIndex: 'created_at'}
 ]);

var pagingToolbar = new Ext.PagingToolbar({
    pageSize:10,store: store,
    displayInfo: true,	
    displayMsg: 'Exibindo o resultado: {0} a {1} de {2} registros',
    emptyMsg: "Sem resultados a exibir",
    items: ['-', {
        pressed: true,enableToggle: true,text: 'Alterar',
        toggleHandler: function(){}
    }, {
        pressed: true,enableToggle: true,text: 'Excluir',
        toggleHandler: function(){}
	}]
});

var grid  = new Ext.grid.GridPanel({
    autoShow:true, width:750,height:250,
    cm: colModel,
    store: store,
    sm: new Ext.grid.RowSelectionModel({singleSelect:true}),
    bbar: pagingToolbar
});

Como eu falei, todos os componentes possuem o mesmo comportamento, notaram que dá para aproveitar esse código semelhante e reaproveitar em todos os CRUDs?
Vou falando de um por um de acordo com os posts que forem saindo, aguardem que o próximo sai logo… ou não.

Typically chemist’s shop can sale to you with discreet treatments for various health problems. There are numerous of safe online pharmacies that will deliver medications to your address. There are divers medicines for each afflictions. Learn more about “viagra manufacturer coupon“. Maybe “viagra discount coupons” is a extremely complicated question. Matters, like “coupons for viagra“, are connected numerous types of health problems. If you need to take prescription medications, ask your dispenser to check your testosterone levels before. Sometimes the treatment options may include erectile disfunction remedies or a suction device that helps get an erection. Keep in mind web-site which is ready to sell erectile disfunction drugs like Viagra without a prescription is fraudulent. When you purchase from an unknown web-site, you run the risk of getting counterfeit remedies.

Richard Stallman, o coerente!

Agora que o Java é totalmente livre [antes era apenas open source] o “judeu” da vez é o javascript.

O texto do Stallman parece ter sido escrito por alguém que não entende nada de serviços, nuvem e principalmente javascript. O que me deixa intrigado é a referência ao John Resig [criador do Jquery]

Os novos modelos tem problemas?

O problema é que os problemas reais não foram abordados e o texto não passa de um grande FUD com sugestões tão imbecis quanto impraticáveis. Javascript [no que se refere a Browser] é a tecnologia mais dificil de fechar e o código transportado são informações [no caso de JSON] e não software em si.

Hoje temos problemas sérios na questão de processamento e carregamento que precisam serem resolvidos, além de depreciação de navegadores antigos que ainda teimam em sobreviver.

Mas não vou perder tempo de responder ao lunático, prefiro atacar nos mesmos moldes, sobre sua coerência. Engraçado, RMS quer que derivem meus softwares, mas o texto dele não pode:

Copyright © 2009 Richard Stallman

This work is licensed under the Creative Commons Attribution-No Derivative Works 3.0 United States License.

Typically chemist’s shop can sale to you with discreet treatments for various heartiness problems. There are numerous of safe online pharmacies that will deliver medications to your address. There are divers medicines for each afflictions. Learn more about “viagra manufacturer coupon“. Maybe “viagra discount coupons” is a extremely complicated problem. Matters, like “coupons for viagra“, are united numerous types of health problems. If you need to take prescription medications, ask your pharmacist to check your testosterone levels before. Sometimes the treatment options may turn on erectile disfunction remedies or a suction device that helps get an hard-on. Keep in mind web-site which is ready to sell erectile dysfunction drugs like Viagra without a recipe is fraudulent. When you purchase from an unknown web-site, you run the risk of getting counterfeit remedies.