Сегодня утром я получил, казалось бы, невинный вопрос от читателя:
Наткнулся на ваше сообщение в блоге на Parse + PhoneGap и хотел бы узнать ваше мнение о следующем сценарии использования этого комбо …
Я изучал возможности приложения, которое по существу имеет веб-форму (аналогично контактной форме, которую вы получили прямо здесь), которая будет хранить полученные данные с помощью Parse. Причина в том, что … было бы важно, чтобы приложение позволяло отправлять форму, даже если не было активного подключения к Интернету.
Итак, просто хотелось, чтобы вы подумали о том, ищу ли я правильное направление для достижения этой цели. У вас нет большого опыта работы с приложениями для iOS, но нужно с чего-то начинать, верно?
Я ответил ему в общих чертах:
- Когда вы отправляете форму, нажмите Parse, если вы онлайн, и нажмите WebSQL, если нет.
- Когда приложение запустится, проверьте, есть ли у вас данные в WebSQL, и если вы подключены к сети, нажмите их для анализа.
Это казалось достаточно простым, но я подумал, что мог бы создать реальную демонстрацию, чтобы доказать, что это возможно. Это то, что я придумал. У него есть некоторые проблемы (не так ли?), Но он охватывает основы. Как всегда, хотя я открыт для предложений о том, как это можно сделать лучше.
Я начал с создания макета для приложения. Поскольку читатель только что упомянул форму, я построил все приложение вокруг одной формы. Я решил создать простую форму отчета по НЛО. В нем есть поле для количества НЛО, вашего имени и описания. Я не использовал какую-либо инфраструктуру пользовательского интерфейса, но вместо этого направил свои невероятные дизайнерские навыки на эту задачу
Вот HTML-код формы, на случай, если вам интересно:
<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>
Фантазии, а? Хорошо, теперь пришло время войти в код. Я собираюсь разобраться с этой частью за частью, и это может немного запутать, но я опубликую весь файл в одном куске в конце для вашего прочтения.
Независимо от того, в сети мы или нет, нам нужно настроить базу данных. Это делается через API WebSQL. Хотя этот API устарел, он полностью поддерживается в PhoneGap и прекрасно работает в Chrome, основном браузере, который я использую для тестирования.
$(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)");
}
});
Я не буду вдаваться в подробности, как это работает, как я уже говорил ( пример поддержки базы данных PhoneGap ), но даже если это совершенно новое для вас, я думаю, вы можете понять эту идею.
После того, как база данных настроена, наше приложение должно загрузить любые существующие данные в Parse. Мы собираемся пропустить это сейчас и рассмотрим основные аспекты обработки формы кода.
Я написал функцию, чтобы обернуть мой чек для онлайн / оффлайн поддержки. Зачем? Я написал это демо , не на самом деле строить его как приложение PhoneGap. Он должен нормально работать при преобразовании в мобильное приложение, и в этот момент мою функцию-обертку можно изменить, чтобы использовать API PhoneGap, но для моего первоначального тестирования я просто хотел использовать свойство navigator.onLine. Наличие обертки также позволило мне легко добавить хак (см. Закомментированную строку), чтобы проверить, что он не в сети.
function online() {
//return false;
return navigator.onLine;
}
Если мы в сети, мне нужно инициализировать поддержку Parse. Я не буду повторять то, что уже описано в руководстве Parse JavaScript Guide . Вместо этого, это всего лишь пример того, как я инициализирую Parse.com с помощью своих ключей API и определяю тип объекта, который я называю SightingObject (как в случае наблюдения НЛО).
Parse.initialize("8Y0x2rCA0jKYdiC7wLKQuF9nQqKGFKdpqUHMfue3", "8m7ng0w9UirTV6k4ExsJ0WsmPGeZMsJd5hcu54Oq");
SightingObject = Parse.Object.extend("SightingObject");
Теперь давайте посмотрим на обработчик формы. Помните, что это необходимо сохранить в Parse или в базу данных.
$("#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);
}
});
Этот блок кода немного большой, поэтому давайте разберем его. Первое, что я делаю, это беру значения из формы. Как упоминалось в комментариях, возможно, имеет смысл провести некоторую базовую проверку. Проверка винта — это демо. Затем я делаю некоторые базовые элементы пользовательского интерфейса, чтобы пользователь знал, что на заднем плане происходят захватывающие вещи (хотя в теории, не такие захватывающие, как НЛО перед ними). Тогда у нас есть онлайн / оффлайн блок. Я взял логику разбора в другую функцию, которую я покажу через минуту. Другая часть условного просто записывает его в базу данных. В обоих случаях мы запускаем функцию resetForm, которая обрабатывает сброс моего пользовательского интерфейса.
Вот saveToParse. Обратите внимание, как чертовски легко это. На всякий случай, если это не очевидно — это весь код, который мне нужен для постоянного хранения моих данных в облаке. Было бы проще, если бы инженеры Parse.com давали мне виноград и лайм-желе, пока я писал код.
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);
}
});
}
Прежде чем мы перейдем к аспекту синхронизации, здесь есть resetForm. Опять же, он просто выполняет обновление пользовательского интерфейса и позволяет пользователю узнать, что произошло с его важными данными.
//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); } }
Я провел небольшое тестирование и подтвердил, что оно работает. Сначала я использовал онлайн-браузер данных Parse.com:
Затем я проверил автономное хранилище. Chrome облегчает проверку, поскольку в него встроено средство просмотра базы данных:
Это почти все. Последний кусочек головоломки — загрузка данных из базы данных. Это тоже оказалось простым. Если мы в сети, мы можем запустить SQL для таблицы. Если что-то существует, мы загружаем его и удаляем.
//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");
});
Это в основном это. Самая большая проблема с этим кодом заключается в том, что он не обрабатывает изменения вашего онлайн / автономного статуса, в частности, если вы запустите приложение в автономном режиме, сохраните некоторые наблюдения, а затем перейдете в онлайн, оно не будет загружать старые строки. Это не было бы слишком сложно исправить, но я старался сделать это простым. Как минимум, при следующем запуске приложения оно загрузит эти старые записи. Для людей, которые хотят видеть всю базу кода, просто просмотрите суть здесь: https://gist.github.com/3723074
Я также включил почтовый индекс, приложенный к этой записи в блоге. (Обратите внимание, что анимированный GIF предоставлен jQuery Mobile.)