DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
View Events Video Library
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Integrating PostgreSQL Databases with ANF: Join this workshop to learn how to create a PostgreSQL server using Instaclustr’s managed service

Mobile Database Essentials: Assess data needs, storage requirements, and more when leveraging databases for cloud and edge applications.

Monitoring and Observability for LLMs: Datadog and Google Cloud discuss how to achieve optimal AI model performance.

Automated Testing: The latest on architecture, TDD, and the benefits of AI and low-code tools.

Trending

  • Common Problems in Redux With React Native
  • Multi-Tenancy With Keycloak, Angular, and SpringBoot
  • Micro Frontends for Quarkus Microservices
  • TypeScript: Useful Features

Better example of PhoneGap, Parse, and uploading files

Raymond Camden user avatar by
Raymond Camden
·
Jul. 29, 13 · Interview
Like (0)
Save
Tweet
Share
7.66K Views

Join the DZone community and get the full member experience.

Join For Free

A few days back I posted about how Parse's JavaScript API now makes it easy to upload files via their SDK. The demo I built was very quick and simple, and while it made use of PhoneGap, it wasn't a great example of the technologies together. Before I spoke on Parse at PhoneGap Day (apparently videos will be posted soon, I'll share when they are) I whipped up a slightly nicer example. Let's take a look.

My example is (I'm sorry) another example of a Note taking app. However this time I've added the ability to attach pictures to a note. The home screen is a listing of your current notes, sorted by date.

Clicking the plus symbol takes you to a form allowing you to write a new note.

At this point you can select to take a picture. Now - for testing purposes in my iOS Simulator, I set the source to the local file system. In a real world app you would ask for the camera itself (or allow the user to select), but I wanted something quick and dirty.

Once you click Save, we then create a new Note object at Parse. The code has to determine if you've taken a picture or not and if you have, it will handle the upload for you. Now let's look at the code.

First - the home page. I'm using jQuery Mobile for the application and have placed both "pages" in the core index.html. Since there seems to be some confusion about this, let me be absolutely clear. jQuery Mobile does not make you use one html page. Period. In a case like this where I have a small app (2 pages), then it made sense for me to include them in one html file. That was 100% a personal choice and not anything jQuery Mobile forced me to do.

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
        <title></title>
        <meta name = "format-detection" content = "telephone=no"/>
        <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width">
  	<link rel="stylesheet" href="http://code.jquery.com/mobile/1.3.1/jquery.mobile-1.3.1.min.css" />
		<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
		<script src="http://code.jquery.com/mobile/1.3.1/jquery.mobile-1.3.1.min.js"></script>
        <script type="text/javascript" src="cordova.js"></script>
        <script src="js/parse-1.2.8.min.js"></script>
        <script src="js/app.js"></script>
        <style>
        div[data-role=content] img {
        	max-width: 200px;
        }
        </style>        
    </head>
    <body>

    	<div data-role="page" id="home">

    		<div data-role="header" data-position="fixed">
    			<a href="#addNote" data-icon="plus" data-iconpos="notext" class="ui-btn-right">Add</a>
    			<h1>Notebook</h1>
    		</div>

    		<div data-role="content">
    		</div>


    	</div>

    	<div data-role="page" id="addNote">

    		<div data-role="header">
    			<a href="#home" data-icon="home" data-iconpos="notext">Home</a>
    			<h1>Notebook</h1>
    		</div>

    		<div data-role="content">
		        <h2>Add Note</h2>
		        <textarea id="noteText"></textarea>
		        <button id="takePicBtn">Add Pic</button>
		        <button id="saveNoteBtn">Save</button>
    		</div>

    	</div>

    </body>
</html>

The HTML here is pretty bare since almost all of the content is dynamic. Now let's take a look at app.js.

var parseAPPID = "supersecret";
var parseJSID = "mybankpinis1234";

//Initialize Parse
Parse.initialize(parseAPPID,parseJSID);

var NoteOb = Parse.Object.extend("Note");

$(document).on("pageshow", "#home", function(e, ui) {
  $.mobile.loading("show");

	var query = new Parse.Query(NoteOb);
	query.limit(10);
	query.descending("createdAt");

	query.find({
		success:function(results) {
			$.mobile.loading("hide");
			var s = "";
			for(var i=0; i<results.length; i++) {
				//Lame - should be using a template
				s += "<p>";
				s += "<h3>Note " + results[i].createdAt + "</h3>";
				s += results[i].get("text");
				var pic = results[i].get("picture");
				if(pic) {
					s += "<br/><img src='" + pic.url() + "'>";
				}
				s += "</p>";
			}
			$("#home div[data-role=content]").html(s);
		},error:function(e) {
			$.mobile.loading("hide");

		}
	});
});

$(document).on("pageshow", "#addNote", function(e, ui) {

	var imagedata = "";

	$("#saveNoteBtn").on("touchend", function(e) {
		e.preventDefault();
		$(this).attr("disabled","disabled").button("refresh");

		var noteText = $("#noteText").val();
		if(noteText == '') return;

		/*
		A bit complex - we have to handle an optional pic save
		*/
		if(imagedata != "") {
			var parseFile = new Parse.File("mypic.jpg", {base64:imagedata});
			console.log(parseFile);
				parseFile.save().then(function() {
					var note = new NoteOb();
					note.set("text",noteText);
					note.set("picture",parseFile);
					note.save(null, {
						success:function(ob) {
							$.mobile.changePage("#home");
						}, error:function(e) {
							console.log("Oh crap", e);
						}
					});
					cleanUp();
				}, function(error) {
					console.log("Error");
					console.log(error);
				});

		} else {
			var note = new NoteOb();
			note.set("text",noteText);
			note.save(null, {
				success:function(ob) {
					$.mobile.changePage("#home");
				}, error:function(e) {
					console.log("Oh crap", e);
				}
			});
			cleanUp();

		}
	});

	$("#takePicBtn").on("click", function(e) {
		e.preventDefault();
		navigator.camera.getPicture(gotPic, failHandler, 
			{quality:50, destinationType:navigator.camera.DestinationType.DATA_URL,
			 sourceType:navigator.camera.PictureSourceType.PHOTOLIBRARY});
	});
	
	function gotPic(data) {
		console.log('got here');
		imagedata = data;
		$("#takePicBtn").text("Picture Taken!").button("refresh");
	}
	
	function failHandler(e) {
		alert("ErrorFromC");
		alert(e);
		console.log(e.toString());
	}

	function cleanUp() {
		imagedata = "";
		$("#saveNoteBtn").removeAttr("disabled").button("refresh");
		$("#noteText").val("");
		$("#takePicBtn").text("Add Pic").button("refresh");
	}

});

First take a look at the pageshow event for #home. This is where we get data from Parse. This is done via a simple query that orders by object creation. I limit the count to 10 and if I wanted to could add paging.

The addNote logic is a bit more complex. Saving Parse data is asynchronous so if we need to store a file we have two, not one, async calls to make. Hence the big IF block that checks if we've got an existing selected image. To be honest this could be done a bit nicer perhaps. For example, the initial creation of the Note object could definitely be taken out of the IF clause, as well as the line where I set the text property. But in general I think you get the idea.

Anyway, I hope this is useful for folks. I've zipped up a copy of this application and attached it to the blog entry.

Download attached file

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

Opinions expressed by DZone contributors are their own.


Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends: