Part 2: Sencha Touch Email + Facebook Log in System with PHP & MySQL Backend

14 min read

Originally published July 15, 2014

eBook Cover

Hey there! There is also a (very) expanded version of this tutorial series available that includes the Facebook login portion. As well as getting a convenient PDF, ePub and mobi version, you'll also get a premium video tutorial series covering the development from start to finish and a bunch of other goodies! Check it out here.

Welcome to Part 2 of my tutorial series where I walk you through how to create a Sencha Touch application with a email + facebook signup and authentication system. If you haven't seen it yet, make sure to start at Part 1. Here's the rest of the series:

Part 1: Setting up the Database and API

Part 2: Creating the Application Screens

Part 3: Creating the User Model and API

Part 4: Finishing off the Logic

In the last past of this series we created a basic implementation of the MySQL database and PHP API that will power the application (there's still more to add to these later). Today we will be taking a similar approach, but this time we will be setting up the basic screen flow for the application. At the end of this tutorial, you should have something that looks like this (make sure you're using Chrome or Safari to view):


There will be no logic behind the buttons at this point, everything is simply just linking to the appropriate page. Later in this series we will add functionality to check the users credentials, create accounts and so on.

We're creating a simple application structure with a few different screens. We will have the main view which contains a sliding menu, this will be the screen we want to 'protect' by forcing users to either login or create an account first. Then we will of course have the login view where users can sign in, and a create account view. Some of the screen design and implementation is inspired by this tutorial by Jorge Roman of miamicoder.com. His tutorials were a great help to me when I was starting out (and still are for that matter) so I would highly recommend checking his stuff out.

Ok, let's get into it!

The beginning of any great Sencha Touch application is of course the following command:

sencha generate app SenchaLogin ../SenchaLogin

Create a new application and name it whatever you wish. I'll walk you through the changes you need to make and files you need to create. Let's start with Main.js – you can delete the Main.js file Sencha Touch automatically creates.

Main.js

app\view\Main.js

Create a file called Main.js (or replace the code in the file that is automatically generated) in your view folder and add the following code – we'll talk about it in a second:

Ext.define('SenchaLogin.view.Main', {
  extend: 'Ext.Container',
  xtype: 'main',
  requires: ['Ext.Menu'],
  config: {
    layout: {
      type: 'card',
    },

    items: [
      {
        xtype: 'toolbar',
        docked: 'top',
        title: 'Sencha Login',
        items: [
          {
            xtype: 'button',
            id: 'listButton',
            iconCls: 'list',
            ui: 'plain',
            handler: function () {
              if (Ext.Viewport.getMenus().left.isHidden()) {
                Ext.Viewport.showMenu('left');
              } else {
                Ext.Viewport.hideMenu('left');
              }
            },
          },
        ],
      },
      {
        xtype: 'container',
        html: ['First view'],
      },
    ],

    listeners: {
      initialize: function () {
        Ext.Viewport.setMenu(this.createMenu(), {
          side: 'left',
          reveal: true,
        });
      },
    },
  },

  createMenu: function () {
    //Menu items
    var items = [
      {
        xtype: 'button',
        text: 'Log off',
        iconCls: 'settings',
        scope: this,
        handler: function () {
          var me = this;
          me.fireEvent('signOffCommand', me);
        },
      },
    ];

    //Create menu
    return Ext.create('Ext.Menu', {
      style: 'padding: 0',
      cls: 'nav-menu',
      id: 'menu',
      width: 250,
      scrollable: 'vertical',
      items: items,
    });
  },
});

This view will be the one that contains our sliding menu, the view that a user will see after they log in. We're creating a card layout (so we can easily switch the "First View" container to whatever we wish by using the setActiveItem() method) and adding a toolbar and our initial view to it. We also add the sliding menu in here which contains one item – a log off button. If you would like more of an explanation about using the sliding menu, check out this tutorial.

Login.js

app\view\Login.js

Next up is our Login view, the view that users will first see giving them the option to log in to your application:

