SQLite with Cordova: Unable to initialize database on other pages - sqlite

I'm playing around SQLite in Cordova as part of an upskilling process for work and I'm hitting a brick wall. The various articles I've read around initializing the SQLite plugin from Chris Brody is to always call it in after device ready, but all examples are around the index page. What if I need to populate data on the products.html page, without also calling all other initialization calls to the database?
What I mean is, given the following JS file, called core.js:
var db,
app = {
// Application Constructor
initialize: function() {
this.bindEvents();
},
// Bind Event Listeners
//
// Bind any events that are required on startup. Common events are:
// 'load', 'deviceready', 'offline', and 'online'.
bindEvents: function() {
document.addEventListener('deviceready', this.onDeviceReady, false);
},
// deviceready Event Handler
//
// The scope of 'this' is the event. In order to call the 'receivedEvent'
// function, we must explicitly call 'app.receivedEvent(...);'
onDeviceReady: function () {
app.receivedEvent('deviceready');
},
// Update DOM on a Received Event
receivedEvent: function (id) {
app.initdb();
console.log('Received Event: ' + id);
},
initdb: function () {
try {
db = window.sqlitePlugin.openDatabase({ name: 'meatblock.db' });
if (!db) {
console.error('Database unable to initialize, it either does not exist or is null');
return false;
}
else {
return true;
}
}
catch (err) {
console.error('Database initialization error: ' + err);
}
}
};
In the receivedEvent, which bubbles up, I call my initdb() function that calls the plugin and opens up the database.
The process works like a charm, in this method I can write my SQL SELECT statement to retrieve data and display it on the page without error.
As soon as I mode the TX script outside of this, it does not work. I even call the initdb() function before it, and still, I get an error saying that it cannot open database on undefined.
in core.js, at the top, I define db globally, as some have suggested in various other blogs, but the following code, out side of the receivedEvent just does not work:
jQuery(document).ready(function ($) {
app.initdb();
db.transaction(function (tx) {
tx.executeSql('SELECT * FROM table_1', [], function (tx, results) {
var _data = results;
for (var i = 0; i < results.rows.length; i++) {
var row = results.rows.item(i);
$li = $('<li></li>').text(row);
$('.table-output').append($li);
}
}, function (e) {
alert('an error occurred trying to retrieve database from table_1');
});
}, function (e) {
alert('an error occurd');
}, function () {
alert('all done');
});
});
after calling app.initdb() just before I handle a TX, my assumption is that it would open the database again, as at this point, right? Even if I don't use jQuery's ready statement, it just does not work, without jQuery:
app.initdb();
db.transaction(function (tx) {
tx.executeSql('SELECT * FROM table_1', [], function (tx, results) {
var _data = results;
for (var i = 0; i < results.rows.length; i++) {
var row = results.rows.item(i);
$li = jQuery('<li></li>').text(row);
jQuery('.table-output').append($li);
}
}, function (e) {
alert('an error occurred trying to retrieve database from table_1');
});
}, function (e) {
alert('an error occurd');
}, function () {
alert('all done');
});
I'm sure there is something that I'm not getting about this. Is it impossible to open the database and retrieve data outside of the device ready statement?

Related

Meteor publication error - Publish function returned an array of non-Cursors

I have this publication
Meteor.publish('temsInThisCompetition', function (id) {
var teams = [];
return Competitions.find(id).fetch().map(function (doc) {
for(var item in doc.teams){
teams.push(Teams.find(item));
}
return teams;
});
});
But I am getting this error
Exception from sub temsInThisCompetition id kDPuEbc9dtWn2tfT3 Error: Publish function returned an array of non-Cursors
This solved the problem for me
Meteor.publish('teamsInThisCompetition', function (id) {
var competition = Competitions.findOne(id);
return Teams.find({_id:{$in:competition.teams}});
});
Since Meteor is asynchronous, your initial code doesn't work because the team array is returned before the for loop is completed.
If you need a for loop in an async environment, you could use a callback function, like this:
function getTeams(id, callback) {
Competitions.find(id).fetch().map(function (doc) {
var teams = [];
for(var item in doc.teams){
teams.push(Teams.find(item));
if(teams.length==doc.teams.length) {
callback(teams);
}
}
});
}
Meteor.publish('temsInThisCompetition', function (id) {
getTeams(id, function(teams) {
return teams;
});
});
Collection.find() without a callback and Collection.findOne() are synchronous, that's why the code in your answer doesn't return an empty set.

