Create Your First Mobile App with PhoneGap Build – Using the Storage API

by Brian Rinaldi on April 22, 2013

The modern web is always changing, and this article is more than two years old.

By Brian Rinaldi

This is part 6 in an ongoing series. You can find part 1 herepart 2 herepart 3 herepart 4 here and part 5 here. The files for the sample app are available here.

Even though our app is now looking and feeling more like an app, the truth is that everything up to this point could have been (and arguably should have been) accomplished via a web page. We’re not using any device features or doing anything that couldn’t be accomplished within the device browser. In this step, we will begin to access some device features via the PhoneGap API for storage. Using this API, we’ll add a local database that will store records of our favorite GitHub repositories.

Interestingly enough, the local database feature also works perfectly fine in your desktop browser, so any code from this step can be tested in your browser. The PhoneGap API, in this case, simply overcomes browser support across various devices as on some device browsers, this feature is not supported.

The first thing we will do is add some JavaScript code to create our local database on jQuery Mobile’s “pageinit” event. Since creating the database is asynchronous, we’ll need four methods, the first opens the connection to the database which then calls another method that submits the SQL for our database transaction. The third method simply does a console log of any error should one occur (obviously, a real application should handle errors more gracefully). Similarly the fourth method is just a success handler that will be called if the transaction succeeds and currently only logs a message of “success.” You will need to add this code to index.js with “pageinit” event binding below overwriting the existing method.

var db;

$('#reposHome').bind('pageinit', function(event) {
    loadRepos();
    db = window.openDatabase("repodb","0.1","GitHub Repo Db", 1000);
    db.transaction(createDb, txError, txSuccess);
});

function createDb(tx) {
    tx.executeSql("DROP TABLE IF EXISTS repos");
    tx.executeSql("CREATE TABLE repos(user,name)");
}

function txError(error) {
    console.log(error);
    console.log("Database error: " + error);
}

function txSuccess() {
    console.log("Success");
}

In order to allow the user to save a project as a favorite, we will add a button in repo-detail.html to the page header (that is the div with a data-role of “header”). You may notice that we are using one of the data properties on the button to set the icon to be a star (suitable for saving a favorite) and to align the button on the right-hand side of the header.

<button id="saveBtn" data-icon="star" class="ui-btn-right">Save as Favorite</button>

Currently, this button does nothing as we have not added any code to respond to the user touching or clicking the button. First, let’s add some JavaScript methods to insert data into the database when called. We’ll perform another database transaction similar to the previous transaction where we created the database. Add the following code to index.js.

function saveFave() {
    db = window.openDatabase("repodb","0.1","GitHub Repo Db", 1000);
    db.transaction(saveFaveDb, txError, txSuccessFave);
}

function saveFaveDb(tx) {
    var owner = getUrlVars().owner;
    var name = getUrlVars().name;

    tx.executeSql("INSERT INTO repos(user,name) VALUES (?, ?)",[owner,name]);
}

function txSuccessFave() {
    console.log("Save success");

    disableSaveButton();
}

Now when the reposDetail page is shown (i.e. the “pageshow” event), we need to add a click event handler to the button so that it responds to the user. Add the following line within our existing “pageshow” handler for the reposDetail page.

$("#saveBtn").bind("click", saveFave);

In the txSuccessFave() method earlier, you may have noticed we called a nonexistent method called disableSaveButton(). Let’s add this function which will change the text of the button to “saved” after the user saves an item, as well as change the color of the button and make it inactive for click events. (If you are wondering why this code seems slightly complicated, see the discussion on jQuery Mobile Generated HTML and Styles below).

function disableSaveButton() {
    // change the button text and style
    var ctx = $("#saveBtn").closest(".ui-btn");
    $('span.ui-btn-text',ctx).text("Saved").closest(".ui-btn-inner").addClass("ui-btn-up-b");

    $("#saveBtn").unbind("click", saveFave);
}

At this point, the favorites will save properly, but when you visit a favorite the button still reads “Save Favorite” rather than “Saved.” In order to correct this, we need to check if we have already saved this project when the page loads. In order to do that, let’s add a line to call to a new method in our “pageshow” event handler for reposDetail.

checkFave();

This method and its associated callbacks will query the database to see if an item with the same username and repository name has already been saved in our local database.

function checkFave() {
    db.transaction(checkFaveDb, txError);
}

function checkFaveDb(tx) {
    var owner = getUrlVars().owner;
    var name = getUrlVars().name;

    tx.executeSql("SELECT * FROM repos WHERE user = ? AND name = ?",[owner,name],txSuccessCheckFave);
}

function txSuccessCheckFave(tx,results) {
    console.log("Read success");
    console.log(results);

    if (results.rows.length)
         disableSaveButton();
}

This last method calls the disableSaveButton() method we created earlier if there are any results returned (i.e. if the item has already been saved).

See Also:  Getting started with Redux using the Mullet Stack

Since the local database functionality works within your browser, you can feel free to test this in the browser first to see if everything works. Once you’ve confirmed that everything function, zip up your project and upload it to PhoneGap Build. When hydration is complete, open the app on your device and it should update. You can see a screenshot of our updated project details page with a saved favorite running in the browser below.

step4

In future updates we should not recreate the database every time the app is loaded, as this overwrites any pre-existing favorites.  Also, we might consider consolidating and/or improving our database transaction callback handlers.

jQuery Mobile Generated HTML and Styles

You may have noticed that the jQuery code we used to change our button text to read “Saved” seemed overly complex. Rather than just simply selecting the button by it’s ID, we are using methods like closest() to traverse the DOM and locate our button text (as an aside, there are instructions on how to do this available here). The reason this happens is because jQuery Mobile automatically adds a bunch of generated HTML and CSS styles to your HTML to make it look and feel like a mobile app.

As mentioned earlier, this can make jQuery Mobile very easy to use but can make some seemingly simple tasks such as this more complicated than you might anticipate. You will frequently need to reference the generated HTML via your browser’s developer tools or the PhoneGap debugging tools discussed earlier in order to determine the actual jQuery selectors necessary to achieve the effect you desire.

See Also:  Why you should limit JavaScript — and how to do it

PhoneGap API Explorer

PhoneGap comes with a long list of supported API’s of which I only cover a couple here. The PhoneGap documentation is really good at showing how to use the API’s, however if you want to actually see how they work on your device, I highly recommend using an app created by Adobe evangelist Christophe Coenraets called the PhoneGap API Explorer.

This app not only shows you how to use PhoneGap’s API’s but allows you to enter parameters and run them on your device to see the result. This app is available for free on the iOS App Store and on Google Play. It is also posted as an open-source project on GitHub.

pgapi_sql

In part 7 we’ll add another page for viewing favorites and integrate PhoneGap’s API for notifications. View Part 7 here.