The Login Page using Ext JS

Exclusive offer: get 50% off this eBook here
Mastering Ext JS

Mastering Ext JS — Save 50%

Learn how to build powerful and professional applications by mastering the Ext JS framework with this book and ebook

$29.99    $15.00
by Loiane Groner | July 2013 | Open Source Web Development

The article The Login Page using Ext JS by Loiane Groner, author of the book Mastering Ext JS, talks about developing a login page for an application using Ext JS.

It is very common to have a login page for an application, which we can use to control access to the system by identifying and authenticating the user through the credentials presented by him/her. Once the user is logged in, we can track the actions performed by the user. We can also restrain access of some features and screens of the system that we do not want a particular user or even a specific group of users to have access to.

In this article, we will cover:

  • Creating the login page
  • Handling the login page on the server
  • Adding the Caps Lock warning message in the Password field
  • Submitting the form by pressing the Enter key
  • Encrypting the password before sending to the server

(For more resources related to this topic, see here.)

The Login screen

The Login window will be the first view we are going to implement in this project. We are going to build it step by step and it will have the following capabilities:

  • User will enter the username and password to log in
  • Client-side validation (username and password required to log in)
  • Submit the Login form by pressing Enter
  • Encrypt the password before sending to the server
  • Password Caps Lock warning (similar to Windows OS)
  • Multilingual capability

Except for the multilingual capability, we will implement all the other features throughout this topic. So at the end of the implementation, we will have a Login window that looks like the following:

So let's get started!

Creating the Login screen

Under the app/view directory, we will create a new file named Login.js.In this file, we will implement all the code that the user is going to see on the screen.

Inside the Login.js file, we will implement the following code:

Ext.define('Packt.view.Login', { // #1 extend: 'Ext.window.Window', // #2 alias: 'widget.login', // #3 autoShow: true, // #4 height: 170, // #5 width: 360, // #6 layout: { type: 'fit' // #7 }, iconCls: 'key', // #8 title: "Login", // #9 closeAction: 'hide', // #10 closable: false // #11 });

On the first line (#1) we have the definition of the class. To define a class we use Ext.define, followed by parentheses (()), and inside the parentheses we first declare the name of the class, followed by a comma (") and curly brackets ({}), and at the end a semicolon. All the configurations and properties (#2 to #11) go inside curly brackets.

We also need to pay attention to the name of the class. This is the formula suggested by Sencha in Ext JS MVC projects: App Namespace + package name + name of the JS file. we defined the namespace as Packt (configuration name inside the app.js file). We are creating a View for this project, so we will create the JS file under the view package/directory. And then, the name of the file we created is Login.js; therefore, we will lose the .js part and use only Login as the name of the View. Putting all together, we have Packt.view.Login and this will be the name of our class.

Then, we are saying that the Login class will extend from the Window class (#2), because we want it to be displayed inside a window, and not on any other component.

We are also assigning this class an alias (#3). The alias for a class that extends from a component always starts with widget., followed by the alias we want to assign. The naming convention for an alias is lowercase . It is also important to remember that the alias must be unique in an application. In this case we want to assign login as alias to this class so later we can instantiate this same class using its alias (that is the same as xtype). For example, we can instantiate the Login class using four different options:

  • Using the complete name of the class, which is the most used one:

    Ext.create('Packt.view.Login');

  • Using the alias in the Ext.create method:

    Ext.create('widget.login');

  • Using the Ext.widget, which is a shorthand way of using

    Ext.ClassManager.instantiateByAlias: Ext.widget('login');

  • Using the xtype as an item of another component:

    items: [ { xtype: 'login' } ]

In this book we will use the first, third, and fourth options most of the time.

Then we have autoShow configured to true (#4). What happens with the window is that instantiating the component is not enough for displaying it. When we instantiate the window we will have its reference, but it will not be displayed on the screen. If we want it to be displayed we need to call the method show() manually. Another option is to have the autoShow configuration set to true. This way the window will be automatically displayed when we instantiate it.

We also have height (#5) and width (#6) of the window. We set the layout as fit (#7) because we want to add a form inside this window that will contain the username and password fields. And using the fit layout the form will occupy all the body space of the window. Remember that when using the fit layout we can only have one item as a child component.

We are setting an iconCls (#8) property to the window; this way we will have an icon of a key in the header of the window. We can also give a title for the window (#9), and in this case we chose Login. Following is the declaration of the key style used by the iconCls property:

.key { background-image:url('../icons/key.png') !important; }

All the styles we will create to use as iconCls have a format like the preceding one.

And at last we have the closeAction (#10) and closable (#11) configurations. The closeAction configuration will tell if we want to destroy the window when we close it. In this case, we do not want to destroy it; we only want to hide it. The closable configuration tells if we want to display the X icon on the top-right corner of the window. As this is a Login window, we do not want to give this option for the user.

If you would like to, you can also add the resizable and draggable options as false. This will prevent the user to drag the Login window around and also to resize it.

So far, this will be the output we have. A single window with an icon at the top-left corner with a title Login :

The next step is to add the form with the username and password fields. We are going to add the following code to the Login class:

items: [ { xtype: 'form', // #12 frame: false, // #13 bodyPadding: 15, // #14 defaults: { // #15 xtype: 'textfield', // #16 anchor: '100%', // #17 labelWidth: 60 // #18 }, items: [ { name: 'user', fieldLabel: "User" }, { inputType: 'password', // #19 name: 'password', fieldLabel: "Password" } ] } ]

As we are using the fit layout, we can only declare one child item in this class. So we are going to add a form (#12) and to make the form to look prettier, we are going to remove the frame property (#13) and also add padding to the form body (#14). The form's frame property is by default set to false. But by default, there is a blue border that appears if we to do not explicitly add this property set to false.

As we are going to add two fields to the form, we probably want to avoid repeating some code. That is why we are going to declare some field configurations inside the defaults configuration of the form (#15); this way the configuration we declare inside defaults will be applied to all items of the form, and we will need to declare only the configurations we want to customize. As we are going to declare two fields, both of them will be of type textfield. The default layout of the form is the anchor layout, so we do not need to make this declaration explicit. However, we want both fields can occupy all the horizontal available space of the body of the form. That is why we are declaring anchor as 100% (#17). By default, the width attribute of the label of the TextField class is 100 pixels. It is too much space for a label User and Password, so we are going to decrease this value to 60 pixels (#18).

And finally, we have the user text field and the password text field. The configuration name is what we are going to use to identify each field when we submit the form to the server. But there is only one detail missing: when the user types the password into the field the system cannot display its value, we need to mask it somehow. That is why inputType is 'password' (#19) for the password field, as we want to display bullets instead of the original value, and the user will not be able to see the password value.

Now we have improved our Login window a little more. This is the output so far:

Client-side validations

The field component in Ext JS provides some client-side validation capability. This can save time and also bandwidth (the system will only make a server request when it is sure the information has passed the basic validation). It also helps to point out to the user where they have gone wrong in filling out the form. Of course, it is also good to validate the information again on the server side for security reasons, but for now we will focus on the validations we can apply to the form of our Login window.

Let's brainstorm some validations we can apply to the username and password fields:

  • The username and password must be mandatory—how are going to authenticate the user without a username and password?
  • The user can only enter alphanumeric characters (A-Z, a-z, and 0-9) in both the fields.
  • The user can only type between 3 and 25 chars in the username field.
  • The user can only type between 3 and 15 chars in the password field.

So let's add into the code the ones that are common to both fields:

allowBlank: false, // #20 vtype: 'alphanum', // #21 minLength: 3, // #22 msgTarget: 'under' // #23

We are going to add the preceding configurations inside the defaults configuration of the form, as they all apply to both the fields we have. First, both need to be mandatory (#20), we can only allow to enter alphanumeric characters (#21) and the minimum number of characters the user needs to input is three (#22). Then, a last common configuration is that we want to display any validation error message under the field (#23).

And the only validation customized for each field is that we can enter a maximum of 25 characters in the User field:

name: 'user', fieldLabel: "User", maxLength : 25

And a maximum of 15 characters in the Password field:

inputType: 'password', name: 'password', fieldLabel: "Password", maxLength : 15

After we apply the client validations, we will have the following output in case the user went wrong in filling out the Login window:

If you do not like it, we can change the place where the error message appears. We just need to change the msgTarget value. The available options are: title, under, side, and none. We can also show the error message as a tooltip (qtip) or display it in a specific target (inner HTML of a specific component).

Creating custom VTypes

Many systems have a special format for passwords. Let's say we need the password to have at least one digit (0-9), one letter lowercase, one letter uppercase, one special character (@, #, $, %, and so on) and its length between 6 and 20 characters.

We can create a regular expression to validate that the password is entering into the app. And to do this, we can create a custom VType to do the validation for us. Creating a custom VType is simple. For our case, we can create a custom VType called passRegex:

Ext.apply(Ext.form.field.VTypes, { customPass: function(val, field) { return /^((?=.*\d)(?=.*[a-z])(?=.*[A-Z])
(?=.*[@#$%]).{6,20})/.test(val); }, customPassText: 'Not a valid password.
Length must be at least 6 characters and maximum of 20Password
must contain one digit, one letter lowercase, one letter uppercase,
onse special symbol
@#$% and between 6 and 20 characters.', });

customPass is the name of our custom VType, and we need to declare a function that will validate our regular expression. customPassText is the message that will be displayed to the user in case the incorrect password format is entered.

The preceding code can be added anywhere on the code, inside the init function of a controller, inside the launch function of the app.js, or even in a separate JavaScript file (recommended) where you can put all your custom VTypes.

To use it, we simply need to add vtype: 'customPass' to our Password field.

To learn more about regular expressions, please visit http://www.regular-expressions.info/.

Adding the toolbar with buttons

So far we have created the Login window, which contains a form with two fields and it is already being validated as well. The only thing missing is to add the two buttons: cancel and submit .

We are going to add the buttons as items of a toolbar and the toolbar will be added on the form as a docked item. The docked items can be docked to either on the top, right, left, or bottom of a panel (both form and window components are subclasses of panel). In this case we will dock the toolbar to the bottom of the form. Add the following code right after the items configuration of the form:

dockedItems: [ { xtype: 'toolbar', dock: 'bottom', items: [ { xtype: 'tbfill' //#24 }, { xtype: 'button', // #25 itemId: 'cancel', iconCls: 'cancel', text: 'Cancel' }, { xtype: 'button', // #26 itemId: 'submit', formBind: true, // #27 iconCls: 'key-go', text: "Submit" } ] } ]

If we take a look back to the screenshot of the Login screen we first presented at the beginning of this article, we will notice that there is a component for the translation/multilingual capability. And after this component there is a space and then we have the Cancel and Submit buttons. As we do not have the multilingual component yet, we can only implement the two buttons, but they need to be at the right end of the form and we need to leave that space. That is why we first need to add a toolbar fill component (#24), which is going to instruct the toolbar's layout to begin using the right-justified button container.

Then we will add the Cancel button (#25) and then the Submit button (#26). We are going to add icons to both buttons (iconCls) and later, when we implement the controller class, we will need a way to identify the buttons. This is why we assigned itemId to both of them.

We already have the client validations, but even with the validations, the user can click on the Submit button and we want to avoid this behavior. That is why we are binding the Submit button to the form (#27); this way the button will only be enabled if the form has no error from the client validation.

In the following screenshot, we can see the current output of the Login form (after we added the toolbar) and also verify the behavior of the Submit button:

Running the code

To execute the code we have created so far, we need to make a few changes in the app.js file.

First, we need to declare views we are using (only one in this case). Also, as we are going to instantiate using the Login class' xtype, we need to declare this class in the requires declaration:

requires: [ 'Packt.view.Login' ], views: [ 'Login' ],

And the last change is inside the launch function. now we only need to replace the console.log message with the Login instance (#1):

splashscreen.next().fadeOut({ duration: 1000, remove:true, listeners: { afteranimate: function(el, startTime, eOpts ){ Ext.widget('login'); // #1 } } });

Now the app.js is OK and we can execute what we have implemented so far!

Using itemId versus id Ext.Cmp is bad!

Before we create the controller, we will need to have some knowledge about Ext.ComponentQuery selectors. And in this topic we will discuss a subject to help us to understand better why we took some decisions while creating the Login window and why we are going to take some other decisions on the controller topic.

Whenever we can, we will always try to use the itemId configuration instead of id to uniquely identify a component. And here comes the question, why?

When using id, we need to make sure that id is unique, and none of all the other components of the application has the same id. Now imagine the situation where you are working with other developers of the same team and it is a big application. How can you make sure that id is going to be unique? Pretty difficult, don't you think? And this can be a hard task to achieve.

Components created with an id may be accessed globally using Ext.getCmp, which is a short-hand reference for Ext.ComponentManager.get.

Just to mention one example, when using Ext.getCmp to retrieve a component by its id, it is going to return the last component declared with the given id. And if the id is not unique, it can return the component that you are not expecting and this can lead into an error of the application.

Do not panic! There is an elegant solution, which is using itemId instead of id.

The itemId can be used as an alternative way to get a reference of a component. The itemId is an index to the container's internal MixedCollection, and that is why the itemId is scoped locally to the container. This is the biggest advantage of the itemId.

For example, we can have a class named MyWindow1, extending from window and inside this class we can have a button with item ID submit. Then we can have another class named MyWindow2, also extending from window, and also with a button with item ID submit.

Having two item IDs with the same value is not an issue. We only need to be careful when we use Ext.ComponentQuery to retrieve the component we want. For example, if we have a Login window whose alias is login and another screen called the Registration window whose alias is registration. Both the windows have a button Save whose itemId is save. If we simply use Ext.ComponentQuery.query('button#save'), the result will be an array with two results. However, if we narrow down the selector even more, let's say we want the Login window's Save button, and not the Registration window's Save button, we need to use Ext.ComponentQuery.query('login button#save'), and the result will be a single item, which is exactly we expect.

You will notice that we will not use Ext.getCmp in the code of our project. Because it is not a good practice; especially for Ext JS 4 and also because we can use itemId and Ext.ComponentQuery instead. We will understand Ext.ComponentQuery better during the next topic.

Creating the login controller

We have created the view for the Login screen so far. As we are following the MVC architecture, we are not implementing the user interaction on the View class. If we click on the buttons on the Login class, nothing will happen because we have not yet implemented this logic. We are going to implement this logic now on the controller class.

Under the app/controller directory, we will create a new file named Login.js. In this file we will implement all the code related to the events management of the Login screen.

Inside the Login.js file we will implement the following code, which is only a base of the controller class we are going to implement:

Ext.define('Packt.controller.Login', { // #1 extend: 'Ext.app.Controller', // #2 views: [ 'Login' // #3 ], init: function(application) { // #4 this.control({ // #5 }); } });

As usual, on the first line of the class we have its name (#1). Following the same formula we used for the view/Login.js we will have Packt (app namespace) + controller (name of the package) + Login (which is the name of the file), resulting in Packt.controller.Login.

Note that that the controller JS file (controller/Login.js) has the same name as view/Login.js, but that is OK because they are in a different package. It is good to use a similar name for the views, models, stores and controllers because it is going to be easier to maintain the project later. For example, let's say that after the project is in production, we need to add a new button on the Login screen. With only this information (and a little bit of MVC concept knowledge) we know we will need to add the button code on the view/Login.js file and listen to any events that might be fired by this button on the controller/Login.js. Easier maintainability is also a great pro of using the MVC architecture.

The controller classes need to extend from Ext.app.Controller (#2), so we will always use this parent class for our controllers.

Then we have the views declaration (#3), which is where we are going to declare all the views that this controller will care about. In this case, we only have the Login view so far. We will add more views later on this article.

Next, we have the init method declaration (#4). The init method is called before the application boots, before the launch function of Ext.application (app.js). The controller will also load the views, models, and stores declared inside its class.

Then we have the control method configured (#5). This is where we are going to listen to all events we want the controller to react. And as we are coding the events fired by the Login window and its child components, this will be our scope in this controller.

Adding the controller to app.js

Now that we already have a base of the login controller, we need to add it to the app.js file.

We can remove this code, since the controller will be responsible for loading the view/Login.js file for us:

requires: [ 'Packt.view.Login' ], views: [ 'Login' ], And add the controllers declaration: controllers: [ 'Login' ],

And as our project is only starting, declaring the views on the controller classes will help us to have a code more organized, as we do not need to declare all the application's views in the app.js file.

Listening to the button click event

Our next step now is to start listening to the Login window events. First, we are going to listen to the Submit and Cancel buttons.

We already know that we are going to add the listeners inside the this.control declaration. The format that we need to use is the following:

'Ext.ComponentQuery selector': { eventWeWantToListenTo: functionOrMethodWeWantToExecute }

First, we need to pass the selector that is going to be used by the Ext.ComponentQuery class to find the component. Then we need to list the event that we want to listen to. And then, we need to declare the function that is going to be executed when the event we are listening to is fired, or declare the name of the controller method that is going to be executed when the event is fired. In our case, we are going to declare the method only for code organization purposes.

Now let's focus on finding the correct selector for the Submit and Cancel buttons. According to Ext.ComponentQuery API documentation, we can retrieve components by using their xtype (if you are already familiar with jQuery, you will notice that Ext.ComponentQuery selectors are very similar to jQuery selectors' behavior). Well, we are trying to retrieve two buttons, and their xtype is button. We try then the selector button. But before we start coding, let's make sure that this is the correct selector to avoid us to change the code all the time when trying to figure out the correct selector. There is one very useful tip we can try: open the browser console (command editor), type the following command, and click on Run :

Ext.ComponentQuery.query('button');

As we can see in the screenshot, it returned an array of the buttons that were found by the selector we used, and the array contains six buttons; too many buttons and it is not what we want. We want to narrow down to the Submit and Cancel buttons.

Let's try to draw a path of the Login window using the components xtype we used:

We have a Login window (xtype: login or window), inside the window we have a form (xtype: form), inside the form we have a toolbar (xtype: toolbar), and inside the toolbar we have two buttons (xtype: button). Therefore, we have login-form-toolbar-button. However, if we use login-form-button we will have the same result, because we do not have any other buttons inside the form. So we can try the following command:

Ext.ComponentQuery.query('login form button');

So let's try this last selector on the command editor:

Now the result is an array of two buttons and these are the buttons that we are looking for! There is still one detail missing: if we use the login form button selector, it will listen to the click event (which is the event we want to listen to) of both buttons. When we click on the Cancel button one thing should happen (reset the form) and when we click on the Submit button, another thing should happen (submit the form to the server to validate the login). So we still want to narrow down the selector even more, until it returns the Cancel button and another selector that will return the Submit button.

Going back to the view/Login code, notice that we declared a configuration named itemId to both buttons. We can use these itemId configurations to identify the buttons in a unique way. According to the Ext.ComponentQuery API docs, we can use # as a prefix of itemId. So let's try the following command on the command editor to get the Submit button reference:

Ext.ComponentQuery.query('login form button#submit');

The output will be only one button as we expect:

Now let's try the following command to retrieve the Cancel button reference:

Ext.ComponentQuery.query('login form button#cancel');

The output will be only one button as we expect:

So now we have the selectors that we were looking for! Console command editor is a great tool and using it can save us a lot of time when trying to find the exact selector that we want, instead of coding, testing, not the selector we want, code again, test again, and so on.

Could we use only button#submit or button#cancel as selectors? Yes, we could use a shorter selector. However, it would work perfectly for now. As the application grows and we declare many more classes and buttons, the event would be fired for all buttons that have the itemId named submit or cancel and this could lead to an error in the application. We always need to remember that itemId is scoped locally to the container. By using login form button as the selector, we make sure that the event will come from the button from the Login window.

So let's implement the code inside the controller class:

init: function(application) { this.control({ "login form button#submit": { // #1 click: this.onButtonClickSubmit // #2 }, "login form button#cancel": { // #3 click: this.onButtonClickCancel // #4 } }); }, onButtonClickSubmit: function(button, e, options) { console.log('login submit'); // #5 }, onButtonClickCancel: function(button, e, options) { console.log('login cancel'); // #6 }

In the preceding code, we have first the listener to the Submit button (#1), and on the following line we say that we want to listen to the click event, and then, when the click event of the Submit button is fired, the onButtonClickSubmit method should be executed (#2).

Then we have the same for the Cancel button: we have the listener to the Cancel button (#3), and on the following line we say that we want to listen to the click event, and then, when the click event of the Cancel button is fired, the onButtonClickCancel method should be executed (#4).

Next, we have the declaration of the methods onButtonClickSubmit and onButtonClickCancel. For now, we are only going to output a message on the console to make sure that our code is working. So we are going to output login submit (#5) in case the user clicks on the Submit button, and login cancel (#6) in case the user clicks on the Cancel button.

But how do you know which are the parameters the event method can receive? You can find the answer to this question in the documentation. If we take a look at the click event in the documentation, this is what we will find:

This is exactly what we declared. For all the other event listeners, we will go to the docs and see which are the parameters the event accepts, and then list them as parameters in our code. This is also a very good practice. We should always list out all the arguments from the docs, even if we are only interested in the first one. This way we always know that we have the full collection of the parameters, and this can come very handy when we are doing maintenance of the application.

Let's go ahead and try it. Click on the Cancel button and then on the Submit button. This should be the output:

Cancel button listener implementation

Let's remove the console.log messages and add the code we actually want the methods to execute. First, let's work on the onButtonClickCancel method. When we execute this method, we want it to reset the form.

So this is the logic sequence we want to program:

  1. Get the Login form reference.
  2. Call the method getForm, which is going to return the form basic class.
  3. Call the reset method to reset the form.

The form basic class provides input field management, validation, submission, and form loading services. The Ext.form.Panel class (xtype: form) works as the container, and it is automatically hooked up with an instance of Ext.form.Basic. That is why we need to get the form basic reference to call the reset method.

If we take a look at the parameters we have available on the onButtonClickCancel method, we have: button, e, and options, and none of them provides us the form reference.

So what can we do about it? We can use the up method from the Button class (inherited from the AbstractComponent class). With this method, we can use a selector to try to retrieve the form. The up method navigates up the component hierarchy, searching from an ancestor container that matches the passed selector.

As the button is inside a toolbar that is inside the form we are looking for, if we use button.up('form'), it will retrieve exactly what we want. Ext JS will see what is the first ancestor in the hierarchy of the button and will find a toolbar. Not what we are looking for. So it goes up again and it will find a form, which is what we are looking for.

So this is the code that we are going to implement inside the onButtonClickCancel method:

button.up('form').getForm().reset();

Some people like to implement the toolbar inside the window instead of the form. No problem at all, it is only a matter of how you like to implement it. In this case, if the toolbar that contains the Submit button is inside the Window class we can use:

button.up('window').down('form').getForm().reset()

And we will have the same result!

Submit button listener implementation

Now we need to implement the onButtonClickSubmit method. Inside this method, we want to program the logic to send the username and password values to the server so that the user can be authenticated.

We can implement two programming logics inside this method: the first one is to use the submit method that is provided by the form basic class and the second one is to use an Ajax call to submit the values to the server. Either way we will achieve what we want to do. However, there is one detail that we need to know prior to making this decision: if using the submit method of the form basic class, we will not be able to encrypt the password before we send it to the server, and if we take a look at the parameters sent to the server, the password will be a plain text, and this is not good. Using the Ajax request will result the same; however, we can encrypt the password value before sending to the server. So apparently, the second option seems better and that is the one that we will implement.

So to summarize, following are the steps we need to perform in this method:

  • Get the Login form reference
  • Get the Login window reference (so that we can close it once the user has been authenticated)
  • Get the username and password values from the form
  • Encrypt the password
  • Send login information to the server
  • Handle the server response
    • If user is authenticated display application
    • If not, display an error message

First, let's get the references that we need:

var formPanel = button.up('form'), login = button.up('login'), user = formPanel.down('textfield[name=user]').getValue(), pass = formPanel.down('textfield[name=password]').getValue();

To get the form reference, we can use the button.up('form') code that we already used in the onButtonClickCancel method; to get the Login window reference we can do the same thing, only changing the selector to login or window. Then to get the values from the User and Password fields we can use the down method, but this time the scope will start from the form reference. For the selector we will use the text field xtype, and to make sure we are retrieving the text field we want, we can create an itemId attribute, but there is no need for it. We can use the name attribute since the user and password fields have different names and they are unique within the Login window. To use attributes within a selector we must wrap it in brackets.

The next step is to submit the values to the server:

if (formPanel.getForm().isValid()) { Ext.Ajax.request({ url: 'php/login.php', params: { user: user, password: pass } }); }

If we try to run this code, the application will send the request to the server, but we will get an error as the response because we do not have the login.php page implemented yet. That's OK because we are interested in other details right now.

With Firebug or Chrome Developer Tools enabled, open the Net tab and filter by the XHR requests. Make sure to enter a username and password (any valid value so that we can click on the Submit button). This will be the output:

We still do not have the password encrypted. The original value is still being displayed and this is not good. We need to encrypt the password.

Under the app directory, we will create a new folder named util where we are going to create all the utility classes. We will also create a new file named MD5.js; therefore, we will have a new class named Packt.util.MD5. This class contains a static method called encode and this method encodes the given value using the MD5 algorithm. To understand more about the MD5 algorithm go to http://en.wikipedia.org/wiki/MD5. As Packt.util.MD5 is big, we will not list its code here, but you can download the source code of this book from http://www.packtpub.com/mastering-ext-javascript/book or get the latest version at https://github.com/loiane/masteringextjs).

If you would like to make it even more secure, you can also use SSL and ask for a random salt string from the server, salt the password and hash it. You can learn more about it at one the following URLs: http://en.wikipedia.org/wiki/Transport_Layer_Security and http://en.wikipedia.org/wiki/Salt_(cryptography).

A static method does not require an instance of the class to be able to be called. In Ext JS, we can declare static attributes and methods inside the static configuration. As the encode method from Packt.util.MD5 class is static, we can call it like Packt.util.MD5.encode(value);.

So before Ext.Ajax.request, we will add the following code:

pass = Packt.util.MD5.encode(pass);

We must not forget to add the Packt.util.MD5 class on the controller's requires declaration (the requires declaration is right after the extend declaration):

requires: [ 'Packt.util.MD5' ],

Now, if we try to run the code again, and check the XHR requests on the Net tab, we will have the following output:

The password is encrypted and it is much safer now.

Mastering Ext JS Learn how to build powerful and professional applications by mastering the Ext JS framework with this book and ebook
Published: July 2013
eBook Price: $29.99
Book Price: $49.99
See more
Select your format and quantity:

Creating the User and Groups tables

Before we start coding the login.php page, we need to add two tables to the sakila database. These two tables are going to represent the users and also the groups that the users can belong to. In our project, a user can belong to only one group, as shown in the following screenshot:

First, we are going to create the Groups table:

CREATE TABLE IF NOT EXISTS `sakila`.`Groups` ( `id` INT NOT NULL AUTO_INCREMENT , `name` VARCHAR(45) NOT NULL , PRIMARY KEY (`id`) ) ENGINE = InnoDB;

Then, we are going to create the User table containing the indexes, and will also create the foreign key to the Groups table:

CREATE TABLE IF NOT EXISTS `sakila`.`User` ( `id` INT NOT NULL AUTO_INCREMENT , `name` VARCHAR(100) NOT NULL , `userName` VARCHAR(20) NOT NULL , `password` VARCHAR(35) NOT NULL , `email` VARCHAR(100) NOT NULL , `picture` VARCHAR(100) NULL , `Group_id` INT NOT NULL , PRIMARY KEY (`id`, `Group_id`) , UNIQUE INDEX `userName_UNIQUE` (`userName` ASC) , INDEX `fk_User_Group1_idx` (`Group_id` ASC) , CONSTRAINT `fk_User_Group1` FOREIGN KEY (`Group_id` ) REFERENCES `sakila`.`Groups` (`id` ) ON DELETE NO ACTION ON UPDATE NO ACTION) ENGINE = InnoDB;

The next step is to insert some data into these tables:


INSERT INTO `sakila`.`Groups` (`name`) VALUES ('admin'); INSERT INTO `sakila`.`User`
(`name`, `userName`, `password`, `email`, `Group_id`) VALUES ('Loiane Groner', 'loiane',
'e10adc3949ba59abbe56e057f20f883e', 'me@loiane.com', '1');

As the password will be encrypted and saved in the database, the value e10adc3949ba59abbe56e057f20f883e corresponds to the value 123456.

Now we are ready to start developing the login.php page.

Handling the login page on the server

Since we have part of the Ext JS code to send the login information to the server, we can implement the server-side code. we are going to use PHP to implement the server-side code. But if you do not know PHP, do not worry because the code is not going to be complicated and we are going to use pure PHP as well. The goal is to focus on the programming logic we need to use on the server side; this way we can apply the same programming logic to any other server-side language that you like to use (Java, .NET, Ruby, Python, and so on).

Connecting to the database

The first step is to create the file that is going to be responsible to connect to the database. We are going to reuse this file in almost every PHP page that we are going to develop.

Create a new folder named php under the project's root folder, and under php create a new folder named db. Then, create a new file named db.php:

<?php $server = "127.0.0.1"; $user = "root"; $pass = "root"; $dbName = "sakila"; $mysqli = new mysqli($server, $user, $pass, $dbName); /* check connection */ if ($mysqli->connect_errno) { printf("Connect failed: %s\n", mysqli_connect_error()); exit(); } ?>

The connection is pretty straightforward. We simply need to inform the server (which is going to be localhost), the database username and password, and also the database name that we want to connect to. And at last, we can check if the connection has happened successfully or has any error occurred.

To learn more about mysqli, please go to http://php.net/manual/en/book.mysqli.php.

login.php

Finally, we can create the login.php file under the php folder. So let's start implementing it:

require("db/db.php"); // #1 session_start(); // #2 $userName = $_POST['user']; // #3 $pass = $_POST['password']; // #4 $userName = stripslashes($userName); // #5 $pass = stripslashes($pass); // #6 $userName = $mysqli->real_escape_string($userName); // #7 $pass = $mysqli->real_escape_string($pass); // #8 $sql = "SELECT * FROM USER WHERE userName='$userName'
and password='$pass'"; // #9

First, we need to require the db.php file to connect to the database (#1). Then, we start a session (#2) where we are going to store the username on the session later.

The next step is to retrieve the user and password values sent by the Ext.Ajax.request (#3 and #4).

The stripslashes function removes the backslashes from the given string (#5 and #6). For example, if the user value is Loiane\'s, the return of the stripslashes function will be Loiane's.

Then, we prepare the $username and $pass variables for the SQL statement by using the function real_escape_string (#7 and #8), which escapes special characters in a string for use in an SQL statement.

Next, we prepare the SQL query that is going to be executed (#9). It is a simple SELECT statement that is going to return a result matching the given username and password.

Let's continue with the next part of the code:

$result = array(); // #10 if ($resultdb = $mysqli->query($sql)) { // #11 $count = $resultdb->num_rows; // #12 if($count==1){ $_SESSION['authenticated'] = "yes"; // #13 $_SESSION['username'] = $userName; // #14 $result['success'] = true; // #15 $result['msg'] = 'User authenticated!'; // #16 } else { $result['success'] = false; // #17 $result['msg'] = 'Incorrect user or password.'; // #18 } $resultdb->close(); // #19 }

In this second part of the login.php code, we first need to create a result variable (#10) that is going to store the result information that we are going to send back to Ext JS.

Next, we need to execute the SQL query and we are going to store the result set into the resultdb variable (#11). Then, we are going to store if the result set returned any rows within the result set (#12).

Now comes the most important part of the code. We are going to verify if the result set returned any rows. As we passed the username and password, if the username and password match with the information we have on the database, the number of rows returned within the result set must be exactly 1. So if the number of rows is equal to 1, we are going to store the username of the authenticated user (#13) in the session, and also store the information that the user is authenticated (#14).

We also need to prepare the result that we are going to return to Ext JS. We are going to send back two pieces of information: the first one is if the user is authenticated (#15)—in this case true—and we can also send back a message (#16).

If the username and password do not match (number of rows returned within the result set is different from 1), we are also going to send back a message to Ext JS saying the username or password informed by the user is incorrect (#18). Therefore, the success information will be false. Then, we need to close the result set (#19).

Now, the third and last part of the code of login.php:

$mysqli->close(); // #20 echo json_encode($result); // #21

We need to close the database connection (#20) and we are going to encode the result that we are going to send back to Ext JS in the JSON (JavaScript Object Notation ) format (#21).

And now the login.php code is complete. However, we should not forget to wrap the preceding code within <?php and ?>.

Handling the return of the server – logged in or not?

We already took care of the server-side code. Now we need to go back to the Ext JS code and handle the response from the server.

But first, we need to understand a very important concept that usually confuses most of the Ext JS developers.

Success versus failure

The Ext.Ajax class performs all Ajax requests done by Ext JS. If we look at the documentation, this class has three events, namely beforerequest, requestcomplete, and requestexception.

The event beforerequest is fired before the request. The requestcomplete event is fired when Ext JS is able to get a response from the server and the requestexception event is fired when an HTTP error status is returned from the server.

Now, let's go back to the Ext.Ajax.request call. We can pass some options to the request, including the URL we want to connect to, parameters, and other options, including the success and failure functions. Now this is where the misunderstanding happens. Some developers understand that if the actions happened successfully on the server we usually return success = true from the server. If something went wrong, we return success = false. Then, on the success function the success = true is handled and on the failure function the success = false is handled. This is wrong and it is not how Ext JS works.

For Ext JS, success is when the server returns a response (success = true or success = false does not matter) and failure is when the server returns an HTTP error status. This means that if the server was able to return a response, we will handle this response on the success function (and we will need to handle if the success information is true or false); on the failure message we need to inform the user that something went wrong and the user should contact the system administrator.

We will implement the failure function first. So inside the Ext.Ajax.request call, we will add the following code:

failure: function(conn, response, options, eOpts) { Ext.Msg.show({ title:'Error!', msg: conn.responseText, icon: Ext.Msg.ERROR, buttons: Ext.Msg.OK }); }

We are going to display an alert to the user with an error icon and an OK button with the HTTP status error information.

To reproduce an error so that the requestexception event can be fired, we can rename the login.php file to something else (for example, login_.php) only for testing purposes. Then, we can execute the code and have the following output:

And this is all we need for the failure function. We can reuse this code in all failure functions for all Ext.Ajax.request calls in our project.

Now, let's focus on the success function:

success: function(conn, response, options, eOpts) { var result = Ext.JSON.decode(conn.responseText, true); // #1 if (!result){ // #2 result = {}; result.success = false; result.msg = conn.responseText; } if (result.success) { // #3 login.close(); // #4 Ext.create('Packt.view.MyViewport'); // #5 } else { Ext.Msg.show({ title:'Fail!', msg: result.msg, // #6 icon: Ext.Msg.ERROR, buttons: Ext.Msg.OK }); } },

The first thing we need to do is to decode the JSON message (#1) that we received from the server. If we log the conn parameter sent to the success function (console.log(conn)), this will be the output we will get on the console:

So when we decode conn.responseText, which is where the information we want to retrieve is, we will be able to access result.success and result.msg. We also need to be careful about one detail: we do not know what is going to be returned from the server, we always hope that is our success and msg information; however, we cannot be sure of it. If any other MySQL error is returned, it is going to be returned inside conn.responseText as well, and it cannot have the JSON format we are expecting. If this happens, the Ext.JSON.decode function will fail and it will throw an exception. We can silence the exception (passing true as the second parameter to the Ext.JSON.decode function, and the result variable will have value null), but we still need to handle it. And that is what we are doing when checking if the result variable is null (#2). If it is null, we are instantiating the result variable and assigning some values (the msg will receive the error sent by the server). If we do not handle this, the user will click on the Submit button and nothing will happen.

If success is true (#3), meaning the user was authenticated on the server, we can do something such as closing the Login window (#4) and then displaying the application (#5). As we do not have the Packt.view.MyViewport class created yet, we will comment it, so that later when we have the class implemented, we can come here and remove the comment. In this class (Packt.view.MyViewport) we will display the menu, header, footer, and the central panel of the application, which is where we are going to display the screens.

On the other hand, when success is false, meaning the username or password do not match with the information we have on the database, we will display an error message to the user with the message sent by the server (#6), in this case Incorrect user or password .

In case any other error happens on the server side, this error will be returned to Ext JS as we already described on line #2. To exemplify, let's say that we entered a wrong password for the database connection. If we try to login, we will get the following error:

This way we can handle all kinds of server responses, the ones we are waiting for and also any exception!

Mastering Ext JS Learn how to build powerful and professional applications by mastering the Ext JS framework with this book and ebook
Published: July 2013
eBook Price: $29.99
Book Price: $49.99
See more
Select your format and quantity:

Enhancing the Login screen

Our Login screen is done. However, there are some enhancements we can apply to it to make it even better and also offer a better experience to the user.

Following are the enhancements that we are going to apply in our Login screen:

  • Applying a loading mask while authenticating
  • Submitting the form when a user presses Enter
  • Displaying a Caps Lock warning message

Applying a loading mask on the form while authenticating

Sometimes, when the user clicks on the Submit button, there can be some delay while waiting for the server to send back the response. Some users will be patient, some will not be. The ones that are not very patient will click on the Submit button again; this means making another request to the server. We can avoid this behavior by applying a loading mask to the Login window while awaiting the response.

First, we need to add the following code right before the Ext.Ajax.request call:

Ext.get(login.getEl()).mask("Authenticating... Please wait...", 'loading');

This will apply the mask to the Login window.

Then, on the first line inside the success and failure functions, we need to add the following line of code:

Ext.get(login.getEl()).unmask();

This will remove the mask from the Login window.

If we try to execute the code, we will have the following output:

Notice that all the buttons are not reachable and the user cannot click on them again until the server sends back a response.

Form submit on Enter

For some forms, especially for the Login form, it is very natural for people to hit Enter when they are ready. This behavior is not automatic for Ext JS; therefore, we have to implement it.

The textfield component has an event to handle special keys such as Enter . This event is called specialkey and it is the one that we are going to listen to in our login controller. First, we need to add the event to the this.control declaration:

"login form textfield": { specialkey: this. onTextfieldSpecialKey }

The selector that we are going to use is login form textfield because this selector will retrieve all the text fields from the Login form, which are user and password. The user can hit Enter while typing at any text field of the form.

Next, we need to implement the onTextfieldSpecialKey method inside the controller as well:

onTextfieldSpecialKey: function(field, e, options) { if (e.getKey() == e.ENTER){ var submitBtn = field.up('form').down('button#submit'); submitBtn.fireEvent('click', submitBtn, e, options); } }

First, we are going to verify if the key pressed by the user is Enter . Then, we will get the reference for the Submit button: we need first to get the form reference, and then retrieve the Submit button that is below the form in the component's hierarchy. And at last, we will fire the click event of the Submit button manually. This way the onButtonClickSubmit method will be called automatically. As we also verify if the form is valid inside the onButtonClickSubmit method, we can be sure that the request will not be made even if the client validations fail.

The Caps Lock warning message

The last enhancement we will apply to the form is the Caps Lock message. Sometimes the Caps Lock key is active and when we input the password, we may input the correct password and yet have the system term it incorrect because it is case sensitive. And warning the user about this is a nice thing to do.

The following screenshot presents the final result of the Caps Lock warning implementation:

As you can see in the preceding screenshot, we will display the warning as a tooltip. So the first thing we need to do is go back to the app.js launch function and on the first line we need to add the following code:

Ext.tip.QuickTipManager.init();

Without the preceding line of code, the tooltips will not work.

Another option is to set enableQuickTips to true inside Ext.application (app.js).

The event that we are going to listen to is the keypress event, and we are only going to listen to this event fired by the password field. By default, the textfield components do not fire this event because it is a little bit heavy with regards to the performance. As we want to listen to this event, we need to add a configuration to the password field (inside Login.js file):

name: 'password', fieldLabel: "Password", enableKeyEvents: true, id: 'password'

We also need to add id to this field. And yes, as we discussed using id instead of itemId is bad, but in this case, there is nothing we can do about it. This is because when creating the tooltip, we need to set a target (in this case, the password field), and this target only accepts id of the component, and not itemId.

Before we add the code to the controller, we need to create the tooltip. We are going to create a new view called Packt.view.authentication.CapsLockTooltip. So we need to create a file named CapsLockTooltip.js under the app/view/authentication directory.

Ext.define('Packt.view.authentication.CapsLockTooltip', { extend: 'Ext.tip.QuickTip', alias: 'widget.capslocktooltip', target: 'password', anchor: 'top', anchorOffset: 60, width: 300, dismissDelay: 0, autoHide: false, title: '<div class="capslock">Caps Lock is On</div>', html:
'<div>Having Caps Lock on may cause you to enter your password</div>' + '<div>incorrectly.</div><br/>' +<>br '<div>You should press Caps Lock
to turn it off before entering</div>' + '<div>your password.</div>' });

In the Packt.view.authentication.CapsLockTooltip view we declared some configurations that are going to set the behavior of the tooltip. For example, we have the following:

  • target: This has the id value of the password fields.
  • anchor: This indicates that the tip should be anchored to a particular side of the target element (the password id field), with an arrow pointing back at the target.
  • achorOffset: This is a numeric value (in pixels) used to offset the default position of the anchor arrow. In this case, the arrow will be displayed 60 pixels after the starting point of the tooltip box.
  • width: This is the numeric value (in pixels) to represent the width of the tooltip box.
  • dismissDelay: This is the delay value (in milliseconds) before the tooltip automatically hides. As we do not want tooltip to be automatically hidden, we set the value to 0 (zero) to disable it.
  • autoHide: This is set to true to automatically hide the tooltip after the mouse exits the target element. If we do not want this, we set it to false.
  • title: This is the title text to be used as the title of the tooltip.
  • html: This is the HTML fragment that will be displayed in the tooltip body.

We also need to add the CSS code into app.css related to the capslock class:

.capslock{ background:url('../icons/bullet_error.png') no-repeat center left; padding:2px; padding-left:20px; font-weight:700; }

And at last, we need to make some changes in the login controller. First, in the views declaration, we will add the CapsLockTooltip class. As we created a subfolder inside the view folder, the controller will not understand if we simply add CapsLockTooltip as a view. So, we need to add authentication.CapsLockTooltip and the controller will understand that the class is inside the view/authentication folder:

views: [ 'Login', 'authentication.CapsLockTooltip' ],

We can have as many subfolders as we need under the app/model, store, view, and controller folders. This can help us to organize the code better, especially when we work with big applications. In the views, controllers, stores, and models declarations we also need to add the name of the subfolder as we did with authentication.CapsLockTooltip.

The next step is to listen to the keypress event inside the this.control declaration:

"login form textfield[name=password]": { keypress: this.onTextfieldKeyPress }

The selector that we are going to use is the login form textfield[name=password]. As we have two text fields on the form, we need a way of finding the password field; we can use the name attribute for this.

Then, we need to implement the onTextfieldKeyPress method inside the controller:

onTextfieldKeyPress: function(field, e, options) { var charCode = e.getCharCode(); // #1 if((e.shiftKey && charCode >= 97 && charCode <= 122) || // #2 (!e.shiftKey && charCode >= 65 && charCode <= 90)){ if(this.getCapslockTooltip() === undefined){ // #3 Ext.widget('capslocktooltip'); // #4 } this.getCapslockTooltip().show(); // #5 } else { if(this.getCapslockTooltip() !== undefined){ // #6 this.getCapslockTooltip().hide(); // #7 } } }

First, we need to get the code of the key that the user pressed (#1). Then, we need to verify if the Shift key is pressed and the user has pressed one of the lower alpha keys (a-z), or if the Shift key is not pressed and the user has pressed one of the capital alpha keys (A-Z) (#2). If the result of this verification is true, this means that the Caps Lock key is active. If you want to check the values of each key, you can go to http://www.asciitable.com/.

If Caps Lock is active, we will verify if there is a reference of the CapsLockTooltip class (#3). If there is not, we will create a reference using its xtype (#4).

If Caps Lock is not active, we need to verify if there is a reference of the CapsLockTooltip class (#6). If positive, we will hide the tooltip.

The last detail: we do not have the reference for this.getCapslockTooltip() inside the controller. That is why we need to create it as well:

refs: [ { ref: 'capslockTooltip', selector: 'capslocktooltip' } ]

ref (reference) is an alternative to locate a component. It also uses the ComponentQuery syntax. ref is very useful, especially if we need to get a reference of a component several times inside a controller. The controller will also generate a get method automatically for a ref. In this case, the controller generates the getCapslockTooltip method for us.

The Caps Lock warning code is now complete. We can save the project and test it.

Summary

In this article, we have covered the details of how to implement a login page step by step. We covered how to create the login view and controller and organized them according to the Ext JS MVC architecture. We applied client validations on the form to make sure that we are sending acceptable data to the server, and we also encrypted the password before sending it to the server. We covered how to do a basic login using PHP, and we covered important concepts of how to handle the data that the server is going to send back to Ext JS.

We learned about some enhancements that we can apply to the Login screen, such as submitting the form when the user hits Enter , and displaying a Caps Lock warning in the password field, and also learned how to apply a load mask on the form while it is sending data and waiting for information from the server.

Resources for Article :


Further resources on this subject:


About the Author :


Loiane Groner

Loiane Groner lives in São Paulo, Brazil and has over 8 years of software development experience. While at university, she demonstrated great interest in IT. She worked as a teaching assistant for 2 and a half years specialising in teaching algorithms, data structures, and computing theory. She represented her university at the ACM International Collegiate Programming Contest, Brazilian Finals (South America Regionals), and also worked as Student Delegate of SBC (Brazilian Computing Society) for 2 years. She won a merit award in her senior year for being one of the top three students with the best GPAs in the Computer Science department and also graduated with honors.

She has already worked at multinational companies, such as IBM. Her areas of expertise include Java SE, Java EE, and also Sencha technologies (Ext JS and Sencha Touch). Nowadays, she is working as Software Development Manager at a financial institution, where she manages overseas solutions. She also works as an independent Sencha consultant and coach.

She has also authored books such as ExtJS 4 First Look and Mastering JS for Packt Publishing.

She is passionate about Sencha and Java, and is the CampinasJUG (Campinas Java Users Group) leader and ESJUG (Espirito Santo Java Users Group) coordinator, both Brazilian JUGs.

She also contributes to the software development community through her blogs: http://loianegroner.com (English) and http://loiane.com (Portuguese-BR), where she writes about IT careers, Ext JS, Sencha Touch, Spring Framework, and general development notes, and also publishes screencasts.

Books From Packt


Ext.NET Web Application Development
Ext.NET Web Application Development

Learning Ext JS 3.2
Learning Ext JS 3.2

Oracle Application Express 4.0 with Ext JS
Oracle Application Express 4.0 with Ext JS

Ext JS 3.0 Cookbook
Ext JS 3.0 Cookbook

Learning Ext JS 4
Learning Ext JS 4

Learning Ext JS
Learning Ext JS

Ext JS 4 First Look
Ext JS 4 First Look

Ext JS 4 Web Application Development Cookbook
Ext JS 4 Web Application Development Cookbook


No votes yet

Post new comment

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.
v
X
H
6
v
V
Enter the code without spaces and pay attention to upper/lower case.
Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software