Requirements

Prerequisite knowledge
Required products
 
Sample files
 
User level
Beginning
 
In my previous article, I discussed how to integrate Parse.com into your PhoneGap application. Parse.com handles data storage for you. This gives you the ability to store data for your mobile applications in the cloud. Parse has quite a few features and my article focused primarily on simple storage techniques, but I encourage you to check out their site for more examples.
In that article, we walked through the set up of Parse as well as the creation of a simple application that stored simple "Note” objects. We didn't actually make use of any PhoneGap features. That's perfectly OK, but doesn't truly create a memorable experience for the user. One of the things that makes PhoneGap truly epic is the combination of other services, like Parse, with the device features that PhoneGap gives you access to. In this article, I'll be improving on the previous sample application by adding offline/online support, geolocation, and child browser features.
 
 
Cell reception is your achilles heel
As powerful as our little mobile devices are, they have one weakness. No, it isn't red kryptonite. It's signal strength. I live in a mid-sized city and still encounter areas where my phone mysteriously stops loading data. It is frustrating, but what makes situations like this even more frustrating are apps that don't correctly handle being offline. Certainly some apps cannot really do much if they cannot get online. Being able to recognize your network connection and handle changes is the mark of a superior application. Let's begin by doing some basic modifications to our application to handle both an initial connection check as well as a change in connection.
 

function onDeviceReady(e) {
$(document).on("online", onlineFunc);
$(document).on("offline", offlineFunc);
if(!(navigator.connection.type === Connection.UNKNOWN &&
navigator.connection.type === Connection.NONE)) {
$(document).trigger("online");
} else {
$(document).trigger("offline");
}
}

The code snippet above is a portion of the onDeviceReady event handler. If you have not used PhoneGap before, this may be new to you. Basically, any code that makes use of device features needs to wait for a "device-ready” event to be fired by the underlying PhoneGap framework. In the prior sample application, we relied on the jQuery document ready event. In this example, we will replace that JavaScript code, in main.js, with the code above.
Begin by adding two event handlers to the document object: one for online and one for offline. As you can probably guess, these handlers respond to a change to your connection status. First, we need to check the current status. 
First, we need to check the current status. PhoneGap's Connection object provides a simple way to check device connectivity. The value of navigator.connection.type is one of Connection.UNKNOWN, Connection.ETHERNET, Connection.WIFI, Connection.CELL_2G, Connection.CELL_3G, Connection.CELL_4G, or Connection.NONE. With these values, you see if the user has any connectivity as well as the the relative strength of the connection. This is useful for deciding if it makes sense to download large media files for example. What is a good idea on a 4G connection is not necessarily on a 2G connection. For our needs, we simply care if there is no connection at all. We also took the pessimistic view that if we cannot determine the connection at all (Connection.UNKNOWN) then we assume the device is offline. Now let's take a look at those handlers.
 

var parse_init = false;
var online;
function offlineFunc(e) {
console.log('offline');
online=false;
navigator.notification.alert("You are offline. Posting notes have been disabled.", null, "Offline!");
addNoteButton.attr("disabled","disabled");
if(geoWatch) navigator.geolocation.clearWatch(geoWatch);
}
function onlineFunc(e) {
console.log('online');
online=true;
if(!parse_init) {
initParse();
getNotes();
}
geoWatch = navigator.geolocation.watchPosition(onGeo, onGeoError, { timeout: 30000 });
addNoteButton.removeAttr("disabled");
}

We have a few things going on here in both handlers, so let's take it step by step. The offline handler first sets a global variable (online) to false. This will be used elsewhere. (Yes, we could re-use the Connection check we had earlier, but this seemed like a simpler solution.) Next, we notify the user that, due to the device being offline, functionality has been disabled. PhoneGap has a Notification API that is perfect for this. (As an added bonus, you could use the same API to vibrate the phone to ensure the user really gets the message.) After the notification we then disable the button used to add notes. And finally, we clear out any geolocation activities the device may be doing. (You'll see where that starts up in a second.)
Now turn your attention to the online handler. As before, we update a global variable reflecting our connection status. The next block handles Parse initialization. As this can only be run once, it is checks the value of parse_init to see if it has already been set. Since we mentioned earlier that geolocation would be part of our updates, the watchPosition call here asks the device to report the user's current position and reports on changes. Finally, we remove the disabled attribute (if it is there) from the button used to add notes.
The initParse() function simply contains the Parse setup logic that was detailed in the previous article:
 

function initParse() {

    Parse.initialize(PARSE_APP, PARSE_JS);

    NoteObject = Parse.Object.extend("NoteObject");

    parse_init=true;

}

 
Adding Geolocation to the Mix
You've seen already where geolocation support was added - both to the online and offline handlers, but let's look at how this data is actually used in the application. The online handler made use of the "watch” feature in Geolocation. This will use the device's GPS to figure out the user's location as well as handling any changes to that location. When I set up the watch I specified onGeo for the success handler and onGeoError for any errors. Because we want to add location to our note objects as an enhancement, the onGeoError handler actually does nothing at all (rather than causing an error, we'll simply leave geolocation data off the note).
 

function onGeoError() {
//So yeah, do nothing and I'm ok with that.
}

Obviously you could do something more here if you wanted, but for now we will leave it open. The success handler is going to copy the values into a global variable.

var geoPoint;
function onGeo(g) {
geoPoint = {latitude:g.coords.latitude, longitude:g.coords.longitude};
}

Now that we've got the user's location, how do we use it? We could simply pass them into the data stored at Parse. Remember, Parse lets you store anything. But instead we'll take advantage of Parse's special support for handling location objects. Let's take a look at the handler fired when user's click the Add Note button to see how this works.

$("#addNoteBtn").on("touchend", function(e) {
e.preventDefault();
//Grab the note details, no real validation for now
var title = $("#noteTitle").val();
var body = $("#noteBody").val();
var note = new NoteObject();
note.set("title", title);
note.set("body", body);
//if we have _some_ data
if(geoPoint.longitude) {
note.set("position", new Parse.GeoPoint({latitude:geoPoint.latitude, longitude:geoPoint.longitude}));
}
note.save(null, {
success:function(object) {
console.log("Saved the object!");
$("#noteTitle").val("");
$("#noteBody").val("");
getNotes();
},
error:function(object,error) {
console.dir(error);
alert("Sorry, I couldn't save it.");
}
});
});

We've made two significant changes to the handler. First, we no longer pass in a structure of data to the first argument of the save() call - notice it is null now. Instead, we use the set() API on the note object to set our values. We can then use a simple conditional to see if we have the user's location. If we do, we pass in a new instance of Parse.GeoPoint. By using a GeoPoint, it enables my application to do a number of geolocation based queries. While I won't be using it in this demo application, we could now use the Parse API to query for notes posted within a certain distance of a location. This is very powerful and Parse makes it incredibly easy.
 
Showing the note's location
So now that we have location data available along with our basic note text, how do we make use of it? One way would be to show it on a map. Google provides comprehensive mapping support, but for our purposes we can get by using their simpler Static Maps API. This API provides for basic mapping that doesn't require interactivity. By creating a URL that includes location data and other parameters, you can create a simple map that shows up as a basic image. But where do we show it?
One of the recent additions to PhoneGap (in version 2.3.0) is the addition of the InAppBrowser API. For folks who made use of the Child Browser plugin in the past, this will be familiar. Essentially, this lets you create a window above your application. It is useful for tasks like displaying web links that exist in a context outside of your application. It even provides multiple hooks into the window for noticing things like page loads and when the user closes the window. Luckily for us our needs are much simpler. Let's look at the modified version of the getNotes function.

function getNotes() {
var query = new Parse.Query(NoteObject);
query.find({
success:function(results) {
console.dir(results);
var s = "";
for(var i=0, len=results.length; i<len; i++) {
var note = results[i];
s += "";
s += ""+note.get("title")+"";
s += "Written "+note.createdAt + "";
//Do we have a geo point?
if(note.has("position")) {
var pos = note.get("position");
s += "View on Map";
}
s += note.get("body");
s += "";
}
$("#notes").html(s);
},
error:function(error) {
alert("Error when getting notes!");
}
});
}

Our changes begin in the result loop. I've added new code to handle checking for the existing of a position value. Remember, not all of our data may have it. If it does exist, I add some more HTML to our result. This includes a simple HTML link with a class of navLink. Also, note the use of data attributes to store the position values. In order to make these links work I've got an event handler just for touch events on them.

$(document).on("touchend", ".navLink", function(e) {
e.preventDefault();
//If we are offline, say we're sorry and move on..
if(!online) {
navigator.notification.alert("Maps can only be viewed online.", null, "Offline!");
return;
}
//Get the position from the data attribute
var long = $(this).data("longitude");
var lat = $(this).data("latitude");
//Generate Google Static Map API link
var link = "http://maps.googleapis.com/maps/api/staticmap?center="+lat+","+long+"&zoom=13&size=400x400&maptype=roadmap&markers=color:red%7Ccolor:red%7C"+lat+","+long+"&sensor=false";
window.open(link, "blank");
});

After blocking the normal link event, our code ensures the user is online. If they aren't, the user is notified and the handler exits. Next, we grab the location data from the HTML and create a link using the Static Map API. Even if this is your first time seeing the API, you should be able to take a good guess as to what the link is doing. Finally the link is opened using the traditional window.open API. Here's how the content looks in the iOS Simulator (Figure 1).
Figure 1. Content in the iOS Simulator.
Add here is what you get when one of the “View on Map” links is clicked (Figure 2).
Figure 2. Screen after a "View on Map" link is clicked.
Notice how the InAppBrowser API creates the typical browser chrome automatically. There is also a "Done” button that dismisses the window. What is not obvious in the screenshot is the nice transition when both opening and closing the window.
 
Where to go from here
Hopefully by now you are beginning to see the power of mixing PhoneGap with Parse. By offloading data storage to Parse we have a simple way to keep user data backed up and accessible even if they lose the device. By using PhoneGap, you have access to the device connection status as well as the GPS location. You can take the sample code attached to this article and upload it to PhoneGap Build today and use it to create your own application!
 
 
This work is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 Unported License. Permissions beyond the scope of this license, pertaining to the examples of code included within this work are available at Adobe.

More Like This