Over a million developers have joined DZone.

Packaging a jQuery Mobile Application with Cordova/Phonegap

· Mobile Zone

In this article we are going to learn the process of packaging a jQuery Mobile Application with Cordova / Phonegap. The application that we will use in the article is the Meeting Room Manager app that we’ve been building over the past months.

jqm-cordova-1

While we haven’t finished developing the Meeting Room Manager app (only the user registration features are completed at this point), I want to show you how to convert it from a mobile web app to a native hybrid app that you can deploy to a device.

In this article we will focus on deploying to an Android device. We will perform an iOS deployment in a follow-up article.

A Short Introduction to Cordova

Apache Cordova is an open-source mobile development framework where you use HTML, CSS and JavaScript to create mobile applications targeting multiple mobile operating systems.

The web applications that you build with Cordova run inside native applications called wrappers, that are targeted to each mobile platform. Cordova uses standard-compliant API bindings to expose different features of the mobile devices – for example, sensors, data and network status – to your application.

Packaging a jQuery Mobile Application with Cordova / Phonegap

The main benefit of using Cordova is that it allows you to use JavaScript, HTML and CSS to develop mobile applications that can run on multiple platforms, bypassing the respective programming languages of each platform.

However, you trade this ease of development for a degraded user experience. The reason is that you are ultimately running a web application on a mobile browser, and mobile browsers have performance and other limitations in their integration with the native features of the device.

Setting Up the Cordova Development Environment

I developed this particular example on a Windows PC. I used Cordova version 4.0.0. I performed the following steps to get ready to develop with Cordova,:

If you haven’t set up your Cordova development environment yet, you should follow the detailed installation instructions found in the Command-Line Interface section of the Cordova documentation.

Creating a Cordova Project

Let’s move on to the more interesting parts of the tutorial. We are going to get started by creating a Cordova project.

The project’s directory where we created our Meeting Room Manager jQuery Mobile web application currently looks like this:

directories-23

We are going to navigate to the project’s directory, open a command window and type the command that creates a Cordova project:

> cordova create cordova-project com.miamicoder.bookit BookIt

The command will create the cordova-project directory within the project’s directory:

directories-24

In the cordova-project directory we will find a boilerplate web-based application with a www subdirectory that contains the application’s home page, located in the index.html file.

The www subdirectory itself contains the css, js, img and other subdirectories that hold various project assets. These resources will be stored on the device’s local file system when the application is packaged and deployed.

directories-25

The config.xml file contains the project metadata that Cordova uses to generate and distribute the application. Let’s open the file and edit the description and author nodes:

<?xml version='1.0' encoding='utf-8'?>
<widget id="com.miamicoder.bookit" version="0.0.1" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
    <name>BookIt</name>
    <description>
        A Meeting Rooms Manager app.
    </description>
    <author email="miamicoder@gmail.com" href="http://miamicoder.com">
        Jorge Ramon - MiamiCoder.com
    </author>
    <content src="index.html" />
    <access origin="*" />
</widget>

Adding Target Platforms to a Cordova Project

We need to specify one or more target platforms to build a Cordova project. In this article we are targeting just the Android platform. To add it to the project, we need to open a command window in the cordova-project directory and type the following command:

c:\cordova-project>cordova platform add android

Be mindful that your computer must support and have installed the target platform’s SDK. We should not have a problem with this requirement because we installed the Android SDK earlier.

You can check the installed platform(s) by running the following command:

c:\cordova-project>cordova platforms ls

Running the Boilerplate Cordova Application

The following command builds the skeletal Cordova application that we just created:

A successfuld build will generate a message similar to the screenshot below:

cordova-project-6

We can run the boilerplate app on an emulator or a device. We will use the Genymotion Emulator in this tutorial prior to testing on an actual device.

I highly recommend Genymotion to speed up the testing phase of your applications. This emulator provides an experience that’s very close to a physical device. It’s also very fast to load, as opposed to the emulators that come with the Android SDK.

Installing and setting up Genymotion is a breeze. Assuming that you’ve already set up Genymotion, you can perform the following steps to test the skeletal Cordova application:

  1. Start one of the virtual devices that you defined in Genymotion.cordova-project-7
  2. Once the Genymotion virtual device is running, use the following Cordova command to view the application in the virtual device:
