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 é 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:
<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, JSON, JavaScript, RIA, Web Development, XMLHttpRequest, web2.0 ~ ~ Trackback

June 29th, 2009 at 7:44 am
oi Milfont,
sempre achei o Ext ‘bonitinho’. Vou aproveitar seu post e dar uma chance a ele.
Estarei aguardando os próximos posts…
Até.
June 29th, 2009 at 7:46 am
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
June 29th, 2009 at 8:39 am
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!
June 29th, 2009 at 8:40 am
Ótimo post!

Vou usar como referência para os próximos estudos!
Abraços
June 29th, 2009 at 6:29 pm
Grande Milfont.
Parabéns pelo post de ótimo conteúdo, como sempre.
Ext é muito show, uso no trabalho também.
July 2nd, 2009 at 2:40 pm
[...] Blog « Introdução ao Ext [...]
July 12th, 2009 at 7:15 am
Muito bom estas dicas, o ext é excelente para criar interfaces, estou dandos meus primeiros passo, onde estas dicas me ajudaram muito, Sucessos
March 7th, 2010 at 4:13 am
[...] 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 [...]