Toda aplicação inicia-se por uma tela de autenticação. Partindo desse pressuposto que escrevo esse post, que ensina tanto como criar uma tela de login, como também criar uma extensão. Para essa extensão segui uma lógica um pouco diferente: imaginei como eu queria que fosse o produto final e então desenvolvi tendo esse modelo como meta. Anunciei no twitter que faria uma tela de login configurável em quatro linhas mas acabei com uma configuração de uma linha somente! O produto final que imagine foi:
new Ext.ux.LoginWindow({ url: 'login.ajax.php' }).show();
Só isso e mais nada! Toda validação, mostrar mensagens, gerenciar layout da janela, tudo está pré-configurado. Ultimamente tenho estudado um pouco de ruby on rails (linguagem/framework fantásticos) e um dos conceitos chave é “Convenção sobre configuração”. Foi algo que tentei implementar nessa extensão. Não se assuste, a nossa interface vai ter uma grande abertura para personalizações, mas se você não quiser a tela ainda assim vai funcionar 100%! :D
Preparando terreno
Como todos os outros tutoriais, me beneficio da estrutura da pasta examples do ext. Nela vamos criar um sub-diretório loginWindow.
Esse tutorial aborda somente a criação do plugin, então os arquivos PHP e as imagens que usaremos você pode obter no pacote completo disponÃvel no final do fonte. Sugiro extrair somente esses recursos, deixe para criar a extensão você mesmo com a ajuda passo-a-passo desse tutorial. Ver o código final vai tirar um pouco a graça da coisa (:
Ext.ux.LoginWindow
Dentro da pasta Ext.ux.LogWindow crie o arquivo .js e .css correspondentes com a imagem anterior. O primeiro passo de uma extensão é criar sua estrutura básica decidindo a que classe ela vai estender. Nesse caso, iremos estender de Ext.Window
Ext.ux.LoginWindow = Ext.extend(Ext.Window,{ /* aqui vai todo o codigo */ });
Em seguida, vamos começar a pré-configurar essa janela, adicionando tÃtulo, dimensões, e demais propriedades
Ext.ux.LoginWindow = Ext.extend(Ext.Window,{
//Config Options {
iconCls : 'ico-cadeado'
,layout : 'form'
,bodyStyle : 'padding:10px;'
,title : 'Autenticação'
,labelAlign : 'right'
,closable : false
,constrain : true
,width : 300
,height : 140
,labelWidth : 45
,minHeight : 140
,minWidth : 220
//}
});
Você já pode experimentar utilizar a extensão sem problemas, apesar dela ainda não estar funcional. É uma boa prática programar e testar, programar e testar, para que caso um erro ocorra você saber facilmente qual alteração ocasionou o erro.
Agora então devemos criar os campos de login e senha, e também o botão para autenticar. Tudo isso faremos no método initComponent, o método que na cadeia dos componentes é responsável pela sua inicialização. Vamos também permitir ao programador personalizar esses campos, caso ele deseje personalizar uma label, emptyText, width, style, etc…Faremos isso criando 2 config options chamados loginField e senhaField, que irão sobrescrever as regras que definimos. Faremos ainda mais e vamos permitir que essas configurações sejam string, assumindo assim o label do campo. Se testarmos novamente poderemos ver os componentes já na tela.
//Inits {
,initComponent: function()
{
if(Ext.isString(this.loginField))
this.loginField = {fieldLabel: this.loginField};
if(Ext.isString(this.senhaField))
this.senhaField = {fieldLabel: this.senhaField};
Ext.apply(this,{
defaults:{
anchor: '-18'
}
,items : [
Ext.apply({
xtype : 'textfield'
,fieldLabel : 'Login'
,emptyText : 'Informe seu login'
,msgTarget : 'side'
,itemId : 'txtLogin'
,allowBlank : false
,selectOnFocus : true
,enableKeyEvents: true
,listeners : {
scope : this
,'keyup': this._onTxtKeyUp
}
},this.loginField)
,
Ext.apply({
xtype : 'textfield'
,inputType : 'password'
,fieldLabel : 'Senha'
,emptyText : '*fakepass*'
,msgTarget : 'side'
,itemId : 'txtSenha'
,allowBlank : false
,selectOnFocus : true
,enableKeyEvents: true
,listeners : {
scope : this
,'keyup': this._onTxtKeyUp
}
},this.senhaField)
]
,buttons: [{
xtype : 'button'
,text : 'Entrar'
,iconCls: 'ico-app-go'
,scope : this
,handler: this._onBtnEntrarClick
}]
});
Ext.ux.LoginWindow.superclass.initComponent.call(this);
}
//}
Caso o servidor não autentique o usuário iremos mostrar uma mensagem de erro ao lado do botão de autenticar. Para isso iremos manipular um pouco os elementos DOM. Criaremos ao lado do botão uma div que irá conter as mensagens de erro. Faremos uso do método onRender, sobrescrevendo-o:
//Overrides {
,onRender: function()
{
Ext.ux.LoginWindow.superclass.onRender.apply(this, arguments);
var btnCt = this.footer.child('.x-panel-btns');
this._errorCt = btnCt.insertFirst({
cls: 'error-msg'
,cn : ''
});
}
//}
Tendo feito tudo isso, basta agora criar o método que lança a requisição ao servidor e resgata sua resposta. Em complemento adiciono o listener dos campos, para que ao pressionar a tecla enter a autenticação também seja disparada. Também iremos permitir que o programador adicione mais parâmetros a requisição através do config options params.
//Listeners{
,_onTxtKeyUp: function(txt,e)
{
if(e.getKey() === e.ENTER)
{
e.stopEvent();
this._onBtnEntrarClick();
}
}
,_onBtnEntrarClick: function()
{
var txtLogin = this.getComponent('txtLogin');
var txtSenha = this.getComponent('txtSenha');
if(!txtLogin.isValid() && !txtSenha.isValid())
return false;
this.buttons[0].disable();
Ext.Ajax.request({
url : this.url
,method : 'POST'
,scope : this
,params : Ext.applyIf({
login: txtLogin.getValue()
,senha: txtSenha.getValue()
},this.params)
,success: function(response)
{
response = Ext.decode(response.responseText);
if( response.success )
{
this.el.mask();
window.location.href = response.redirect||this.redirectUrl;
}
else
{
this._errorCt.update(response.message||"Login e/ou senha inválidos");
this._errorCt.fadeIn();
txtLogin.focus();
}
}
,callback: function()
{
this.buttons[0].enable();
}
});
}
//}
Nossa extensão está pronta! A requisição é feita, e o servidor deve responder um json com a propriedade success. Caso verdadeiro, o usuário está autenticado e eu posso redirecioná-lo para url informada pelo servidor, ou uma url padrão informada pelo usuário na definição da janela. Caso falso, uma mensagem de erro, informada pelo servidor ou a padrão, irá ser mostrada ao usuário.
Conclusão
Perceba como fazemos o uso da sobrescrita de métodos para criar extensões. Se você quiser utilizar somente os componentes pré-definidos do Ext você não precisa de muito esforço, mas para extrair ao máximo sua produtividade é preciso estudar a estrutura dos componentes. Eu só sei da existência de um método onRender que pode ser sobrescrito porque um dia eu abri o código fonte de Ext.Window, Ext.Panel, Ext.Component e estudei um pouco. Portanto, não tenha medo de abrir o código fonte do Ext. Seus 500 e poucos KB assustam mas no fim ele é feito do bom e velho javascript que conhecemos bem.
Aproveito o post para anunciar uma novidade do ExtDesenv, estou adicionando a documentação Ext ao site. E mais do que isso, estou adicionando a documentação dessa extensão que acabamos de fazer no site. Além do que, está rolando um projeto para tradução da documentação para o português, quem sabe o extdesenv pode contribuir com o projeto! ;D
E é isso aà galera! Espero ter sido o mais explicativo possÃvel. Caso tenham qualquer dúvida não hesitem em me contactar. Deixo aqui link para a documentação, exemplo on-line e código-completo. Forte abraço e até a próxima!
P.S.: Nosso colega Wemerson Januario já havia criado uma extensão para o mesmo propósito que chamou também de Ext.ux.LoginWindow. Para visualizar sua extensão veja no seu post no fórum. Valeu pelo apoio Wemerson!
Posts relacionados:






Cara, parabéns pelo post e pelo blog!
Fazer extensões das classes do ext é meio complicado no inÃcio, portanto quanto mais exemplos melhor!
muito obrigado!
abraço
Bruno ficou show, assim acho que todo mundo aprende a extender um classe, Parabéns pelo post.
Obrigado pela dedicatoria.
po cara ficou muito show !!!
Ficou muito bom, parabéns pela contribuição.
Esse exemplo nao roda no ExtJS 3.1 ?? :(
Funciona oO Não tem motivo pra não funcionar, eu uso Ext puro, nenhuma extensão ou override foi utilizada. Qqer problema manda um e-mail mais detalhado para contato[at]extdesenv.com.br que eu esclareço melhor. Abs!
Bom, quero agradecer pela contribuição! muito show!
fui tentar rodar com o EXTJS 3.1 e não funcionou! debugando com os Velhos Alerts hehehehe descobrir que o erro ocorre aqui!
var btnCt = this.footer.child('.x-panel-btns');essa linha está retornando null.Também tive este mesmo erro visto no firebug:
var btnCt = this.footer.child('.x-panel-btns');Não sei se o que eu fiz está correto, mas olhei no código e vi que no evento onRender, a parte do rodapé ainda não está completamente feita. Por algum motivo os botões ainda não estão rendenizados.
pesquisei um pouco e descobri o evento afterRender.
substitui o código do onRender pelo afterRender e funcionou no extjs 3.2 usando firefox
,afterRender: function()
{
Ext.ux.LoginWindow.superclass.afterRender.apply(this, arguments);
alert('depois do render');
var btnCt = this.footer.child('.x-toolbar-left');
alert(btnCt);
this._errorCt = btnCt.insertFirst({
cls: 'error-msg'
,cn : ''
});
}
Alguém pode confirmar a solução, por favor? (faz 2 dias q conheci o extjs e não sei se está certo o que fiz)
[]´s
Olá Ricardo! Agradeço demais pela sua dica. Ainda não pude testar mas pelo que vejo o código está tudo ok. Surpreendente para quem está começando já sacar dos métodos internos do Ext!
Abs!
No meu firebug esta aparecendo um erro nesssa linha:
this._errorCt = btnCt.insertFirst({
Pra mim tbm,
erro:
btnCt is null
Alguma dica pro pessoal resolver isso Bruno, abraços até mais
Obrigado Bruno!
Segundo o que conversei com ele, provavelmente na mudança de versao do Ext, eles mudaram a div onde o Bruno exibe a msg de erro, ao lado do botão, ele me disse pra alterar no onRender, em:
var btnCt = this.footer.child(‘.x-panel-btns’);
mudar para:
var btnCt = this.footer;
Obrigado!
mudar para:
var btnCt = this.footer.child(‘.x-panel-btns’);
Bom, não adiantou mto, se fazer isso que citei acima, como a msg vai no footer da janela, o botão é eliminado (some). Alguma sugestão Bruno?
Obrigado!
Até hoje não vi essa tela funcionando.. Nem no exemplo do site…
Será q o autor poderia rrumar isso?
Verdade.. Dá erro mesmo.. E qdo alterado o código pra esse novo o botão desaparece.. E agora José?