cordova run android
  1. The command will build the app and deploy it to the connected device, which in this case is Genymotion’s. On the device, the app should look like this:cordova-project-8

Migrating a jQuery Mobile App to a Cordova Project

At this point we have our starter Cordova project set up and we can focus on migrating our jQuery Mobile application into it. The migration will consist of these steps:

  1. Move the jQuery Mobile pages that today exist in their own separate html files over to the index.html file in the Cordova project.
  2. Migrate the JavaScript files located under the project’s js directory over to the cordova-project/www/js directory.
  3. Move the CSS files located the project’s CSS files from the CSS directory to the cordova-project/www/css directory.

Adding the jQuery Mobile References

We will start the jQuery Mobile pages migration by opening the cordova-project/www/index.html file and adding the references to the jQuery Mobile library’s CSS file, as well as our and our app’s CSS file, in the head section of the html page:

<head>
    <meta charset="utf-8" />
    <meta name="format-detection" content="telephone=no" />
    <meta name="msapplication-tap-highlight" content="no" />
    <!-- WARNING: for iOS 7, remove the width=device-width and height=device-height attributes. See https://issues.apache.org/jira/browse/CB-4323 -->
    <meta name="viewport" content="width=device-width, height=device-height, user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, target-densitydpi=device-dpi" />

    <link href="css/themes/2/bookit.min.css" rel="stylesheet" />
    <link href="css/themes/2/jquery.mobile.icons.min.css" rel="stylesheet" />
    <link href="lib/jqm/1.4.5/jquery.mobile.structure-1.4.5.min.css" rel="stylesheet" />
    <link rel="stylesheet" type="text/css" href="css/app.css" />
    <title>BookIt</title>
</head>

Note how the references are assuming that the library’s files are in the cordova-project/www/css and cordova-project/www/lib directories. We will add these files in a few minutes.

We also need to add the JavaScript references. Add the following entries between the reference to the cordova.js file and the tag, replacing the entry for the js/index.js file:

<script type="text/javascript" src="cordova.js"></script>
<script src="lib/fastclick/fastclick.min.js"></script>
<script src="lib/jquery/2.1.1/jquery-2.1.1.min.js"></script>
<script src="js/api-messages.js"></script>
<script src="js/settings.js"></script>
<script src="js/signup-controller.js"></script>
<script type="text/javascript" src="js/index.js"></script>
<script src="lib/jqm/1.4.5/jquery.mobile-1.4.5.min.js"></script>

FastClick is a library that eliminates the 300ms delay between a physical tap and the firing of a click event on mobile browsers. We will add its files to the Cordova project later in the tutorial.

The api-messages.js, settings.js, signup-controller.js and index.js contain our application’s JavaScript code. We will bring them in from the project’s js directory.

Adding the Home Page

Next, we are going to open the index.html file in the project’s root and copy the jQuery Mobile page’s html code, which is this:

<div data-role="page" id="page-index">
    <div data-role="header" data-theme="c" data-position="fixed">
        <h1>BookIt</h1>
    </div><!-- /header -->
    <div role="main" class="ui-content">
        <h2 class="mc-text-center">Welcome!</h2>
        <p class="mc-top-margin-1-5"><b>Existing Users</b></p>
        <a href="#page-signin" class="ui-btn ui-btn-b ui-corner-all">Sign In</a>
        <p class="mc-top-margin-1-5"><b>Don't have an account?</b></p>
        <a href="#page-signup" class="ui-btn ui-btn-b ui-corner-all">Sign Up</a>
        <p></p>
    </div><!-- /content -->
</div><!-- /page -->

Then, we need to open the cordova-project/www/index.html file and replace the div element in the document’s body with the html above. After you paste the code, modify the value of the page’s id attribute so it becomes page-index. Also, add the data-position and data-tap-toggle attributes to the page’s header as shown below:

