Sencha Touch 2: Exemplo Página de Login

Oi Pessoal,

Recebi alguns pedidos e sugestões de post para o blog que é fazer uma página de Login com Sencha Touch 2. E aqui estou escrevendo um post sobre isso! :)

Hoje então vou mostrar passo a passo como fazer uma página bem simples de login com Sencha Touch - para caso alguém queira usar de base. Não vou falar sobre autenticação, pois senão já entra em um assunto muito amplo e que vai depender da tecnologia usada no server side. Também não vou explicar muito a fundo, pois todos os conceitos abordei nos cursos de Ext JS 4 e Sencha Touch 2, então espero que tenha um conhecimento básico de certas coisas! :)

PS.: para quem já usa o Sencha Architect 3, deixo a dica que o SA3 já vem com um template para Login. O que vou mostrar hoje é um pouco diferente! :)

Views

O projeto consiste em 3 views: a página de login, uma página principal que está vazia (e é onde seria colocado o conteúdo da app) com um botão de logout, e um Viewport que vai ter a página de login e a página principal fazendo o wrapper da app.

Página de Login

A página de Login é apenas um Form Panel com os campos de usuário e senha, conforme screenshot abaixo:

senchatouch-login-01

Note que nas linhas 15 - 21 também temos uma Label hidden que é para mostrar alguma mensagem de erro como usuário e senha inválidos ou algo parecido.

E nas linhas 51 - 55 temos uma função que é para setar a mensagem de erro na Label que descrevemos acima.

Ext.define('MyApp.view.Login', {
    extend: 'Ext.form.Panel',
    alias: 'widget.loginform',

    config: {
        padding: '10px',
        styleHtmlContent: true,
        items: [
            {
                xtype: 'toolbar',
                docked: 'top',
                title: 'Login'
            },
            {
                xtype: 'label',
                hidden: true,
                hideAnimation: 'fadeOut',
                html: 'Por favor, entre com as credenciais corretas',
                itemId: 'failmsg',
                showAnimation: 'fadeIn',
                style: 'color: #990000; margin:5px 0px;'
            },
            {
                xtype: 'fieldset',
                title: 'Login',
                items: [
                    {
                        xtype: 'textfield',
                        itemId: 'user',
                        required: true,
                        placeHolder: 'usuário'
                    },
                    {
                        xtype: 'passwordfield',
                        itemId: 'pass',
                        required: true,
                        placeHolder: 'senha'
                    }
                ]
            },
            {
                xtype: 'button',
                itemId: 'loginbtn',
                padding: '10px',
                ui: 'action-round',
                text: 'Entrar'
            }
        ]
    },

    showSignInFailedMessage: function(message) {
        var label = this.down('#failmsg');
        label.setHtml(message);
        label.show();
    }

});

Página Principal

A página Principal está vazia, mas seria aqui que colocaríamos o conteúdo da app. A página consiste de um Panel com uma Toolbar que contém um botão de Logout.

E apenas para não fica vazia, deixei um HTML bem basicão só para dizer que o usuário está logado! :)

A Página Principal fica assim:

senchatouch-login-03

E o código:

Ext.define('MyApp.view.Main', {
    extend: 'Ext.Panel',
    alias: 'widget.mainpanel',

    config: {
        html: 'Você está logado!',
        styleHtmlContent: true,
        items: [
            {
                xtype: 'toolbar',
                docked: 'top',
                title: 'Minha App',
                layout: {
                    pack: 'end',
                    type: 'hbox'
                },
                items: [
                    {
                        xtype: 'button',
                        itemId: 'logoutbtn',
                        text: 'Logout'
                    }
                ]
            }
        ]
    }

});

Viewport

O Viewport é apenas um container que irá juntar as duas views que já criamos e será o reponsável pela navegação da app. Para isso, vou usar o Card Layout, assim apenas uma view fica visível por vez.

No Card Layout, a primeira view é a view ativa por padrão, então quando o usuário carregar a app, a página de login será a view exibida e a view Main ficará escondida (só para o exemplo viu, isso não é coisa que se deva fazer em app que vai para produção :) ).

Ext.define('MyApp.view.MyViewport', {
    extend: 'Ext.Container',
    alias: 'widget.myviewport',

    requires: [
        'MyApp.view.Login',
        'MyApp.view.Main'
    ],

    config: {
        layout: {
            type: 'card'
        },
        items: [
            {
                xtype: 'loginform'
            },
            {
                xtype: 'mainpanel'
            }
        ]
    }

});

Controller

Ah, o Controller é aquele cara que vai ficar escutando os eventos disparados pelos componentes (views). Para esse exemplo estamos interessados em escutar dois eventos: quando o usuário fazer um tap no botão de Login ou quando fazer um tap no botão de logout (lembrando que o tap seria o click de apps desktop).

Código:

Ext.define('MyApp.controller.Login', {
    extend: 'Ext.app.Controller',

    config: {
        refs: {
            loginForm: {
                selector: 'loginform',
                xtype: 'Ext.form.Panel'
            },
            myViewport: {
                selector: 'myviewport',
                xtype: 'Ext.Container'
            },
            mainPanel: {
                selector: 'mainpanel',
                xtype: 'Ext.Panel'
            }
        },

        control: {
            "loginform #loginbtn": {
                tap: 'onLogInButtonTap'
            },
            "mainpanel #logoutbtn": {
                tap: 'onLogOutButtonTap'
            }
        }
    },

    onLogInButtonTap: function(button, e, eOpts) {
        var form = button.up('loginform'),
            user = form.down('#user'),
            pass = form.down('#pass'),
            label = form.down('#failmsg'),
            me = this;

        label.hide();

        var userName = user.getValue(),
            password = pass.getValue();

        var task = Ext.create('Ext.util.DelayedTask', function () {
            label.setHtml('');
            me.onSignInCommand(form, userName, password);
            user.setValue('');
            pass.setValue('');
        });

        task.delay(500);
    },

    onLogOutButtonTap: function(button, e, eOpts) {
        var me = this,
            login = me.getLoginForm(),
            myViewport = me.getMyViewport();

        myViewport.animateActiveItem(login, {type: 'slide', direction: 'right'});
    },

    onSignInCommand: function(login, username, password) {
        var me = this;

        if (username.length === 0 || password.length === 0) {
            login.showSignInFailedMessage('Por favor, informe seu usuário e senha.');
            return;
        }

        login.setMasked({
            xtype: 'loadmask',
            message: 'Signing In...'
        });

        if (username.length > 0 || password.length > 0) {
            me.signInSuccess();
        }
    },

    signInSuccess: function() {
        var me = this,
            login = me.getLoginForm(),
            mainPanel = me.getMainPanel(),
            myViewport = me.getMyViewport();

        login.setMasked(false);
        myViewport.animateActiveItem(mainPanel, {type: 'slide', direction: 'left'});
    }

});

Vamos lá!

Linhas 5 - 17 : temos as refs, que são apelidos criados para nos ajudar a referenciar esses componentes no nosso código. Essas refs são similar a usar Ext.ComponentQuery.query('selector')[0]. Por isso precisamos ter certeza de ter apenas 1 instância desses componentes no nosso código. Como selector usei o próprio alias de cada view.

Linhas 21 - 27 : aqui estamos escutando os eventos tap dos botões login e logout. Usei o itemId dos botões e para diminuir o escopo de busca do Sencha Touch, declarei também o componente (xtype/alias) que contém esses botões.

Linhas 30 - 50 : essa é a função que é executada quando o usuário faz um tap no botão Login. A idéia geral é verificar se o usuário e senha foram digitados pelo usuário (não estão vazios - pelo menos 1 char).

  • Linha 31 - obter a referência do Form
  • Linha 32 - com a referência do Form, pegamos a referência do campo Usuário
  • Linha 33 - com a referência do Form, pegamos a referência do campo Senha
  • Linha 34 - com a referência do Form, pegamos a referência da label para caso exista necessidade de exibir uma mensagem de erro
  • Linha 37 - escondemos da Label de erro - caso já tenha acontecido um erro em outra ocasião
  • Linhas 39 e 40 - obtemos o valor inputado de usuário e senha
  • Linhas 42 - apenas para fazer um charme, na hora de fazer o login temos um pequeno delay
  • Linha 49 - instanciamos o delay
  • Linhas 43, 45 e 46 - limpamos os campos de usuário e senha, e a label de erro
  • Linha 44 - chamada da função onSignInCommand passando o Form, e campos de usuário e senha como parâmetros

Linhas 60 - 76 : função que apenas valida se usuário e senha são válidos. Não estou fazendo nenhum verificação com o server, mas seria aqui que enviaria para o server para verificar as credenciais.

  • Linha 63 a 66: se usuário e senha estão em branco, atualizados a mensagem de erro e mostramos na tela e também saímos da função terminando a execução.

senchatouch-login-02

  • Linhas 68 a 71 - apenas uma graça mascarando a tela de login
  • Linhas 73 a 75 - Se usuário e senha não estão vazios, faz o login chamando a função signInSuccess

Linhas 78 - 86 : apenas troca a view ativa do Viewport pela View Main.

  • Linha 80 - obtém a referência do Form (poderíamos ter passado como parâmetro através da função anterior também)
  • Linha 81 - obtém a referência da View da página principal
  • Linha 82 - obtém a referência do Viewport
  • Linha 84 - remove a máscara que adicionamos nas linhas 68 a 71
  • Linha 85 - apenas troca a view ativa do ViewPort para a view principal fazendo uma animação para ficar bonitinho! :)

Linhas 52 - 58 : função que faz logout. Apenas coloca a view Login como view ativa do Viewport.

Download Código

O código está disponível no github: https://github.com/loiane/sencha-touch2-examples-architect/tree/master/SenchaTouchLogin

Até a próxima! :)