Статьи

Лучший пример PhoneGap, Parse и загрузки файлов

Несколько дней назад я написал о том, как JavaScript API Parse теперь позволяет загружать файлы через их SDK. Демонстрация, которую я построил, была очень быстрой и простой, и, хотя в ней использовался PhoneGap, он не был хорошим примером совместной технологии. Прежде, чем я говорил о Parse на PhoneGap Day (видимо, скоро будут опубликованы видео, я поделюсь ими, когда они появятся), я привел немного более хороший пример. Давайте взглянем.

Мой пример (извините) еще один пример приложения для заметок. Однако на этот раз я добавил возможность прикреплять картинки к заметке. Главный экран — это список ваших текущих заметок, отсортированный по дате.

Нажав на символ плюса, вы попадете в форму, позволяющую написать новую заметку.

На данный момент вы можете сделать снимок. Теперь — в целях тестирования в моем симуляторе iOS я установил источник в локальной файловой системе. В реальном приложении вы бы запрашивали саму камеру (или позволяли пользователю выбирать), но я хотел что-то быстрое и грязное.

Как только вы нажмете Сохранить, мы создадим новый объект Note в Parse. Код должен определить, сделали ли вы фотографию или нет, и будет ли она обрабатывать загрузку для вас. Теперь давайте посмотрим на код.

Сначала — домашняя страница. Я использую jQuery Mobile для приложения и разместил обе «страницы» в основном index.html. Так как в этом есть некоторая путаница, позвольте мне быть абсолютно ясным. JQuery Mobile не заставляет вас использовать одну HTML-страницу. Период. В таком случае, когда у меня есть небольшое приложение (2 страницы), тогда для меня имеет смысл включить их в один HTML-файл. Это был на 100% личный выбор, и jQuery Mobile не заставил меня сделать это.

<!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>

HTML здесь довольно прост, так как почти весь контент динамический. Теперь давайте посмотрим на 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");
	}

});

Сначала взгляните на событие pagehow для #home. Здесь мы получаем данные из Parse. Это делается с помощью простого запроса, который упорядочивает при создании объекта. Я ограничил счет до 10, и если бы я хотел, мог бы добавить пейджинг.

Логика addNote немного сложнее. Сохранение данных анализа выполняется асинхронно, поэтому, если нам нужно сохранить файл, у нас есть два, а не один, асинхронных вызова. Отсюда большой блок IF, который проверяет, есть ли у нас уже выбранное изображение. Честно говоря, это может быть сделано немного лучше, возможно. Например, первоначальное создание объекта Note может быть определенно исключено из предложения IF, а также строки, в которой я устанавливаю свойство text. Но в целом я думаю, вы поняли идею.

Во всяком случае, я надеюсь, что это полезно для людей. Я скопировал копию этого приложения и прикрепил его к записи в блоге.

Скачать прикрепленный файл