<div data-role="page" id="page-index">
    <div data-role="header" data-theme="c" data-position="fixed">
        <h1>BookIt</h1>
    </div><!-- /header -->
    <div role="main" class="ui-content">
        <h2 class="mc-text-center">Welcome!</h2>
        <p class="mc-top-margin-1-5"><b>Existing Users</b></p>
        <a href="#page-signin" class="ui-btn ui-btn-b ui-corner-all">Sign In</a>
        <p class="mc-top-margin-1-5"><b>Don't have an account?</b></p>
        <a href="#page-signup" class="ui-btn ui-btn-b ui-corner-all">Sign Up</a>
        <p></p>
    </div><!-- /content -->
</div><!-- /page -->

Setting the data-position attribute to fixed makes the header float above the content.

Adding the Sign In Page

We are going to follow the same procedure with the Sign In page. Open the sign-in.html file in the project’s root directory and copy the html code for the Sign In page:

<div data-role="page" data-bookit-page="signIn">
    <div data-role="header" data-theme="c">
        <h1>Book It</h1>
    </div><!-- /header -->
    <div role="main" class="ui-content">
        <h3>Sign In</h3>
        <label for="txt-email">Email Address</label>
        <input type="text" name="txt-email" id="txt-email" value="">
        <label for="txt-password">Password</label>
        <input type="password" name="txt-password" id="txt-password" value="">
        <fieldset data-role="controlgroup">
            <input type="checkbox" name="chck-rememberme" id="chck-rememberme" >
            <label for="chck-rememberme">Remember me</label>
        </fieldset>
        <a href="#dlg-invalid-credentials" data-rel="popup" data-transition="pop" data-position-to="window" id="btn-submit" class="ui-btn ui-btn-b ui-corner-all mc-top-margin-1-5">Submit</a>
        <p class="mc-top-margin-1-5"><a href="begin-password-reset.html">Can't access your account?</a></p>
        <div data-role="popup" id="dlg-invalid-credentials" data-dismissible="false" style="max-width:400px;">
            <div role="main" class="ui-content">
                <h3 class="mc-text-danger">Login Failed</h3>
                <p>Did you enter the right credentials?</p>
                <div class="mc-text-center"><a href="#" data-rel="back" class="ui-btn ui-corner-all ui-shadow ui-btn-b mc-top-margin-1-5">OK</a></div>
            </div>
        </div>
    </div><!-- /content -->
</div><!-- /page -->

Then, go to the cordova-project/www/index.html file and paste the code immediately below the index page. Make sure to modify the page’s id attribute and the header div as shown below:

<div data-role="page" id="page-signin">
    <div data-role="header" data-theme="c" data-position="fixed">
        <a href="#page-index" data-icon="home" data-iconpos="notext" data-transition="slide" data-direction="reverse"></a>
        <h1>BookIt</h1>
    </div><!-- /header -->
    <div role="main" class="ui-content">
        <h3>Sign In</h3>
        <label for="txt-email">Email Address</label>
        <input type="text" name="txt-email" id="txt-email" value="">
        <label for="txt-password">Password</label>
        <input type="password" name="txt-password" id="txt-password" value="">
        <fieldset data-role="controlgroup">
            <input type="checkbox" name="chck-rememberme" id="chck-rememberme">
            <label for="chck-rememberme">Remember me</label>
        </fieldset>
        <a href="#dlg-invalid-credentials" data-rel="popup" data-transition="pop" data-position-to="window" id="btn-submit" class="ui-btn ui-btn-b ui-corner-all mc-top-margin-1-5">Submit</a>
        <p class="mc-top-margin-1-5"><a href="begin-password-reset.html">Can't access your account?</a></p>
        <div data-role="popup" id="dlg-invalid-credentials" data-dismissible="false" style="max-width:400px;">
            <div role="main" class="ui-content">
                <h3 class="mc-text-danger">Login Failed</h3>
                <p>Did you enter the right credentials?</p>
                <div class="mc-text-center"><a href="#" data-rel="back" class="ui-btn ui-corner-all ui-shadow ui-btn-b mc-top-margin-1-5">OK</a></div>
            </div>
        </div>
    </div><!-- /content -->
</div><!-- /page -->

Notice how we added a back button to the header:

<div data-role="header" data-theme="c" data-position="fixed" data-tap-toggle="false">
    <a href="#page-index" data-icon="home" data-iconpos="notext" data-transition="slide" data-direction="reverse"></a>
    <h1>BookIt</h1>
</div><!-- /header -->

