Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

A Parse.com JavaScript Application with Offline Support

DZone's Guide to

A Parse.com JavaScript Application with Offline Support

· Mobile Zone
Free Resource

Launching an app doesn’t need to be daunting. Whether you’re just getting started or need a refresher on mobile app testing best practices, this guide is your resource! Brought to you in partnership with Perfecto

This morning I got a seemingly innocent question from a reader:

Came across your blog post on Parse + PhoneGap and wanted to get your opinion on the following use-case for that combo...

I've been exploring the possibilities of an app that essentially has a web form (similar to the contact form you've got right here, actually) that would store the resulting data via Parse. The reason being...it would be important that the app would allow a form to submit, even if there wasn't an active Internet connection available.

So, just wanted your thoughts on whether I am looking in the right direction to accomplish this. Don't have much experience in the way of iOS apps, but have to start somewhere, right?

I replied to him with a basic outline:

  • When you submit the form, hit Parse if you are online, hit WebSQL if not.
  • When the application starts, see if you have data in WebSQL, and if you are online, push it to parse.

That seemed simple enough, but I figured I might as well build a real demo just to prove it can be done. This is what I came up with. It's got some issues (don't we all?) but it covers the basics. As always though I'm open to suggestions for how this could be done better.

I began by creating the layout for an application. Since the reader just mentioned a form, I built the entire application around one form. I decided to build a simple UFO Report Form. It has a field for the number of UFOs, your name, and the description. I didn't make use of any UI framework but instead directed my incredible design skills at the task.

Here's the HTML behind the form, just in case your curious:

<h2>Sighting Reporter</h2>

<form id="sightForm">
Number of UFOs: <input type="number" id="numufos"><br/>
Your Name: <input type="text" id="yourname"><br/>
Description: <textarea id="description"></textarea><br/>
<input type="submit" value="Send Sighting">
</form>

Fancy, eh? Ok, now it's time to get into the code. I'm going to tackle this piece by piece, and it may get a bit confusing, but I'll post the entire file in one chunk at the end for your perusal.

Whether or not we are online, we need to set up the database. This is done via the WebSQL API. While this API is deprecated, it is fully supported in PhoneGap and works great in Chrome, the main browser I use for testing.

$(document).ready(function() {

     //initialize db
     db = window.openDatabase("sightingdb", "1.0", "Sighting Database", 200000);
     db.transaction(createDB, errorCB, initApp);

     function createDB(trans) {
          trans.executeSql("create table if not exists sighting(id INTEGER PRIMARY KEY,numufos,yourname,description)");
     }

});

view raw gistfile1.js This Gist brought to you by GitHub.

I'm not going to detail how this works as I've covered it before (Example of PhoneGap's Database Support), but even if this is brand new to you I think you can get the idea.

After the database is set up, our application needs to upload any existing data to Parse. We're going to skip that now though and look at the basic form handling aspects of the code.

I wrote a function to wrap my check for online/offline support. Why? I wrote this demo without actually building it as a PhoneGap application. It should work fine when converting into a mobile application, and at that point my wrapper function can be modified to use PhoneGap's API, but for my initial testing I just wanted to use the navigator.onLine property. Having a wrapper also let me easily add in a hack (see the commented out line) to test being offline.

function online() {
          //return false;
          return navigator.onLine;
}
view raw gistfile1.js This Gist brought to you by GitHub.

If we are online, I need to initialize Parse support. I won't repeat what is already covered in the Parse JavaScript Guide. Instead, this is just an example of how I initialize Parse.com with my API keys and define an object type I'm calling SightingObject (as in UFO sighting).

Parse.initialize("8Y0x2rCA0jKYdiC7wLKQuF9nQqKGFKdpqUHMfue3", "8m7ng0w9UirTV6k4ExsJ0WsmPGeZMsJd5hcu54Oq");
SightingObject = Parse.Object.extend("SightingObject");
view raw gistfile1.js This Gist brought to you by GitHub.

Now let's look at the form handler. Remember, this needs to either save to Parse or to the database.

$("#sightForm").on("submit", function(e) {
     e.preventDefault();
     /*
     gather the values - normally we'd do a bit of validation, but since UFO chasers
     are known for their rigorous and rational pursuit of science, this will not be necessary
     */
     var report = {};
          report.numufos = $("#numufos").val();
          report.yourname = $("#yourname").val();
          report.description = $("#description").val();
     console.log("To report: ",report);
     //ok, disable the form while submitting and show a loading gfx
     $(this).attr("disabled","disabled");
     $("#loadingGraphic").show();

     if(online()) {
           console.log("I'm online, send to parse");
          saveToParse(report,resetForm);
     } else {
          console.log("I'm offline, save to WebSQL");
          db.transaction(function(trans) {
                trans.executeSql("insert into sighting(numufos,yourname,description) values(?,?,?)", [report.numufos, report.yourname, report.description]);
          }, errorCB, resetForm);
      }
});

view raw gistfile1.js This Gist brought to you by GitHub.

This code block is a bit large, so let's break it down. The first thing I do is grab the values from the form. As mentioned in the comments, it would probably make sense to do some basic validation. Screw validation - this is a demo. Next I do some basic UI stuff to let the user know that exciting things are happening in the background (although in theory, not as exciting as the UFO in front of them). Then we have the online/offline block. I've taken the Parse logic out into another function that I'll show in a moment. The other part of the conditional simply writes it out to the database. In both cases we run a function, resetForm, that handles resetting my UI.

Here is saveToParse. Notice how darn easy this is. Just in case it isn't obvious - this is all the code I need to store my data, permanently, in the cloud. It would only be easier if the Parse.com engineers fed me grapes and lime jello shots while I wrote the code.

function saveToParse(ob,successCB) {
     var sightingObject = new SightingObject();
     sightingObject.save(ob, {
         success: function(object) {
               console.log("Saved to parse.");
               console.dir(object);
               successCB(object);
         },
         error: function(model, error) {
               console.log("Error!");
               console.dir(error);
         }
     });
}

view raw gistfile1.js This Gist brought to you by GitHub.

Before we get into the synchronization aspect, here is resetForm. Again, it just handles updating the UI and letting the user know something happened with their important data.

//handles removing the disabled form stuff and loading gfx
function resetForm() {
          $("#numufos").val("");
          $("#yourname").val("");
          $("#description").val("");
          $("#sightForm").removeAttr("disabled","disabled");
          $("#loadingGraphic").hide();
          var status = $("#status");
          if(online()) {
                 status.fadeIn().html("Your sighting has been saved!").fadeOut(4000);
          } else {
               status.fadeIn().html("Your sighting has been saved locally and will be uploaded next time you are online!").fadeOut(4000);
          }
}

 

view raw gistfile1.js This Gist brought to you by GitHub.

I did some quick testing and confirmed it was working. I used Parse.com's online data browser first:

I then tested offline storage. Chrome makes it easy to check since it has a database viewer built in:

That's almost it. The final piece of the puzzle is handling uploading the database data. This turned out to be simple too. If we are online, we can run a SQL against the table. If anything exists, we upload it and remove it.

//do we have existing objects in the db we can upload?
db.transaction(function(trans) {
     trans.executeSql("select * from sighting", [], function(trans,result) {
          //do we have rows?
          if(result.rows.length > 0) {
                    console.log("Ok, we need to push stuff up");
                    for(var i=0, len=result.rows.length; i<len; i++) {
                         var row = result.rows.item(i);

                         (function(row) {
                              //Parse will try to save everything, including ID, so make a quick new ob
                              var report = {};
                              report.numufos = row.numufos;
                              report.yourname = row.yourname;
                              report.description = row.description;

                              saveToParse(report, function() {
                                   console.log("i need to delete row "+row.id);
                                   db.transaction(function(trans) {
                                        trans.executeSql("delete from sighting where id = ?", [row.id]);
                                   }, errorCB);
                         });
                    }(row));

               }
          }
     },errorCB);
}, errorCB, function() {
     console.log("Done uploading the old rows");
});

view raw gistfile1.js This Gist brought to you by GitHub.

That's basically it. The biggest issue with this code is that it doesn't handle a change to your online/offline status, specifically, if you start the application offline, save some sightings, and then become online, it won't upload the old rows. That wouldn't be too hard to fix, but I was trying to keep it simple. At minimum, the next time you run the application it will upload those old records. For folks who want to see the entire code base, simply view the gist here: https://gist.github.com/3723074

I've also included a zip attached to this blog entry. (Note that the animated gif is courtesy of jQuery Mobile.)

Download attached file

 

Keep up with the latest DevTest Jargon with the latest Mobile DevTest Dictionary. Brought to you in partnership with Perfecto.

Topics:

Published at DZone with permission of Raymond Camden, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

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

X

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

{{ parent.tldr }}

{{ parent.urlSource.name }}