Hi I am using phonegap to develop a shopping app. I want to give the user an option to save their order and complete wheneven he/she finds convenient. My question where do I save the order data. Local file system or local db of the mobile device? I will like to save the order
in json format in a local file. Please suggest the best option for me. Also a snippet will be highly appreciated. Thanks
You could also use HTML5 localStorage as an easier alternative to file storage. I have been using an encapsulated version of localStorage to facilitate get/set operations and decrease namespace pollution. Please see code base below:
/**
* The class is designed to facilitate flexible permanent storage of key value
* pairs utilzing HTML5 localStorage.
*
* #class LocalMap
* #author Zorayr Khalapyan
* #version 10/25/2012
*/
var LocalMap = function ( name ) {
var that = {};
//Prevent compatability issues in different execution environments.
if ( !localStorage ) {
localStorage = {};
}
if ( !localStorage[name] ) {
localStorage[name] = "{}";
}
var setMap = function ( map ) {
localStorage[name] = JSON.stringify( map );
};
that.getMap = function () {
return JSON.parse( localStorage[name] );
};
/**
* Stores the specified (key, value) pair in the localStorage
* under the map's namespace.
*/
that.set = function ( name, object ) {
var map = that.getMap();
map[ name ] = object;
setMap( map );
};
that.get = function ( name ) {
var map = that.getMap();
return typeof( map[ name ] ) !== "undefined" ? map[name] : null;
};
that.importMap = function ( object ) {
var map = that.getMap();
var key;
for ( key in object ) {
if (object.hasOwnProperty(key)) {
map[key] = object[key];
}
}
setMap(map);
};
that.length = function () {
var map = that.getMap();
var size = 0, key;
for (key in map) {
if (map.hasOwnProperty(key)) size++;
}
return size;
};
that.erase = function () {
localStorage[name] = JSON.stringify({});
};
that.isSet = function (name) {
return that.get(name) != null;
};
that.release = function (name) {
var map = that.getMap();
if (map[name]) {
delete map[name];
}
setMap(map);
};
that.deleteNamespace = function(){
if (localStorage.hasOwnProperty(name)) {
delete localStorage[name];
}
};
return that;
};
LocalMap.destroy = function () {
for ( var item in localStorage ) {
if ( localStorage.hasOwnProperty( item ) ) {
delete localStorage[ item ];
}
}
};
LocalMap.exists = function (name) {
return (localStorage[name]) ? true : false;
};
Below are the unit tests for get and set functions:
test( "Test set()", function() {
var map = LocalMap('test-namespace');
///
map.set("var-1", "val-1");
map.set("var-2", "val-2");
map.set("var-3", "val-3");
//
ok(map.isSet("var-1"), "A variable should be successful set.");
ok(map.isSet("var-2"), "A variable should be successful set.");
ok(map.isSet("var-3"), "A variable should be successful set.");
});
test( "Test get()", function() {
var map = LocalMap('test-namespace');
map.set("var-1", "val-1");
map.set("var-2", "val-2");
map.set("var-3", "val-3");
///
var var1 = map.get("var-1");
var var2 = map.get("var-2");
var var3 = map.get("var-3");
var var4 = map.get("var-4");
//
equal(var1, "val-1", "A set variable should be succesfully retreived.");
equal(var2, "val-2", "A set variable should be succesfully retreived.");
equal(var3, "val-3", "A set variable should be succesfully retreived.");
equal(var4, null, "A variable that was not set should not be retreived.");
});
Hope this helps, and let me know if you have any questions.
How about the below code? I copied it from here. Actually I like its code.
// define dbContext & entities------------------------------------
var DemoDataContext = function () {
nova.data.DbContext.call(this, "Demo", "1.0", "Demo DB", 1000000);
this.users = new nova.data.Repository(this, User, "users");
this.roles = new nova.data.Repository(this, Role, "roles");
};
DemoDataContext.prototype = new nova.data.DbContext();
DemoDataContext.constructor = DemoDataContext;
var User = function () {
nova.data.Entity.call(this);
this.name = "";
this.password = "";
this.birthYear = 1980;
this.createdDate = new Date();
this.deleted = false;
};
User.prototype = new nova.data.Entity();
User.constructor = User;
var Role = function () {
nova.data.Entity.call(this);
this.name = "";
this.createdDate = new Date();
};
Role.prototype = new nova.data.Entity();
Role.constructor = Role;
// end define dbContext & entities------------------------------------
// service methods----------------------------------------------------
function getAllUsers(callback) {
new DemoDataContext().users.toArray(function (users) {
alert(users.length);
callback(users);
});
}
function getUserByName(name, callback) {
new DemoDataContext().users.where("name='" + name + "'").toArray(function (users) {
callback(users.firstOrDefault());
});
}
function addRole(roleName, callback) {
var role = new Role();
role.name = roleName;
var db = new DemoDataContext();
db.roles.add(role);
db.saveChanges(callback);
}
function updateUserPassword(username, password, callback) {
getUserByName(username, function (user) {
if (user == null) {
throw "no user found.";
}
user.password = password;
var db = new DemoDataContext();
db.users.update(user);
db.saveChanges(callback);
});
}
function deleteUserByName(name, callback) {
getUserByName(name, function (user) {
if (user == null) {
throw "no user found.";
}
var db = new DemoDataContext();
db.users.remove(user);
db.saveChanges(callback);
});
}
// end service methods----------------------------------------------------
Related
My Bookmark Service which stores a result item with JSON structure doesn't work well.
Probably the issue comes from the array that doesn't work with the local storage function.
I've tried to JSON.stringify my items.
var key = 'fud_bookmarks';
var bookmarks = [];
this.addBookmark = function(resultItem) {
var bookmarks = this.getBookmarks();
bookmarks.push(resultItem);
return setBookmarks(bookmarks);
};
this.deleteBookmark = function(resultItem) {
var bookmarks = this.getBookmarks();
var i = bookmarks.indexOf(resultItem);
if (i >= 0) {
bookmarks.splice(i, 1);
console.log(bookmarks)
return setBookmarks(bookmarks);
}
return false;
};
this.getBookmarkCount = function() {
return getBookmarks().length;
};
this.getBookmarks = function() {
var bookmarks = localStorage.getItem(key);
if (!bookmarks) {
bookmarks = [];
}
return bookmarks;
}
function setBookmarks(bookmarks) {
return localStorage.setItem(key, bookmarks);
}
The resulted items look like this.
{
id: "112",
docType: "doctyp117",
title: "Abschließender Bericht über die Arbeit des Kunsts…- September 1944, Exemplar für Tieschowitz, o.D.",
type: "Archivbestand",
description: null,
…
}
$$hashKey: "object:455"
archive: {
id: "24",
title: "Familienarchiv der Grafen Wolff Metternich zur Gracht"
}
right now I get this error in the console =>
bookmarks.push is not a function
at Object.addBookmark (bookmark.js:12)
localStorage store a string values, not an objects.
If you want to store an Array should stringify it.
function setBookmarks(bookmarks) {
return localStorage.setItem(key, JSON.stringify(bookmarks));
}
this.getBookmarks = function () {
var bookmarks = JSON.parse(localStorage.getItem(key));
...
https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage
If value in starage will be not a valid JSON JSON.parse method throw an error, so you can use a try catch to avoid errors:
this.getBookmarks = function () {
var bookmarks;
try {
bookmarks = JSON.parse(localStorage.getItem(key));
} catch (e) {
bookmarks = [];
}
...
deleteBookmark should looks like:
this.deleteBookmark = function (resultItem) {
var i = 0;
let bookmarks = this.getBookmarks();
for (;i < bookmarks.length - 1;i++) {
if (resultItem.id === bookmarks[i].id) {
break;
}
}
if (i >= 0) {
bookmarks.splice(i, 1);
console.log(bookmarks)
return setBookmarks(bookmarks);
}
return false;
}
Try this: Check if typeof bookmarks is not object then return empty array. As Array is also a special type of object in javascript.
this.getBookmarks = function() {
var bookmarks = localStorage.getItem(key);
if (typeof bookmarks !== "object") {
bookmarks = [];
};
return bookmarks;
};
I have a method that is failing when returning an array of objects. As mentioned in the title - the array is confirmed to be populated but is empty in the response.
Here is the full flow:
The Url:
http://localhost:53000/api/v1/landmarks?lat=40.76959&lng=-73.95136&radius=160
Is routed to the corresponding index:
api.route('/api/v1/landmarks').get(Landmark.list);
The Index Calls a Service:
exports.list = (req, res) => {
const landmark = new LandmarkService();
landmark.getLandmarks(req)
.then(landmarks => {
var response = new Object();
response.startindex = req.query.page;
response.limit = req.query.per_page;
response.landmarks = landmarks;
res.json(response);
})
.catch(err => {
logger.error(err);
res.status(422).send(err.errors);
});
};
The Service Method Uses a Data Access Class to Return the Promise
getLandmarks(req) {
const params = req.params || {};
const query = req.query || {};
const page = parseInt(query.page, 10) || 1;
const perPage = parseInt(query.per_page, 10);
const userLatitude = parseFloat(query.lat);
const userLongitude = parseFloat(query.lng);
const userRadius = parseFloat(query.radius) || 10;
const utils = new Utils();
const data = new DataService();
const landmarkProperties = ['key','building','street','category','closing',
'email','name','opening','phone','postal','timestamp','type','web'];
return data.db_GetAllByLocation(landmarksRef, landmarkLocationsRef,
landmarkProperties, userLatitude, userLongitude, userRadius);
} // getLandmarks
However, the response is always empty.
I am building an array in the called method and populating it with JSON objects. That is what is supposed to be sent back in the response. I can confirm that the attributes array is correctly populated before I hit the return statement. I can log it to the console. I can also send back a test array filled with stub values successfully.
I have a feeling it is how I am setting things up inside the Promise?
Data Access Method That Should Return Array of Objects:
db_GetAllByLocation(ref, ref_locations, properties, user_latitude, user_longitude, user_radius)
{
const landmarkGeoFire = new GeoFire(ref_locations);
var geoQuery = landmarkGeoFire.query({
center: [user_latitude, user_longitude],
radius: user_radius
});
var locations = [];
var onKeyEnteredRegistration = geoQuery.on("key_entered", function (key, coordinates, distance) {
var location = {};
location.key = key;
location.latitude = coordinates[0];
location.longitude = coordinates[1];
location.distance = distance;
locations.push(location);
});
var attributes = [];
var onReadyRegistration = geoQuery.on("ready", function() {
ref.on('value', function (refsSnap) {
refsSnap.forEach((refSnap) => {
var list = refSnap;
locations.forEach(function(locationSnap)
{
//console.log(refSnap.key, '==', locationSnap.key);
if (refSnap.key == locationSnap.key)
{
var attribute = {};
for(var i=0; i<=properties.length-1; i++)
{
if(properties[i] == 'key') {
attribute[properties[i]] = refSnap.key;
continue;
}
attribute[properties[i]] = list.child(properties[i]).val();
}
attribute['latitude'] = locationSnap.latitude;
attribute['longitude'] = locationSnap.longitude;
attribute['distance'] = locationSnap.distance;
attributes.push(attribute);
} // refSnap.key == locationSnap.key
}); // locations.forEach
}); // refsSnap.forEach
return Promise.resolve(attributes); <-- does not resolve (throws 'cannot read property .then')
//geoQuery.cancel();
}); // ref.on
}); // onreadyregistration
return Promise.resolve(attributes); <-- comes back empty
}
It seems that data.db_GetAllByLocation is an asynchronous function, therefore the call resolve(landmarks); is getting called before the execution of the async function is finished. If the data.db_GetAllByLocation returns a promise then call the resolve(landmarks) inside the promise.
data.db_GetAllByLocation().then(function() {
resolve();
})
Also try the following modified db_GetAllByLocation()
db_GetAllByLocation(ref, ref_locations, properties, user_latitude, user_longitude, user_radius)
{
return new Promise(function(resolve, reject){
const landmarkGeoFire = new GeoFire(ref_locations);
var geoQuery = landmarkGeoFire.query({
center: [user_latitude, user_longitude],
radius: user_radius
});
var locations = [{}];
var onKeyEnteredRegistration = geoQuery.on("key_entered", function (key, coordinates, distance) {
var location = {};
location.key = key;
location.latitude = coordinates[0];
location.longitude = coordinates[1];
location.distance = distance;
locations.push(location);
});
var attributes = [{}];
var onReadyRegistration = geoQuery.on("ready", function() {
ref.on('value', function (refsSnap) {
refsSnap.forEach((refSnap) => {
var list = refSnap;
locations.forEach(function(locationSnap)
{
if (refSnap.key == locationSnap.key)
{
var attribute = {};
for(var i=0; i<=properties.length-1; i++)
{
if(properties[i] == 'key') {
attribute[properties[i]] = refSnap.key;
continue;
}
attribute[properties[i]] = list.child(properties[i]).val();
}
attribute['latitude'] = locationSnap.latitude;
attribute['longitude'] = locationSnap.longitude;
attribute['distance'] = locationSnap.distance;
attributes.push(attribute);
} // refSnap.key == locationSnap.key
}); // locations.forEach
}); // refsSnap.forEach
// return JSON.stringify(attributes);
return resolve(attributes);
}); // ref.on
}); // onreadyregistration
});
}
OK, I sorted this by removing all my code and writing some test logic (I should have done this before I posted my question).
The below flow works for me, and, applied back to my code, gave me the results I was looking for. No need to re-post the code, but maybe the below flow will be helpful to somebody.
route
api.route('/api/v1/landmarks').get(Landmark.test);
index
exports.test = (req, res) => {
const landmark = new LandmarkService();
landmark.getLandmarksTest(req)
.then(landmarks => {
var final = {};
final.attr1 = 'attr1';
final.attr2 = 'attr2';
final.landmarks = landmarks;
res.json(final);
})
.catch(err => {
logger.error(err);
res.status(422).send(err.errors);
});
};
service method
getLandmarksTest(req)
{
const data = new DataService();
data.db_PromiseTest().then(results => {
return Promise.resolve(results);
}).catch(err => {
return Promise.reject(err.errors);
});
}
data layer method
db_PromiseTest()
{
var stub = {
"name": "Madame Uppercut",
"age": 39,
"secretIdentity": "Jane Wilson",
"powers": [
"Million tonne punch",
"Damage resistance",
"Superhuman reflexes"
]
};
return Promise.resolve(stub);
}
I'm constructing a Builder in JavaScript, and I'm not sure how builders normally handle undefined values for optionals. I would like to think that the Builder doesn't append the optional field to the object if the field is undefined. Is that acceptable for a Builder? If not, what would be alternatives?
Here is a sample of the first implementation where the builder doesn't append an undefined optional:
Builder:
function Thing(required1, required2, required3) {
//check required params
var fields = {
required1: required1,
required2: required2,
required3: required3
};
var _withOptionalParam = function(param) {
if(!param) { return this; } //exit function early if param is undefined
fields.param = param;
return this;
};
var _build = function() {
var result = fields;
return result;
};
var builder = {
withOptionalParam: _withOptionalParam,
build: _build
};
return builder;
}
In action:
var thing = new Thing("foo","bar","baz").withOptionalParam(undefined).build();
//'thing' should be
// {
// required1:"foo",
// required2:"bar",
// required3:"baz"
// };
//
Thanks in advance!
I think you are losing the context of this in your _withOptinalParam function. You could bind you fields object to it as the this context.
function Thing(required1, required2, required3) {
//check required params
var fields = {
required1: required1,
required2: required2,
required3: required3
};
var _withOptionalParam = function(param) {
if(!param) { return this; } //exit function early if param is undefined
fields.param = param;
return this;
};
var _build = function() {
var result = fields;
return result;
};
var builder = {
withOptionalParam: _withOptionalParam.bind(fields),
build: _build
};
return builder;
}
var thing = new Thing("foo","bar","baz").withOptionalParam(undefined);
console.log( thing );
//'thing' should be
// {
// required1:"foo",
// required2:"bar",
// required3:"baz"
// };
//
<script src="http://codepen.io/synthet1c/pen/WrQapG.js"></script>
i reuse the same template in other route with different data argument but using the same publication...
if i do normal pub/sub, the data being published as expected. but when i do conditional pub/sub like below, i fail to subscribe the data. console log return empty array,,,
server/publication.js
Meteor.publish('ACStats', function(cId, uId) {
var selectors = {cId:cId, uId:uId};
var options = {
fields: {qId:0}
};
return ACStats.find(selectors,options);
});
client/onCreated
Template.channelList.onCreated(function() {
this.disable = new ReactiveVar('');
if (FlowRouter.getRouteName() === 'profile') {
var self = this;
self.autorun(function() {
var penName = FlowRouter.getParam('penName');
var u = Meteor.users.findOne({slugName:penName});
if (u) {var uId = u._id;}
Meteor.subscribe('ACStats', null, uId);
});
} else{
var self = this;
self.autorun(function() {
var channelName = FlowRouter.getParam('channel');
var c = Channels.findOne({title:channelName});
if (c) {var cId = c._id;}
Meteor.subscribe('ACStats', cId, null);
});
}
});
console
ACStats.find().fetch() //return empty array
anyone have figured out my mistake ..??
thank You so much....
You can make two publications:
Meteor.publish ('ACStatsChannels', cId, function() {
});
Meteor.publish ('ACStatsUsers', uId, function() {
})
And then subscribe like this:
Template.channelList.onCreated(function() {
this.disable = new ReactiveVar('');
var self = this;
self.autorun(function() {
if (FlowRouter.getRouteName() === 'profile') {
var penName = FlowRouter.getParam('penName');
self.subscribe('ACStatsUsers', penName);
} else {
var channelName = FlowRouter.getParam('channel');
self.subscribe('ACStatsChannels', channelName);
}
});
});
I want to be able to call simultaneously something like this in javascript:
classInstance.room.get('criteria');
classInstance.room('criteria').remove('criteria');
classInstance.room().update('criteria');
I have seen implemented something similar at shouldjs
should(10).be.a.Number();
(10).should.be.a.Number();
Updated
I have the following code:
function connectToDatabase() {
var server = orientDB(dbConfig.server);
var db = server.use(dbConfig.database);
db.on("endQuery", function onDbEndQuery() {
db.server.close();
});
return db;
}
var DbSet = function DbSet(name) {
return {
list: function list(where, select, order) {
where = where || true;
select = _.isString(select) || _.isArray(select) ? select : '*';
order = _.isString(order) || _.isArray(order) ? order : 'rid';
return connectToDatabase()
.select(select)
.from(name)
.where(where)
.order(order)
.all();
},
get: function get(where, select) {
where = where || true;
select = _.isString(select) || _.isArray(select) ? select : '*';
return connectToDatabase()
.select(select)
.from(name)
.where(where)
.all()
.then(function onResults(results) {
if (results.length > 1) {
throw new Error('multiple results');
}
return results[0];
});
},
create: function create(record) {
return connectToDatabase()
.insert()
.into(name)
.set(record)
.one();
},
update: function update(where, changes) {
where = where || true;
return connectToDatabase()
.update(name)
.set(changes)
.where(where)
.scalar();
},
remove: function remove(where) {
where = where || true;
return connectToDatabase()
.delete('VERTEX', name)
.where(where)
.scalar();
}
};
};
var db = function getDb() {
return {
room: new DbSet('Room'),
invitation: new DbSet('Invitation'),
participant: new DbSet('Participant'),
};
};
module.exports = db();
And I want to change the code be able to execute the following code:
var db=require('path/to/database');
var room = db.room.get({name:'room 1'});
var sameRoom = db.room({name:'room 1'}).get();
db.room.create({name:'second room'});
db.room({name:'second room'}).create();
//same for methods list and delete
var room = db.room.list({status:'active'});
var sameRooms = db.room({status:'active'}).list();
db.room.update({name:'second room'},{status:'inactive'});
db.room({name:'second room'}).update({status:'inactive'});
I want to be able to execute the same code for Invitation and Participant too.
We need more information as to what those functions do, but this code presents those features.
Klass = function () {};
Klass.prototype.room = function () {
....
return {
get: function () {...},
remove: function () {...},
update: function () {...}
}
};
Klass.prototype.room.get = function () {...};
classInstance = new Klass();