The button will allow users to navigate back to the index page if they choose to. We used the data-iconpos attribute to define that we want an icon-only button:

cordova-project-10

The data-transition and data-direction attributes allow us to define a reverse slide transition for the navigation back to the index page.

Adding the Sign Up Page

Now, on to the Sign Up page. Open the sign-up.html file in the project’s root directory and copy the page’s html code:

<div data-role="page" id="page-signup">
    <div data-role="header" data-theme="c">
        <h1>BookIt</h1>
    </div><!-- /header -->
    <div role="main" class="ui-content">
        <h3>Sign Up</h3>
        <div id="ctn-err" class="bi-invisible"></div>
        <label for="txt-first-name">First Name</label>
        <input type="text" name="txt-first-name" id="txt-first-name" value="">
        <label for="txt-last-name">Last Name</label>
        <input type="text" name="txt-last-name" id="txt-last-name" value="">
        <label for="txt-email-address">Email Address</label>
        <input type="text" name="txt-email-address" id="txt-email-address" value="">
        <label for="txt-password">Password</label>
        <input type="password" name="txt-password" id="txt-password" value="">
        <label for="txt-password-confirm">Confirm Password</label>
        <input type="password" name="txt-password-confirm" id="txt-password-confirm" value="">
        <!--<a href="#dlg-sign-up-sent" data-rel="popup" data-transition="pop" data-position-to="window" id="btn-submit" class="ui-btn ui-btn-b ui-corner-all mc-top-margin-1-5">Submit</a>-->
        <button id="btn-submit" class="ui-btn ui-btn-b ui-corner-all mc-top-margin-1-5">Submit</button>
        <div data-role="popup" id="dlg-sign-up-sent" data-dismissible="false" style="max-width:400px;">
            <div data-role="header">
                <h1>Almost done...</h1>
            </div>
            <div role="main" class="ui-content">
                <h3>Confirm Your Email Address</h3>
                <p>We sent you an email with instructions on how to confirm your email address. Please check your inbox and follow the instructions in the email.</p>
                <div class="mc-text-center"><a href="sign-in.html" class="ui-btn ui-corner-all ui-shadow ui-btn-b mc-top-margin-1-5">OK</a></div>

            </div>
        </div>
    </div><!-- /content -->
</div><!-- /page -->

Then, go to the cordova-project/www/index.html file and paste the code immediately below the page-signin page. Make sure to modify the page’s id attribute and the header div as shown below:

<div data-role="page" id="page-signup">
    <div class="bi-header" data-role="header" data-theme="c" data-position="fixed" data-tap-toggle="false">
        <a href="#page-index" data-icon="home" data-iconpos="notext" data-transition="slide" data-direction="reverse"></a>
        <h1>BookIt</h1>
    </div><!-- /header -->
    <div role="main" class="ui-content">
        <h3>Sign Up</h3>
        <div id="ctn-err" class="bi-invisible"></div>
        <label for="txt-first-name">First Name</label>
        <input type="text" name="txt-first-name" id="txt-first-name" value="">
        <label for="txt-last-name">Last Name</label>
        <input type="text" name="txt-last-name" id="txt-last-name" value="">
        <label for="txt-email-address">Email Address</label>
        <input type="text" name="txt-email-address" id="txt-email-address" value="">
        <label for="txt-password">Password</label>
        <input type="password" name="txt-password" id="txt-password" value="">
        <label for="txt-password-confirm">Confirm Password</label>
        <input type="password" name="txt-password-confirm" id="txt-password-confirm" value="">
        <!--<a href="#dlg-sign-up-sent" data-rel="popup" data-transition="pop" data-position-to="window" id="btn-submit" class="ui-btn ui-btn-b ui-corner-all mc-top-margin-1-5">Submit</a>-->
        <button id="btn-submit" class="ui-btn ui-btn-b ui-corner-all mc-top-margin-1-5">Submit</button>
        <div data-role="popup" id="dlg-sign-up-sent" data-dismissible="false" style="max-width:400px;">
            <div data-role="header">
                <h1>Almost done...</h1>
            </div>
            <div role="main" class="ui-content">
                <h3>Confirm Your Email Address</h3>
                <p>We sent you an email with instructions on how to confirm your email address. Please check your inbox and follow the instructions in the email.</p>
                <div class="mc-text-center"><a href="sign-in.html" class="ui-btn ui-corner-all ui-shadow ui-btn-b mc-top-margin-1-5">OK</a></div>

            </div>
        </div>
    </div><!-- /content -->