Ext.define('SenchaLogin.view.Login', {
  extend: 'Ext.form.Panel',
  xtype: 'login',
  requires: [
    'Ext.form.FieldSet',
    'Ext.form.Password',
    'Ext.Label',
    'Ext.util.DelayedTask',
    'Ext.Img',
  ],
  config: {
    listeners: [
      {
        delegate: '#logInButton',
        event: 'tap',
        fn: 'onLogInButtonTap',
      },
      {
        delegate: '#createAccountButton',
        event: 'tap',
        fn: 'onCreateAccountButtonTap',
      },
    ],

    cls: 'login-view',

    layout: {
      type: 'vbox',
      pack: 'center',
      align: 'center',
    },

    items: [
      {
        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: 'image',
        mode: 'image',
        src: 'resources/img/fblogin.png',
        style: 'height: 50px; width: auto; margin: auto',
      },
      {
        xtype: 'fieldset',
        style: 'margin-top: 60px; width: 260px;',
        items: [
          {
            xtype: 'textfield',
            placeHolder: 'email',
            itemId: 'emailTextField',
            name: 'emailTextField',
            required: true,
          },
          {
            xtype: 'passwordfield',
            placeHolder: 'password',
            itemId: 'passwordTextField',
            name: 'passwordTextField',
            required: true,
          },
          {
            xtype: 'button',
            itemId: 'logInButton',
            ui: 'action',
            padding: '10px',
            text: 'Login with Email',
          },
        ],
      },
      {
        xtype: 'container',
        docked: 'bottom',
        styleHtmlContent: true,
        items: [
          {
            xtype: 'button',
            ui: 'confirm',
            itemId: 'createAccountButton',
            text: 'Create Account',
          },
        ],
      },
    ],
  },

  onLogInButtonTap: function () {
    var me = this;

    var emailField = me.down('#emailTextField'),
      passwordField = me.down('#passwordTextField'),
      label = me.down('#signInFailedLabel');

    label.hide();

    var email = emailField.getValue(),
      password = passwordField.getValue();

    //Give hide animation time to finish
    var task = Ext.create('Ext.util.DelayedTask', function () {
      label.setHtml('');
      me.fireEvent('signInCommand', me, email, password);
      emailField.setValue('');
      passwordField.setValue('');
    });

    task.delay(500);
  },

  onCreateAccountButtonTap: function () {
    var me = this;
    me.fireEvent('createAccountCommand', me);
  },

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

In this view we're just creating a few form fields, which have been set up to be made use of later. We're grabbing the values and passing them to our controller through the fireEvent method (we will add the controller shortly) but for now we don't actually care what the values are. We a button that will trigger the sign in process and another button that will launch the CreateAccount view. We've also created a method to display an error if there is any in the future (thanks again to Jorge for that one).

You may also be confused as to what is going on in the listeners config:

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

This is simply a way to listen for events on elements. In the first listener we are saying to listen for the 'tap' event on the element with an id of 'logInButton' and when that event is detected trigger the 'onLogInButtonTap' method. If you want to read more about events and controllers, check out this tutorial.

CreateAccount.js

app\view\CreateAccount.js

The CreateAccount.js view is the last of the screens we will be adding. This is the screen that users will see when they wish to create a new account.

Ext.define('SenchaLogin.view.CreateAccount', {
  extend: 'Ext.form.Panel',
  requires: [
    'Ext.form.FieldSet',
    'Ext.field.Number',
    'Ext.field.Select',
    'Ext.field.DatePicker',
  ],
  xtype: 'createaccount',

  config: {
    scrollable: 'vertical',
    styleHtmlContent: true,

    itemId: 'createaccount',
    layout: {
      type: 'vbox',
    },

    listeners: [
      {
        delegate: '#submitAccountButton',
        event: 'tap',
        fn: 'onSubmitAccountButtonTap',
      },
    ],

    cls: 'login-view',

    items: [
      {
        xtype: 'fieldset',
        items: [
          {
            xtype: 'label',
            html: 'Login failed. Please enter the correct credentials.',
            itemId: 'createFailedLabel',
            hidden: true,
            hideAnimation: 'fadeOut',
            showAnimation: 'fadeIn',
            style: 'color: #990000; margin: 5px 0px;',
          },
          {
            xtype: 'textfield',
            name: 'email',
            itemId: 'email',
            label: 'Email',
            labelAlign: 'top',
          },
          {
            xtype: 'passwordfield',
            name: 'password',
            itemId: 'password',
            label: 'Password',
            labelAlign: 'top',
          },
          {
            xtype: 'passwordfield',
            name: 'passwordConfirm',
            itemId: 'passwordConfirm',
            label: 'Password Confirm',
            labelAlign: 'top',
          },
          {
            xtype: 'button',
            itemId: 'submitAccountButton',
            ui: 'confirm',
            text: 'Create Account',
          },
        ],
      },
    ],
  },

  onSubmitAccountButtonTap: function () {
    var me = this;
    me.hideFailedMessage();
    me.fireEvent('submitAccountCommand', me);
  },

  showFailedMessage: function (message) {
    var label = this.down('#createFailedLabel');
    label.setHtml(message);
    label.show();
  },

  hideFailedMessage: function () {
    var label = this.down('#createFailedLabel');
    label.hide();
  },
});

This view is very similar to the Login view so it shouldn't require any explaining. I don't mean to be presumptuous of course though, if there's anything confusing you please feel free to post in the comments!

Utility.js

\app\util\Utility.js

Time to take a little detour and create a Utility file. We can create utility files like this to create helper functions that we want to use frequently throughout our application or to reference static values. Create a folder called 'util' in your app folder and then create this file inside:

Ext.define('SenchaLogin.util.Utility', {
  singleton: true,

  // This function helps us to control transitions between pages
  showActiveItem: function (parentPanel, childPanel, direction, activeItem) {
    if (parentPanel && childPanel) {
      parentPanel.animateActiveItem(childPanel, {
        type: 'slide',
        direction: direction,
        listeners: {
          animationend: function () {
            if (activeItem) {
              //Having unused components lying around the place can slow
              //down our application. If an activeItem is supplies to this
              //function we destroy it after the animation has finished.
              activeItem.destroy();
            }
          },
        },
      });
    }

    if (!Ext.Viewport.getMenus().left.isHidden()) {
      Ext.Viewport.hideMenu('left');
    }

    return this;
  },
});

Here we are just creating one function, the showActiveItem() function. We're creating this so we can easily transition between the various views we have created. This function will be triggered when a user chooses a menu item, so we're checking to see if the menu is open and closing it after a transition as well.

Login.js

\app\controller\Login.js

Now we're creating our first controller, the Login controller. Save this file in your controller folder.

Ext.define('SenchaLogin.controller.Login', {
  extend: 'Ext.app.Controller',
  util: 'SenchaLogin.util.Utility',
  config: {
    refs: {
      main: 'main',
      login: 'login',
      createAccount: 'createaccount',
    },

    control: {
      main: {
        signOffCommand: 'onSignOffCommand',
      },
      login: {
        signInCommand: 'onSignInCommand',
        createAccountCommand: 'onCreateAccountCommand',
      },
      createaccount: {
        submitAccountCommand: 'onSubmitAccountCommand',
      },
    },
  },

  launch: function () {
    var me = this;
  },

  getSlideLeftTransition: function () {
    return { type: 'slide', direction: 'left' };
  },

  getSlideRightTransition: function () {
    return { type: 'slide', direction: 'right' };
  },

  onSignInCommand: function (view, email, password) {
    var me = this;
    me.signInSuccess();
  },

  signInSuccess: function () {
    console.log('Signed in');
    var loginView = this.getLogin(),
      mainMenuView = this.getMain();

    loginView.setMasked(false);

    Ext.Viewport.animateActiveItem(mainMenuView, this.getSlideLeftTransition());
  },

  signInFailure: function (message) {
    var loginView = this.getLogin();

    loginView.showSignInFailedMessage(message);
    loginView.setMasked(false);
  },

  onSignOffCommand: function () {
    if (!Ext.Viewport.getMenus().left.isHidden()) {
      Ext.Viewport.hideMenu('left');
    }

    Ext.Viewport.animateActiveItem(
      this.getLogin(),
      this.getSlideRightTransition()
    );
  },

  onCreateAccountCommand: function () {
    Ext.Viewport.animateActiveItem(
      this.getCreateAccount(),
      this.getSlideLeftTransition()
    );
  },

  onSubmitAccountCommand: function () {
    var me = this;
    me.signInSuccess();
  },
});

In this file you will notice that we are specifying the utility file at the top that we just created. The purpose of this controller is to listen to events from our views and then take the appropriate action. Right now it's simply listening for the button events and triggering the appropriate screen to show – it will do much more than this later on down the track.

You will also notice the the refs and control. Basically a ref is telling the controller what views to watch, and the control tells the controller where to direct events that it hears. Again if you're interested in learning more about this check out this post.

app.js

app.js

This is the final file we will be touching for this part of the series. There's two changes we're going to have to make to our app.js file to make use of the files we just created. First make sure you include the appropriate views and controllers by adding the following:

views: [
    'Main',
    'Login',
    'CreateAccount'
],

controllers: [
    'Login'
],

and of course we are going to have to change our launch function to add our login views before the main view. Change your launch function to look like this:

launch: function() {
        // Destroy the #appLoadingIndicator element
        Ext.fly('appLoadingIndicator').destroy();

        Ext.Viewport.add([
                {xtype: 'login'},
                {xtype: 'createaccount'},
                {xtype: 'main'}
        ]);
    },

You should now be able to load up your application and switch between the screens as you can in the example at the top of this post. This was a long one and we've made a lot of progress – take a break and then continue on to Part 3 of this series.

If you had any difficulty with this tutorial or have any questions please leave them in the comments below. See you in the next one!