Jorge is the author of three software development books: "Building a Sencha Touch Application", "How to Build a jQuery Mobile Application", and the "Ext JS 3.0 Cookbook". He runs a software development and developer education shop that focuses on mobile, web and desktop technologies. Jorge is a DZone MVB and is not an employee of DZone and has posted 52 posts at DZone. You can read more from them at their website. View Full User Profile

Adding a Login Screen to a Sencha Touch Application, Part 1

12.06.2012
| 8498 views |
  • submit to reddit

In this tutorial you will learn how to add a Login screen and Login controller to a Sencha Touch application. The sample application you will build will have two views: The Login view and the MainMenu view. This is a popular setup in mobile applications, where you present users with a Login screen, and upon successful login, you display the application’s main menu:

The App’s Directories

Let’s begin by creating the application’s directories, which should look like the following screenshot:

The index.html file is the file that connects all the pieces of the application. In it, you will place the references to the Sencha Touch library, as well as to the JavaScript files containing the application’s code.

Creating the Login View

The view/Login.js is the Login view. As depicted in the wireframe below, you will use a Sencha Touch form panel, an image, a couple of text boxes, and a button to build this view:

Let’s first define the Login view:

Ext.define('Sample.view.Login', {
    extend: 'Ext.form.Panel',
    alias: "widget.loginview",
    config: {
        title: 'Login',
        html: 'Login View'
    }
});

Before adding any components to this view, we are going to open the app.js file and define the application like so:

Ext.application({
    name: 'Sample',
    views: ['Login'],
    launch: function () {

        Ext.Viewport.add([
            { xtype: 'loginview' }
        ]);
    }
});

If you launch the index.html in Google Chrome, you should be able to see the Login view:

The Login View’s Components

Now we can go back to the view/Login.js file and create the components of the view:

Ext.define('Sample.view.Login', {
    extend: 'Ext.form.Panel',
    alias: "widget.loginview",
    requires: ['Ext.form.FieldSet', 'Ext.form.Password', 'Ext.Label', 'Ext.Img'],
    config: {
        title: 'Login',
        items: [
                    {
                        xtype: 'image',
                        src: Ext.Viewport.getOrientation() == 'portrait' ? '../../../img/login.png' : '../../../img/login-small.png',
                        style: Ext.Viewport.getOrientation() == 'portrait' ? 'width:80px;height:80px;margin:auto' : 'width:40px;height:40px;margin:auto'
                    },
                    {
                        xtype: 'label',
                        html: 'Login failed. Please enter the correct credentials.',
                        itemId: 'signInFailedLabel',
                        hidden: true,
                        hideAnimation: 'fadeOut',
                        showAnimation: 'fadeIn',
                        style: 'color:#990000;margin:5px 0px;'
                    },
                    {
                        xtype: 'fieldset',
                        title: 'Login Example',
                        items: [
                            {
                                xtype: 'textfield',
                                placeHolder: 'Username',
                                itemId: 'userNameTextField',
                                name: 'userNameTextField',
                                required: true
                            },
                            {
                                xtype: 'passwordfield',
                                placeHolder: 'Password',
                                itemId: 'passwordTextField',
                                name: 'passwordTextField',
                                required: true
                            }
                        ]
                    },
                    {
                        xtype: 'button',
                        itemId: 'logInButton',
                        ui: 'action',
                        padding: '10px',
                        text: 'Log In'
                    }
         ]
    }
});

The first component in this view is an image, which you can create using the xtype:’image’ config:

{
    xtype: 'image',
    src: Ext.Viewport.getOrientation() == 'portrait' ? '../../../img/login.png' : '../../../img/login-small.png',
    style: Ext.Viewport.getOrientation() == 'portrait' ? 'width:80px;height:80px;margin:auto' : 'width:40px;height:40px;margin:auto'
}

Note that the check on the device’s orientation in order to determine what image file to use. If the viewport’s orientation’s is landscape, you will use a smaller image, which prevents the username and password fields from being pushed outside the viewport:

While it is acceptable for a simple example such as this tutorial, a disadvantage of this approach is that it only works when the view is instantiated. If the device’s orientation changes after the view is created, the view will not re-adjust itself . You should consider using device profiles for production-grade applications where you need the best possible experience for any given platform and orientation.

Right after the image, you will add a label to display any login errors that might occur:

{
    xtype: 'label',
    html: 'Login failed. Please enter the correct credentials.',
    itemId: 'signInFailedLabel',
    hidden: true,
    hideAnimation: 'fadeOut',
    showAnimation: 'fadeIn',
    style: 'color:#990000;margin:5px 0px;'
}

As the hidden config indicates, the label is not visible initially. You will make it visible when an error condition occurs.