</div><!-- /page -->

We also added a back button to the header section of this page to allow users to navigate back to the index page.

Adding the Registration Succeeded Page

The last page that we will migrate is the Registration Succeeded page. Open the registration-succeeded.html file in the project’s root directory and copy the jQuery Mobile page:

<div data-role="page" data-bookit-page="signup-succeeded">
    <div data-role="header" data-theme="c">
        <h1>Book It</h1>
    </div><!-- /header -->
    <div role="main" class="ui-content">
        <h2 class="mc-text-center">Registration Succeeded</h2>
        <p class="mc-top-margin-1-5">Congratulations!  You are now registered with BookIt.</p>
        <a href="sign-in.html" class="ui-btn ui-btn-b ui-corner-all">Sign In</a>
        <p></p>
    </div><!-- /content -->
</div><!-- /page -->

Place the code in the cordova-project/www/index.html file, immediately below the page-signup page. Then update the page’s id to page-signup-succeeded as shown below:

<div data-role="page" id="page-signup-succeeded">
    <div data-role="header" data-theme="c">
        <h1>Book It</h1>
    </div><!-- /header -->
    <div role="main" class="ui-content">
        <h2 class="mc-text-center">Registration Succeeded</h2>
        <p class="mc-top-margin-1-5">Congratulations!  You are now registered with BookIt.</p>
        <a href="#page-signin" class="ui-btn ui-btn-b ui-corner-all">Sign In</a>
        <p></p>
    </div><!-- /content -->
</div><!-- /page -->

This concludes the migration of the application’s jQuery Mobile pages into the Cordova project. Next, we will migrate the JavaScript files.

Migrating the JavaScript Files Into the Cordova Project

We are going to start the JavaScript files migration by creating the directories for the libraries that we will use in the Cordova project.

Create the lib directory in the cordova-project/www directory. Then, create the lib/fastclick, lib/jqm and lib/jquery directories:

directories-26

Adding the Library Files

Now, download the FastClick library and place it in its directory. Do the same with jQuery Mobile and jQuery.

directories-27

Adding the Applications File

The second step consists of adding the application files. Copy the api-messages.js and settings.js files from the project’s root folder and paste them into the cordova-project/www/js directory:

directories-28

Be mindful of the value that you assign to the signUpUrl variable in the setttings.js file. When you are testing the app, this URL should point to your Express Server instance (We created the server in the Using MongoDB and Mongoose for User Registration, Login and Logout in a Mobile Application chapter of this series). When we move the app to production, this URL should point to our public Express Server endpoint.

Creating the Controller Module

As a third step, we are going to refactor the code in the index.js file. What we want is to create JavaScript Classes that will implement different features of the app, starting with the user signup feature.

The features’ core logic will reside in these Classes, separate from each other and from the jQuery Mobile page lifecycle events.

This change will make the application more maintainable. It will be easier for multiple developers to work on different areas of the app, and it will be easier to create unit tests for the different features.

We will start the refactoring of the signup code by creating the the signup-controller.js file in the cordova-project/www/js directory:

directories-29

We will define a SignUpController Class in the file:

var BookIt = BookIt || {};

BookIt.SignUpController = function () {

    this.$signUpPage = null;
    this.$btnSubmit = null;
    this.$ctnErr = null;
    this.$txtFirstName = null;
    this.$txtLastName = null;
    this.$txtEmailAddress = null;
    this.$txtPassword = null;
    this.$txtPasswordConfirm = null;
};

The $signUpPage, $btnSubmit, $ctnErr, $txtFirstName, $txtLastName, $txtEmailAddress, $txtPassword and $txtPasswordConfirm variables will hold references to the html elements in the jQuery Mobile page that contains the signup form.

Moving on to the implementation of the Class, we first are going to add the init method:

