HTML5 Zone is brought to you in partnership with:

Raymond Camden is a developer evangelist for Adobe. His work focuses on web standards, mobile development and Cold Fusion. He's a published author and presents at conferences and user groups on a variety of topics. He is the happily married proud father of three kids and is somewhat of a Star Wars nut. Raymond can be reached via his blog at www.raymondcamden.com or via email at raymondcamden@gmail.com Raymond is a DZone MVB and is not an employee of DZone and has posted 234 posts at DZone. You can read more from them at their website. View Full User Profile

Building a Parse.com Enabled PhoneGap App - Part 3

11.01.2012
| 3409 views |
  • submit to reddit

 

Welcome to the third part of my blog series on building a Parse.com enabled PhoneGap application. If you haven't yet read the earlier entries in this series, please see the links at the bottom for background and the story so far. In today's entry, I finally get to some code! Not a whole lot of code, but actual data gets created and displayed which is quite a bit farther than we've gotten so far.

My goal for today's post is to build the initial layout of the application. That includes the home page, the Add Tip page, and the Get Tips page. I wanted the Add Tip page to actually support creating content while the Get Tips page would list out existing data in a simple list format. The fancy map stuff will come later.

Before I got started, I had a decision to make. I wanted my application to look nice on mobile devices. I could have used jQuery Mobile. But I didn't want jQuery Mobile set up to be a concern for folks reading this series. Whenever I blog about a particular topic, I try my best to focus in as narrowly as possible on that topic and avoid things that could cause distractions. I didn't want folks going through these blog entries to have issues with jQuery Mobile. Don't get me wrong - jQuery Mobile is very easy to use and I don't think it would cause any problems, but it nagged at me enough for me to consider other options.

In the end, I decided on Twitter Bootstrap. I knew that it had support for responsive mobile-friendly design, and I thought this would be an excellent opportunity to try it out. It also had minimal code requirements and would - for the most part - stay out of my way.

Deciding on the UI framework for an application is an incredibly important decision. I don't want to trivialize that. At the same time, I want to get my nice UI done quickly and focus in on the task at hand - making use of Parse.com and PhoneGap.

I began then with a simple home page based on my mockups from the first blog entry. I plopped in the header graphic, some bullcrap marketing text, and my buttons. Here's how it looked:

Notice that menu on top? It's part of the Bootstrap responsive navigation system. Clicking on it brings out a flyout menu. If viewed on a tablet in landscape mode, the items show up on the bar.

As a quick aside - how did I generate those screen shots? With Adobe Edge Inspect (formerly Shadow) of course. Edge Inspect was a great help as I built out the application.

Let's look at the code behind the homepage. There isn't anything terribly interesting here, but it will give you a look at a page with Bootstrap in it.

<!doctype html>
<html lang="en">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
<script src="js/parse-1.1.2.min.js"></script>
<script src="js/main.js"></script>
<link href="bootstrap/css/bootstrap.min.css" rel="stylesheet">
<link href="bootstrap/css/bootstrap-responsive.css" rel="stylesheet">
</head>

<body>

<div class="navbar">
<div class="navbar-inner">
<div class="container">

<a class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</a>

<!-- Be sure to leave the brand out there if you want it shown -->
<a class="brand" href="#">CowTipLine</a>

<div class="nav-collapse">
<ul class="nav">
<li class="active"><a href="index.html">Home</a></li>
<li><a href="add.html">Add Tip</a></li>
<li><a href="get.html">Get Tips</a></li>
</ul>
</div>

</div>
</div>
</div>

<div class="container">

<img src="images/ctl.png" title="CowTipLine">
<p>
Tipping Cows? Need to find a cow? Welcome to
the app that hooks you up!
</p>

<a href="add.html" class="btn btn-block">Add Tip</a>
<a href="get.html" class="btn btn-block">Get Tips</a>

</div>

<script src="js/jquery-1.8.2.min.js"></script>
<script src="bootstrap/js/bootstrap.min.js"></script>

</body>
</html>

Next up was the Add Tip page. Again, this was fairly easy since our tips consist of only three requirements. I built out a simple form:

<form id="addtipForm">

<label for="numcows">How many cows?</label>
<input type="number" id="numcows" class="input-xlarge" placeholder="Enter the number of cows">

<label for="howdangerous">How dangerous?</label>
<select id="howdangerous" class="input-xlarge">
<option value="1">Totally Safe</option>
<option value="2">Some Risk</option>
<option value="3">Farmer with Shotgun!</option>
</select>

<label for="comments">Anything else?</label>
<textarea id="comments" class="input-xlarge"></textarea>

<input type="submit" class="btn btn-block btn-primary" value="Send Tip">

</form>

Which gave me this...

Ok, so now let's talk about the code that's going to integrate with this form. I'll share the relevant code (which may be found in main.js), and then explain what's going on.