The hideAnimation and showAnimation configs allow you to specify the animations used to hide and show the label respectively. Using these animations will make the process of displaying error messages more pleasant to the eyes of the app’s users.

Upon adding the image and label components to the Login view, you need to take care of the fields that you will use to capture a user’s credentials and perform the authentication logic of the app. The username and password fields will live inside a fieldset component, which you will add to the Login view:

{
    xtype: 'fieldset',
    title: 'Login Example',
    items: [
        {
            xtype: 'textfield',
            placeHolder: 'Username',
            itemId: 'userNameTextField',
            name: 'userNameTextField',
            required: true
        },
        {
            xtype: 'passwordfield',
            placeHolder: 'Password',
            itemId: 'passwordTextField',
            name: 'passwordTextField',
            required: true
        }
    ]
}

Placing these text fields inside a fieldset will give the view a nicer look:

Note that you need to set the itemId config for these fields so it is easier to refer to them from within any event handlers that you create in the view. You will see this in play in a few minutes.

The last component in the view is the login button. You will add it using the following code:

{
    xtype: 'button',
    itemId: 'logInButton',
    ui: 'action',
    padding: '10px',
    text: 'Log In'
}

Generating an Application-Wide Sign-In Event

With all the view’s components in place, it is time to create the login logic. The first step would be to create a tap listener for the login button. You will add this listener to the view’s listeners config:

listeners: [{
    delegate: '#logInButton',
    event: 'tap',
    fn: 'onLogInButtonTap'
}]

The listener indicates that tap events on the component with the id logInButton will be handled by the onSingInButtonTap method. You can add the method right after the view’s config object:

onLogInButtonTap: function () {

    var me = this;

    var usernameField = me.down('#userNameTextField'),
        passwordField = me.down('#passwordTextField'),
        label = me.down('#signInFailedLabel');

    label.hide();

    var username = usernameField.getValue(),
        password = passwordField.getValue();

    // Using a delayed task in order to give the hide animation above
    // time to finish before executing the next steps.
    var task = Ext.create('Ext.util.DelayedTask', function () {

        label.setHtml('');

        me.fireEvent('signInCommand', me, username, password);

        usernameField.setValue('');
        passwordField.setValue('');
    });

    task.delay(500);
}

Let’s take a moment to examine onSingInButtonTap. First, you create references to the view, the label, the username and password fields, and the values of the fields:

var me = this,
    usernameField = me.down('#userNameTextField'),
    passwordField = me.down('#passwordTextField'),
    label = me.down('#signInFailedLabel'),
    username = usernameField.getValue(),
    password = passwordField.getValue();

Next comes hiding the label. You need to perform this step because when you enter the handler, you don’t know if the label is already visible due to a prior login error.

The most interesting code in the method is the instantiation of a DelayedTask, and the code this DelayedTask instance executes:

var task = Ext.create('Ext.util.DelayedTask', function () {

    label.setHtml('');

    me.fireEvent('signInCommand', me, username, password);

    usernameField.setValue('');
    passwordField.setValue('');
});

task.delay(500);

The DelayedTask first resets the error label’s html, fires an event to signal the controller that a request to sign in has occurred, and resets the values of the username and password fields.

You may ask why you should use a DelayedTask to run this code when you could just run it in the scope of the onLogInButtonTap method. The reason you are delaying the execution of this code for 500 milliseconds is that you want the label’s hide animation to finish before firing the singInCommand event. Remember that earlier you set this animation using the hideAnimation config of the signInFailedLabel component.

Also, note how the me variable allows you to keep a reference to the Login view and use it inside the DelayedTask’s anonymous function. This function runs in a different execution context, where the this variable is a reference to the Window object. You can easily observe the me and this pointers in Chrome if you set a breakpoint inside the onLogInButtonTap method:

When the debugger breaks, you can create a couple of watches, one for the this variable, and one for the me variable. As depicted below, the this variable is a reference to the Window object, while the me variable is a reference to the Login view, which is a Form panel:

Next Steps

In this article, we set out to learn how to add a Login feature to a Sencha Touch application. We decided to build a small Sencha Touch app that will demonstrate a very basic Login feature, consisting of a Login view and a Login controller, which will authenticate the app’s user and grant her access to other features of the application.

At this point you have created the app’s Login view, which has four main visual components:

  • The error label
  • The username text field
  • The password text field
  • The Logon button

You have also added the onLogInButtonTap method, an internal event handler for the tap event of the Logon button. This method fires the signInCommand application event, which will be captured by the Login controller.

In the next chapter of this series, you will create the Login controller. In it, you will handle the signInCommand event, and create the logic to authenticate the user and grant her access to the application’s main menu Screen.












Published at DZone with permission of Jorge Ramon, author and DZone MVB. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)