BookIt.SignUpController.prototype.init = function () {
    this.$signUpPage = $("#page-signup");
    this.$btnSubmit = $("#btn-submit", this.$signUpPage);
    this.$ctnErr = $("#ctn-err", this.$signUpPage);
    this.$txtFirstName = $("#txt-first-name", this.$signUpPage);
    this.$txtLastName = $("#txt-last-name", this.$signUpPage);
    this.$txtEmailAddress = $("#txt-email-address", this.$signUpPage);
    this.$txtPassword = $("#txt-password", this.$signUpPage);
    this.$txtPasswordConfirm = $("#txt-password-confirm", this.$signUpPage);
};

This method is where we obtain the references to the html elements in the jQuery Mobile page that contains the signup form.

Next, the method that verifies that the values entered in the password and confirm password input elements match:

BookIt.SignUpController.prototype.passwordsMatch = function (password, passwordConfirm) {
    return password === passwordConfirm;
};

Then, the method where we check that the password is complex:

BookIt.SignUpController.prototype.passwordIsComplex = function (password) {
    // TODO: implement complex password rules here.  There should be similar rule on the server side.
    return true;
};

Remember that in the previous chapter of this tutorial I left the implementation of the password complexity rules as homework for you.

We also need to add a method that checks that the value entered in the Email Address text field is formatted as an email address:

BookIt.SignUpController.prototype.emailAddressIsValid = function (email) {
    var re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
    return re.test(email);
};

And a method to reset the signup form:

BookIt.SignUpController.prototype.resetSignUpForm = function () {

    var invisibleStyle = "bi-invisible",
        invalidInputStyle = "bi-invalid-input";

    this.$ctnErr.html("");
    this.$ctnErr.removeClass().addClass(invisibleStyle);
    this.$txtFirstName.removeClass(invalidInputStyle);
    this.$txtLastName.removeClass(invalidInputStyle);
    this.$txtEmailAddress.removeClass(invalidInputStyle);
    this.$txtPassword.removeClass(invalidInputStyle);
    this.$txtPasswordConfirm.removeClass(invalidInputStyle);

    this.$txtFirstName.val("");
    this.$txtLastName.val("");
    this.$txtEmailAddress.val("");
    this.$txtPassword.val("");
    this.$txtPasswordConfirm.val("");

};

We will invoke this method every time the user opens the SignUp page, making sure that any prior values entered in the form are cleared.

Finally, the template method that will capture the registration data, send it to the server and process the server’s response:

BookIt.SignUpController.prototype.onSignupCommand= function () {

    var firstName = this.$txtFirstName.val().trim(),
        lastName = this.$txtLastName.val().trim(),
        emailAddress = this.$txtEmailAddress.val().trim(),
        password = this.$txtPassword.val().trim(),
        passwordConfirm = this.$txtPasswordConfirm.val().trim(),
        invalidInput = false,
        invisibleStyle = "bi-invisible",
        invalidInputStyle = "bi-invalid-input";

    // Reset styles.
    this.$ctnErr.removeClass().addClass(invisibleStyle);
    this.$txtFirstName.removeClass(invalidInputStyle);
    this.$txtLastName.removeClass(invalidInputStyle);
    this.$txtEmailAddress.removeClass(invalidInputStyle);
    this.$txtPassword.removeClass(invalidInputStyle);
    this.$txtPasswordConfirm.removeClass(invalidInputStyle);

    // Flag each invalid field.
    if (firstName.length === 0) {
        this.$txtFirstName.addClass(invalidInputStyle);
        invalidInput = true;
    }
    if (lastName.length === 0) {
        this.$txtLastName.addClass(invalidInputStyle);
        invalidInput = true;
    }
    if (emailAddress.length === 0) {
        this.$txtEmailAddress.addClass(invalidInputStyle);
        invalidInput = true;
    }
    if (password.length === 0) {
        this.$txtPassword.addClass(invalidInputStyle);
        invalidInput = true;
    }
    if (passwordConfirm.length === 0) {
        this.$txtPasswordConfirm.addClass(invalidInputStyle);
        invalidInput = true;
    }

    // Make sure that all the required fields have values.
    if (invalidInput) {
        this.$ctnErr.html("<p>Please enter all the required fields.</p>");
        this.$ctnErr.addClass("bi-ctn-err").slideDown();
        return;
    }

    if (!this.emailAddressIsValid(emailAddress)) {
        this.$ctnErr.html("<p>Please enter a valid email address.</p>");
        this.$ctnErr.addClass("bi-ctn-err").slideDown();
        this.$txtEmailAddress.addClass(invalidInputStyle);
        return;
    }

    if (!this.passwordsMatch(password, passwordConfirm)) {
        this.$ctnErr.html("<p>Your passwords don't match.</p>");
        this.$ctnErr.addClass("bi-ctn-err").slideDown();
        this.$txtPassword.addClass(invalidInputStyle);
        this.$txtPasswordConfirm.addClass(invalidInputStyle);
        return;
    }

    if (!this.passwordIsComplex(password)) {
        // TODO: Use error message to explain password rules.
        this.$ctnErr.html("<p>Your password is very easy to guess.  Please try a more complex password.</p>");
        this.$ctnErr.addClass("bi-ctn-err").slideDown();
        this.$txtPassword.addClass(invalidInputStyle);
        this.$txtPasswordConfirm.addClass(invalidInputStyle);
        return;
    }

    $.ajax({
        type: 'POST',
        url: BookIt.Settings.signUpUrl,
        data: "email=" + emailAddress + "&firstName=" + firstName + "&lastName=" + lastName + "&password=" + password + "&passwordConfirm=" + passwordConfirm,
        success: function (resp) {

            if (resp.success === true) {
                $.mobile.navigate("#page-signup-succeeded");
                return;
            } else {
                if (resp.extras.msg) {
                    switch (resp.extras.msg) {
                        case BookIt.ApiMessages.DB_ERROR:
                        case BookIt.ApiMessages.COULD_NOT_CREATE_USER:
                            // TODO: Use a friendlier error message below.
                            this.$ctnErr.html("<p>Oops! BookIt had a problem and could not register you.  Please try again in a few minutes.</p>");
                            this.$ctnErr.addClass("bi-ctn-err").slideDown();
                            break;
                        case BookIt.ApiMessages.EMAIL_ALREADY_EXISTS:
                            this.$ctnErr.html("<p>The email address that you provided is already registered.</p>");
                            this.$ctnErr.addClass("bi-ctn-err").slideDown();
                            this.$txtEmailAddress
.addClass(invalidInputStyle);
                            break;
                    }
                }
            }
        },
        error: function (e) {
            console.log(e.message);
            // TODO: Use a friendlier error message below.
            this.$ctnErr.html("<p>Oops! BookIt had a problem and could not register you.  Please try again in a few minutes.</p>");
            this.$ctnErr.addClass("bi-ctn-err").slideDown();
        }
    });
};

Note how we implemented this Class so it doesn’t have any direct ties to the jQuery Mobile page lifecycle events, or to the jQuery Mobile page that contains the Sign Up form. Its only connection with the page is indirect, through the variables that hold references to the html elements of the form.

Modifying the Index.js File

Now we are going to turn our attention to the cordoba-project/www/js/index.js file, where we need to invoke the SignUpController Class that we just created. At this point, this file only contains the starter code generated when we created the Cordova project.

Let’s open the file and replace its code with the code below.

var BookIt = BookIt || {};

// Begin boilerplate code generated with Cordova project.

var app = {
    // Application Constructor
    initialize: function () {
        this.bindEvents();
    },
    // Bind Event Listeners
    //
    // Bind any events that are required on startup. Common events are:
    // 'load', 'deviceready', 'offline', and 'online'.
    bindEvents: function () {
        document.addEventListener('deviceready', this.onDeviceReady, false);
    },
    // deviceready Event Handler
    //
    // The scope of 'this' is the event. In order to call the 'receivedEvent'
    // function, we must explicitly call 'app.receivedEvent(...);'
    onDeviceReady: function () {
        app.receivedEvent('deviceready');
    },
    // Update DOM on a Received Event
    receivedEvent: function (id) {

    }
};

app.initialize();

// End boilerplate code.

$(document).on("mobileinit", function (event, ui) {
    $.mobile.defaultPageTransition = "slide";
});

app.signupController = new BookIt.SignUpController();

$(document).delegate("#page-signup", "pagebeforeshow", function () {
    // Reset the signup form.
    app.signupController.resetSignUpForm();
});

$(document).delegate("#page-signup", "pagebeforecreate", function () {

    app.signupController.init();

    app.signupController.$btnSubmit.off("tap").on("tap", function () {
        app.signupController.onSignupCommand();
    });

});

The first block of code in the file is part of the boilerplate code that was already there, generated when we created the Cordova project. We will leave it in place for now.

After the boilerplate code, we use the mobileinit handler to set the jQuery Mobile application’s default page transition to slide. I think that the slide transition gives the navigation a more native feel:

$(document).on("mobileinit", function (event, ui) {
    $.mobile.defaultPageTransition = "slide";
});

Next, we create the signupController property in the app object, and assign it an instance of the SignUpController Class:

app.signupController = new BookIt.SignUpController(); 

At this point we are ready to create the handlers for the jQuery Mobile page lifecycle events that we are interested in, the first of which is pagecontainerbeforeshow:

$(document).on("pagecontainerbeforeshow", function (event, ui) {
    if (typeof ui.toPage == "object") {
        switch (ui.toPage.attr("id")) {
            case "page-signup":
                // Reset the signup form.
                app.signupController.resetSignUpForm();
                break;
        }
    }
});

We use the pagecontainerbeforeshow event to reset the fields of the signup form. It’s possible for a user to visit this page multiple times. For security reasons, we want to clear any prior entries before we show the form to the user.

The second page event that we want to intercept is the beforecreate event:

$(document).delegate("#page-signup", "pagebeforecreate", function () {

    app.signupController.init();

    app.signupController.$btnSubmit.off("tap").on("tap", function () {
        app.signupController.onSignupCommand();
    });

});

We use the pagebeforecreate event to hook our tap handler for the Submit button. The handler invokes the SignUpController’s onSignupCommand method, which validates the user’s input, sends the registration request to the server and processes the server’s response.

This concludes the migration of the application’s core logic over to the Cordova project. We also moved the input validation and user registration logic into the SingUpController Class, in the signup-controller.js file. We left the logic that hooks the jQuery Mobile page events in the index.js file. This logic invokes the SignUpController’s methods as needed.

Let’s test the app to make sure the user signup process works.

Choices for Testing a Cordova App

I prefer to test my Android apps using either Ripple (double-check the link, I noticed that it changes over time), GenyMotion, or my own development devices. In this article I will show you how to test with GenyMotion and your own device.

Testing a Cordova Application with GenyMotion

To test your application with GenyMotion, follow these steps:

  1. Install and configure GenyMotion. Then, add the virtual devices that you want to test the app on. The instructions on how to do this are available in GenyMotion’s Dev Zone.
  2. Start the GenyMotion virtual device that you want to test on.
  3. Start your MongoDB instance if it isn’t already running.
  4. Open a command window in the [project root]/server directory and run the following command to start your Express Server:
    node index.js
  5. Open acommand window and run the following Cordova command to build and deploy the application to the virtual device:
    cordova run android --device

When the last step finishes, the application will execute on the virtual device.

cordova-project-11

Testing a Cordova App on a Physical Device

To test your application on a device, follow these steps:

  1. Set up your device for development. The instructions on how to do this are available in the Android’s developer website.
  2. Connect your device to your development computer with a USB cable.
  3. Open a terminal/command window in the [project root]/server directory and run the following command to start your Express Server:
    node index.js
  4. Open a terminal/command window and run the following Cordova command to build and deploy the application to the device:
    cordova run android --device

Summary and Next Steps

In this article we transformed a jQuery Mobile web application into a jQueryMobile + Cordova native application that can be deployed to mobile devices. We went through the process of creating a Cordova project, migrating a jQuery Mobile application into it, and deploying the mobile application to a device.

In the next article of this series of mobile development tutorials, we will continue building the features of the Meeting Room Manager app that is the subject of this series. Next up is the user login feature. Make sure to sign up for my newsletter so you can be among the first to know when the next article is online.

Download the Source Code

Download the source code for this tutorial here: jQuery Mobile and Cordova tutorial on GitHub

All the Chapters of this Series

You can find all the published parts of this series here: The Meeting Room Booking App Tutorial.

Topics:
java ,mobile ,frameworks ,jquery ,phonegap ,jquery mobile ,cordova

Published at DZone with permission of Jorge Ramon, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.
Subscribe

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}