Parse.initialize("oe3dboiG0RzJNULxKvdHYGWEb3Cq7mzHRC3Dwh6E", "cR8whmMjybMoXUqfAzhxUJSiBXw3QPt7ZB4bRGw8");
TipObject = Parse.Object.extend("TipObject");

$("#addtipForm").on("submit", function(e) {
e.preventDefault();

//get values
var numcows = $("#numcows").val();
var howdangerous = $("#howdangerous option:selected").val();
var comments = $("#comments").val();

//TBD: Validation
var tip = new TipObject();
tip.save(
{
numcows:numcows,
howdangerous:howdangerous,
comments:comments
},{
success:function(object) {
console.log("Saved object");
doAlert("Tip Saved!", function() { document.location.href='index.html'; });
},
error:function(model, error) {
console.log("Error saving");
}
});

});

First off - I've got my initialization for Parse that makes use of my Application ID and JavaScript API key. I called these out in my last blog entry.

Next I create an instance of my TipObject. This is the Parse way to create a generic object type that will - eventually - be persisted. TipObject will act like a class I can reuse when working with data.

Now let's look at that event handler. The first portion is boilerplate jQuery form access. As you can see, I decided to skip on validation for now. (Validation is for wimps!) Once I've got my data, I can make a new tip. The variable, tip, is an instance of TipObject, which is created by the Parse library.

Saving that data then becomes trivial. The object has an instance of a save method. The first argument is the actual data I'm saving. Now - this is a crucial point here so pay close attention.

Parse data types are free form. That means I can define my object types any way I want. I've decided that "Tips" will consist of the number of cows, a ranking on how dangerous the area is, and free form comments. Parse doesn't care how I define my Tip object. Even more interesting, later on, I can completely change my mind on what the structure will look like.

This is both good and bad. During development, it's great. I can try things out and figure out which form "feels" best for the application. Parse will happily store new objects with a new schema no problem. In fact, I plan on doing just that in the next blog entry. (I'll be adding geolocation support.)

After launch though this falls into the realm of Very Bad Idea.

If you change your data structure after folks have been using the application, your code then has to handle this somehow. So imagine you had a data type called person with a "Name" property. You figure out later on it makes much more sense to have a firstName and lastName property instead. You now have to update all of that old data or write code that handles both 'styles' of objects. It is certainly possible to do that (don't forget that you can access Parse.com data via a REST API - which means you could use some server-side code to do a one-time data update) but for the most part, you want to try to get your structure as concrete as possible before launch.

Ok, enough with the sermon, you get the idea. But the coolest thing is - I didn't have to go to the Parse data browser. I didn't have to make a database table. I made the object. I saved it. I'm done. Seriously - I've just offloaded the entire database requirement to Parse.com in about 30 seconds of code. If that doesn't make you excited, then I don't know what will!

On saving the object, I let the user know and then send them back. The "doAlert" call there is simply a wrapper function I wrote to handle alerts and callbacks. When I get PhoneGap working with the code properly, that function will handle desktop versus device detection and use the PhoneGap Notification API when possible.

So - the final part to today's blog entry is the list function. I plan on using some mapping there and intelligent reporting (i.e., tell me the reports within 10 miles), but for now, I'm doing a simple list:

I'll skip the HTML for this one since it is just a div, but here's a look at how I retrieved that data:

var query = new Parse.Query(TipObject);
query.find({
success: function(results) {
var s = '';
for(var i=0, len=results.length; i<len; i++) {
var result = results[i];
console.dir(result);
s+= '<p>';
s+= '<b>ID:</b> '+ result.id + '<br/>';
s+= 'Created: ' + result.createdAt + '<br/>';
s+= 'Number of Cows: ' + result.attributes.numcows + '<br/>';
s+= 'How Dangerous?: ' + result.attributes.howdangerous + '<br/>';
s+= 'Comments: ' + result.attributes.comments;
s+= '</p>';
}
$("#tipdisplay").html(s);
},
error: function(error) {
alert("Error: " + error.code + " " + error.message);
}
});

Parse supports very detailed queries and pagination even. But for now, I simply ask the service for everything and dump it to screen. I'd typically use a JavaScript templating engine for this instead, but as it is temporary, it gets the job done.

Note how every object has an ID and createdAt automatically. I don't have to worry about that. Parse does it. It also tracks the last updated value as well.

Want to play with it? I've updated the GitHub repo with the latest code. Oh - and even though I'm not actually using any PhoneGap APIs yet I went ahead and created the application at PhoneGap Build:

https://build.phonegap.com/apps/215210/share

Note that while there is an iOS download link there, it will only work on my own devices. If this application gets fancy enough, and if readers want me to try, I'll actually submit this to the App Store and see what happens. Do note that this is a Hydration enabled build. That means as I update the application I can tell PhoneGap Build to automatically create new builds from the GitHub repo. It couldn't be easier.




Published at DZone with permission of Raymond Camden, author and DZone MVB. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)