Meteor Reactive Session: Not Working (Why?)

I'm having trouble with reactive Sessions in Meteor.js.
Demo: Meteor Pad
Template.rows.helpers({
'rows': function () {
return Session.get('rows'); // data set in Session
}
});
Template.count.events({
'click .mdl-radio__button': function (e) {
// target represents a number of selected rows (1, 2, 5, or 10)
var value = $(e.currentTarget).val();
Session.set('limit', value);
},
'click #reset': function () {
Session.set('limit', 0);
Session.set('rows', null);
},
'click #run': function () {
// should only get rows when run() is pressed
Session.set('rows', currentItems);
}
});
Users should be able to select a new number of collections to receive, controlled by the limit. However, I keep getting the following error:
Error: Match error: Failed Match.OneOf or Match.Optional validation
Any ideas why? Can someone show me a working MeteorPad demo?
I'm having trouble with your meteorpad. But your problem isn't Session. The problem is your usage of Tracker.autorun. You should read the docs on that.
You are assuming that Tracker.autorun(getItems) returns what getItems returns. That's not the case tough. You'll need to set currentItems inside the autorun (in your case getItems).
getItems = function () {
if (Session.get('limit') > 0) {
currentItems = Items
.find({}, {limit: Session.get('limit')})
.map(function (item, index) {
item.index = index + 1;
return item;
});
} else {
currentItems = null;
}
};
Finally figured it out. Apparently Session creates a string, so that Session.set('limit', 1) sets the limit to "1". Of course, strings can be processed in a Mongo collection request.
The solution was using {limit: parseInt(Session.get('limit')}.

Meteor wrapasync save Twitter REST response to Mongo collection giving an error "Error: Meteor code must always run within a Fiber."

I am trying to use Twitter REST API GET followers/ids and save it to Mongo using Mongo collection insert method
Code inside /server/server.js:
Meteor.startup(function () {
// code to run on server at startup
TwitterFollowersIDsCollecions = new Mongo.Collection("twitterFollowersIDs");
var Twit = Meteor.npmRequire('twit');
var T = new Twit({
consumer_key: '###',
consumer_secret: '###',
access_token: '###',
access_token_secret: '###'
});
var getTwitterFollowersIDsAsync = function (screenname, cb) {
T.get('followers/ids', { screen_name: screenname }, function (err, data, response) {
console.log(data);
var vids = data.ids;
for(var i in vids) {
TwitterFollowersIDsCollecions.insert({
twitterFollowerID:vids[i]
});
}
}
);
};
Meteor.methods({
getTwitterFollowersIDsCollectionsClient : function (screenname){
var getTwitterFollowersIDsNow = Meteor.wrapAsync(getTwitterFollowersIDsAsync);
var result = getTwitterFollowersIDsNow('meteorjs');
console.log(result);
return result;
}
});
});
Error in server console:
Error: Meteor code must always run within a Fiber.
Try wrapping callbacks that you pass to non-Meteor libraries with Meteor.bindEnvironment.
Objective is to save twitter followers to a Mongo collection.
Meteor v. 1.1.0.2
Meteor packages:
meteor-platform
autopublish
insecure
differential:vulcanize
accounts-twitter
accounts-ui
meteorhacks:npm
npm-container
npm modules being used inside Meteor through meteorhacks:npm: "twit": "1.1.20" (added inside packages.json)
**UPDATE Second attempt **
Meteor.startup(function () {
// code to run on server at startup
TwitterFollowersIDsCollecions = new Mongo.Collection("twitterFollowersIDs");
var Twit = Meteor.npmRequire('twit');
var T = new Twit({
consumer_key: '###',
consumer_secret: '###',
access_token: '###',
access_token_secret: '###'
});
Meteor.methods({
// this is the server method called from the client
getTwitterFollowersIDsCollectionsClient : function (){
setTimeout(function(){
Meteor.call('getTwitterFollowersIDsNow', 'meteorjs');
},10);
return;
},
getTwitterFollowersIDsNow : function (screenname) {
T.get('followers/ids', { screen_name: screenname }, function (err, data, response) {
console.log(data);
});
}
});
});
I'm then calling the below code from browser console:
Meteor.call('getTwitterFollowersIDsCollectionsClient');
The server crashes with the same error:
Error: Meteor code must always run within a Fiber. Try wrapping callbacks that you pass to non-Meteor libraries with Meteor.bindEnvironment.
UDPATE:
getTwitterFollowersIDsCollectionsClient : function (screenname){
Meteor.setTimeout(function(screenname){
T.get('followers/ids', { screen_name: screenname }, Meteor.bindEnvironment(function (err, data, response) {
console.log("from getTwitterFollowersIDsCollectionsClient : "+data.ids);
var vids = data.ids;
for(var i in vids)
{
TwitterFollowersIDsCollecions.insert({
twitterFollowerID:vids[i]
});
}
return data;
}));
},10);
return;
}
Added Meteor.bindEnvironment to T.get callback method. This code worked and I was able to save the follower IDs to a mongo collection
Glad you got it working, but I played around with this and Meteor provides another, super easy way: wrapAsync. At least, it was easy once I figured it out! Here's the server code I wound up with -
var T = new TwitMaker({
consumer_key: '...'
, consumer_secret: '...'
, access_token: '...'
, access_token_secret: '...'
})
var wrapGet = Meteor.wrapAsync(T.get, T);
Meteor.methods({
getTwitImg: function(target) {
data = wrapGet('users/show', {screen_name: target});
if (data) {
img_url = data['profile_image_url'];
US.update({twitter: target}, {$set: {'targetImg': img_url}});
return img_url;
}
}
});
For the client and template code see this gist: https://gist.github.com/DanAncona/a09ce375e48bfa8efeca
Your code is a bit confusing. It seems like you're trying to execute a web service call async, but still return the result immediately (which won't work).
First of all, you probably wouldn't need to wrap the function to fetch the followers in an async block.
If you want your server method to return something immediately to the client after it has been called, I'd use a Meteor.setTimeout (see What's the point of Meteor.setTimeout() vs just setTimeout()?) block and call another method to do the fetching:
Meteor.methods({
// this is the server method called from the client
getTwitterFollowersIDsCollectionsClient : function (screenname){
Meteor.setTimeout(function() {
Meteor.call('getTwitterFollowersIDsNow', 'meteorjs');
}, 10);
return;
},
getTwitterFollowersIDsNow : function (screenname) {
T.get('followers/ids', { screen_name: screenname }, function (err, data, response) {
console.log(data);
var vids = data.ids;
for(var i in vids) {
TwitterFollowersIDsCollecions.insert({
twitterFollowerID:vids[i]
});
}
}
}
});
Ideally you would use a template helper to retrieve your followers from your collection. Due to these kind of helpers being reactive, you could just call the server method from the client and let the reactivity of Meteor solve your problem of returning the followers via the helper (which is re-executed/re-rendering the template on data change).
try calling:
var wrappedInsert = Meteor.bindEnvironment(function(tweet) {
TweetsCollection.insert(tweet);},
"Failed to insert tweet into Posts collection.");
from inside of api callback
getTwitterFollowersIDsNow : function (screenname) {
T.get('followers/ids', { screen_name: screenname }, function (err, data, response) {
for(var i in data)
{
wrappedInsert(data[i]);
}
});
}

Working with Meteor and Async balanced-payments functions

I'm using balanced-payments and their version 1.1 of balanced.js within Meteor.
I'm trying to create a new customer using
balanced.marketplace.customers.create(formData);
Here is my CheckFormSubmitEvents.js file
Template.CheckFormSubmit.events({
'submit form': function (e, tmpl) {
e.preventDefault();
var recurringStatus = $(e.target).find('[name=is_recurring]').is(':checked');
var checkForm = {
name: $(e.target).find('[name=name]').val(),
account_number: $(e.target).find('[name=account_number]').val(),
routing_number: $(e.target).find('[name=routing_number]').val(),
recurring: { is_recurring: recurringStatus },
created_at: new Date
}
checkForm._id = Donations.insert(checkForm);
Meteor.call("balancedCardCreate", checkForm, function(error, result) {
console.log(result);
// Successful tokenization
if(result.status_code === 201 && result.href) {
// Send to your backend
jQuery.post(responseTarget, {
uri: result.href
}, function(r) {
// Check your backend result
if(r.status === 201) {
// Your successful logic here from backend
} else {
// Your failure logic here from backend
}
});
} else {
// Failed to tokenize, your error logic here
}
// Debuging, just displays the tokenization result in a pretty div
$('#response .panel-body pre').html(JSON.stringify(result, false, 4));
$('#response').slideDown(300);
});
}
});
Here is my Methods.js file
var wrappedDelayedFunction = Async.wrap(balanced.marketplace.customers.create);
Meteor.methods({
balancedCardCreate: function (formData) {
console.log(formData);
var response = wrappedDelayedFunction(formData);
console.log(response);
return response;
}
});
I get nothing back when I submit the form, except that on the server console I do see the log of the form data.
I'm sure I'm not calling some of these async functions correctly. The hard part for me here is that the balanced function are async, but I don't know if they fit into the same mold as some of the examples I've seen.
I've tried to follow this example code.
http://meteorhacks.com/improved-async-utilities-in-meteor-npm.html
Is there a specific change that needs to be done in regard to working with balanced here? Does anyone have any tips for working with Async functions or see something specific about my code that I've done wrong?
Thanks
The NPM utilities Async.wrap does the same thing as the undocumented Meteor function Meteor._wrapAsync, in that it takes an asynchronous function with the last argument function(err, result) {} and turns it into a synchronous function which takes the same arguments, but either returns a result or throws an error instead of using the callback. The function yields in a Fiber until the asynchronous callback returns, so that other code in the event loop can run.
One pitfall with this is that you need to make sure that the function you wrap is called with the correct context. So if balanced.marketplace.customers.create is a prototype method that expects this to be set to something, it will not be set properly unless you bind it yourself, using function.bind or any of the other various library polyfills.
For more information, see https://stackoverflow.com/a/21542356/586086.
What I ended up doing was using a future. This works great, I just need to do better at catching errors. Which will be a question for a pro I think ; - )
Credit should go to user3374348 for answering another similar question of mine, which solved both of these.
https://stackoverflow.com/a/23777507/582309
var Future = Npm.require("fibers/future");
function extractFromPromise(promise) {
var fut = new Future();
promise.then(function (result) {
fut["return"](result);
}, function (error) {
fut["throw"](error);
});
return fut.wait();
}
Meteor.methods({
createCustomer: function (data) {
balanced.configure(Meteor.settings.balancedPaymentsAPI);
var customerData = extractFromPromise(balanced.marketplace.customers.create({
'name': data.fname + " " + data.lname,
"address": {
"city": data.city,
"state": data.region,
"line1": data.address_line1,
"line2": data.address_line2,
"postal_code": data.postal_code,
},
'email': data.email_address,
'phone': data.phone_number
}));
var card = extractFromPromise(balanced.marketplace.cards.create({
'number': data.card_number,
'expiration_year': data.expiry_year,
'expiration_month': data.expiry_month,
'cvv': data.cvv
}));
var associate = extractFromPromise(card.associate_to_customer(customerData.href).debit({
"amount": data.total_amount*100,
"appears_on_statement_as": "Trash Mountain" }));
});
As Andrew mentioned, you need to set the context for the method.
Here's the way you can do that with Async.wrap
Async.wrap(balanced.marketplace.customers, "create");

meteor findOne or find returns undefined

I'm trying to do some validation prior to loading the main page. To do this I need to find a document that I have confirmed, exist in the Mongo Collection. Unfortunately finding the document in the client.js doesn't seem to work. In my opinion the client and server collection are not in sync. Based on similar articles i have read I made many changes without success. Here is a quick summary of what I have tried.
Option1: Try to find the record in the client side and not using auto-subscribe: record not found.
In app.js
credentialToken = "2KcNCRzpTHzyZ1111";
if (Meteor.isClient) {
Template.hello.greeting = function () {
return "Welcome to ares_sso.";
};
Meteor.startup(function () {
var results = Meteor.findrec(credentialToken);
console.log("results:",results); //results is undefined.
});
Template.hello.events({
'click input': function () {
if (typeof console !== 'undefined')
console.log("You pressed the button");
}
});
}
if (Meteor.isServer) {
Meteor.startup(function () {
// code to run on server at startup
});
}
In /client/app.js
crs_collection = new Meteor.Collection("crs");
Meteor.subscribe("crs");
Meteor.findrec = function(credentialToken) {
target = {credentialtoken:credentialToken};
recfound = crs_collection.findOne(target);
//No luck with find either.
//recfound = crs_collection.find({credentialtoken:credentialToken}, {limit:1}).fetch()[0];
console.log("recfound:",recfound); //returns recfound is undefined.
return recfound;
}
In /server/server.js
crs_collection = new Meteor.Collection("crs");
Meteor.publish("crs", function(){
return crs_collection.find();
});
Option2: Next I did the find in the server side, using a method "server_recfind" which worked but I'm not able get the content to the client.
In app.js
credentialToken = "2KcNCRzpTHzyZ1111";
if (Meteor.isClient) {
Template.hello.greeting = function () {
return "Welcome to ares_sso.";
};
Meteor.startup(function () {
var results = Meteor.call('server_findrec',credentialToken);
console.log("results=",results); // also returns undefined
});
Template.hello.events({
'click input': function () {
if (typeof console !== 'undefined')
console.log("You pressed the button");
}
});
}
if (Meteor.isServer) {
Meteor.startup(function () {
// code to run on server at startup
});
}
In /client/app.js
crs_collection = new Meteor.Collection("crs");
Meteor.subscribe("crs");
In /server/app.js
crs_collection = new Meteor.Collection("crs");
Meteor.publish("crs", function(){
return crs_collection.find();
});
// Using Sync which finds the record but how do I sent the content to the client?
Meteor.methods ({
'server_findrec': function(credentialToken) {
// tried unblock but didnt work
//this.unblock();
var rec = crs_collection.findOne({'credentialtoken': credentialToken});
console.log("INSIDE server findrec rec=",rec); //shows content found
// tried flush but it didn't do anything
crs_collection.flush;
return rec; //rec not returning to the client
}
})
Option3: Frustrated and since I was able to find the document record with the server method. I tried adding global variables to delivery the content to the client side. Unfortunately it didn't work
In app.js
credentialToken = "2KcNCRzpTHzyZ1111";
//added global variables
c1 = '';
c2 = '';
c3 = ''
if (Meteor.isClient) {
Template.hello.greeting = function () {
return "Welcome to ares_sso.";
};
Meteor.startup(function () {
var results = Meteor.call('server_findrec',credentialToken);
console.log("results=",results); // also returns undefined
console.log("c1=",c1);
console.log("c2=",c2);
console.log("c3=",c3);
});
Template.hello.events({
'click input': function () {
if (typeof console !== 'undefined')
console.log("You pressed the button");
}
});
}
if (Meteor.isServer) {
Meteor.startup(function () {
// code to run on server at startup
});
}
In /client/app.js
crs_collection = new Meteor.Collection("crs");
Meteor.subscribe("crs");
In /server/app.js
crs_collection = new Meteor.Collection("crs");
Meteor.publish("crs", function(){
return crs_collection.find();
});
// Using Sync which finds the record but how do I sent the content to the client?
Meteor.methods ({
'server_findrec': function(credentialToken) {
// tried unblock but didnt work
//this.unblock();
var rec = crs_collection.findOne({'credentialtoken': credentialToken});
console.log("INSIDE server findrec rec=",rec); //shows content found
c1 = rec.cont1;
c2 = rec.cont2;
c3 = rec.cont3;
//confirm that c1,c2 and c3 have content
console.log(In server_findrec c1=",c); //shows content
console.log(In server_findrec c2=",c2); //shows content
console.log(In server_findrec c3=",c3); //shows content
// tried flush to sync to client...didn't work
crs_collection.flush;
return rec; //rec not returning to the client
}
})
There is a lot more code, so I have assembled all of the above hoping it gives you a clear picture of what I have tried and what I'm trying to do. I'm sorry if I made a mistake in the process.
Overall it will be great to know what am I doing wrong? I believe the 3 scenarios should work. Any help or recommendation will be appreciated.
I'm using Meteor Release 0.7.1.2, no CoffeeScript.
Thank you all
You're making two mistakes:
Define crs_collection once, and make sure its in a file thats executed on the client AND the server. It should be defined globally.
crs_collection must be defined before your pub/sub code. Meteor executes files in the lib directory first, so its best to put your collection code there.
That's really all there is to it. I'm happy to provide an example if needed.

Resources