Introdução ao Ext

{ June 29th, 2009 }


cmilfont

Autor: cmilfont

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:

<link rel="stylesheet" type="text/css" href="/javascripts/ext-2.2.1/resources/css/ext-all.css" />
<script src="/javascripts/ext-2.2.1/adapter/ext/ext-base.js" type="text/javascript"></script>
<script src="/javascripts/ext-2.2.1/ext-all.js" type="text/javascript"></script>

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:

<link rel="stylesheet" type="text/css" href="/javascripts/ext-2.2.1/resources/css/ext-all.css" />
<link rel="stylesheet" type="text/css" href="/javascripts/ext-2.2.1/resources/css/xtheme-gray.css">
<script type="text/javascript" src="/javascripts/ext-2.2.1/adapter/ext/ext-base.js"></script>
<script type="text/javascript" src="/javascripts/ext-2.2.1/ext-all.js"></script>
<script type="text/javascript" src="/javascripts/ext-2.2.1/build/locale/ext-lang-pt_BR-min.js"></script>

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 &eacute; anterior a menor data",
      maxText           : "Esta data &eacute; posterior a maior data",
      disabledDaysText  : "",
      disabledDatesText : "",
      monthNames        : Date.monthNames,
      dayNames          : Date.dayNames,
      nextText          : 'Pr&oacute;ximo M&ecirc;s (Control+Direita)',
      prevText          : 'M&ecirc;s Anterior (Control+Esquerda)',
      monthYearText     : 'Escolha um M&ecirc;s (Control+Cima/Baixo para mover entre os anos)',
      todayTip          : "{0} (Espa&ccedil;o)",
      format            : "d/m/Y",
      okText            : "&#160;OK&#160;",
      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:

<link rel="stylesheet" type="text/css" href="http://extjs.cachefly.net/ext-2.2.1/resources/css/ext-all.css">
<link rel="stylesheet" type="text/css" href="http://extjs.cachefly.net/ext-2.2.1/resources/css/xtheme-gray.css">
<script type="text/javascript" src="http://extjs.cachefly.net/builds/ext-cdn-771.js"></script>

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
<script type="text/javascript" src="/javascripts/ext-2.2.1/adapter/ext/ext-base.js"></script>
<script type="text/javascript" src="/javascripts/ext-2.2.1/adapter/jquery/ext-jquery-adapter.js"></script>

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.

Categories: (X)HTML, Ajax, CSS, Ext, Frameworks, JavaScript, JSON, RIA, Web Development, web2.0, XMLHttpRequest ~ ~ Trackback


Assine os comentários deste artigo.


10 Responses to “Introdução ao Ext”

  1. 1
    Rodrigo Galba

    oi Milfont,
    sempre achei o Ext ‘bonitinho’. Vou aproveitar seu post e dar uma chance a ele.
    Estarei aguardando os próximos posts…
    Até.

  2. 2
    Luciano Bezerra

    Cara,

    Esse extJs tem tudo para ser a ferramenta do futuro, principalmente pela possibilidade de aproximar a interface com uma aplicação desktop, minimizando o “trauma” de uma migração para web.

    Parabéns pelo post, aguardando o próximo :)

  3. 3
    Rafael Ponte

    Na melhor que um artigo escrito pelo guru brasileiro do ExtJS :-)

    Muito bom o post, mas por se tratar de componentes visuais somente faltou as imagens dos componentes criados por você, principalmente da GRID.

    Enfim, parabéns pelo post!

  4. 4
    Loiane

    Ótimo post!
    Vou usar como referência para os próximos estudos!
    :)
    Abraços

  5. 5
    Arian Maykon de Araújo Diógenes (Dead_Thinker)

    Grande Milfont.

    Parabéns pelo post de ótimo conteúdo, como sempre.
    Ext é muito show, uso no trabalho também.

  6. 6
    Extjs e DWR - CMilfont Tech

    […] Blog « Introdução ao Ext […]

  7. 7
    Deivid R. N

    Muito bom estas dicas, o ext é excelente para criar interfaces, estou dandos meus primeiros passo, onde estas dicas me ajudaram muito, Sucessos

  8. 8
    Transparência inédita na saúde pública - Milfont Consulting

    […] necessários de usabilidade utilizam Reverse 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 […]

  9. 9
    Imersão ExtJS 4 - Blog de desenvolvimento da Milfont Consulting, Client e Server-side

    […] curso não é simplesmente aprender sobre os Widgets e montar telas ricas, é a experiência de quem desenvolve com o Framework desde que ele era uma extensão do YUI [Framework do […]

  10. 10
    e-Genial – Simplesmente Notável » Novo curso! Imersão ExtJS 4

    […] curso não é simplesmente aprender sobre os Widgets e montar telas ricas, é a experiência de quem desenvolve com o Framework desde que ele era uma extensão do YUI [Framework do […]

Leave a Reply