I'm new to PhoneGap I'm using database and its working fine with below code
db = window.openDatabase("Sample", "1.0", "PhoneGap Demo", 200000);
db.transaction(getDetails, transaction_error);
function transaction_error(tx, error) {
$('#busy').hide();
alert("Database Error: " + error);
}
function getUserDetails(tx) {
var sql = "select id, Name, DisplayName from details where Name=Name";
try {
tx.executeSql(sql, [],getList_success);
}
catch(err)
{
alert(err);
}
}
function getList_success(tx, results)
{
var len = results.rows.length;
for(var i=0; i <len; i++)
{
//Some code goes here
}
db = null;
}
Now i want to use the functions getUserDetails and getList_success by passing paramenters, i tried the below code its giving error
db = window.openDatabase("Sample", "1.0", "PhoneGap Demo", 200000);
getUserDetails(db);
function getUserDetails(tx) {
var sql = "select id, Name, DisplayName from details where Name=Name";
try {
tx.executeSql(sql, [],getList_success);
}
catch(err)
{
alert(err);
}
}
the error is TypeError:'undefined' is not a function in the catch block, can any one help me to get through this?.
Thanks in advance
The executeSql function need 4 parameters, try this:
function errorCallback(err){
//show error
}
...
tx.executeSql(sql, [],getList_success,errorCallback);
The problem is that you are handing over a callback function to db.transaction. If you want to provide some data to the callee you have to provide this in the context of the caller by using a closure.
var db = window.openDatabase("Sample", "1.0", "PhoneGap Demo", 200000);
var context = {val1: 'x', val2: 'y'};
db.transaction(function(tx){
myfunction(tx,context);
},
tx_error,
tx_success);
function myfunction(ty,context){
tx.executeSql(query,[context.val1,context.val2],myf_success,myf_error);
}
Keep in mind that this happens in a new thread! So if you call db.transaction be aware that this happens concurrently to the caller. If e.g. you are calling db.transaction() from inside the success callback of a jQuery.ajax() call the ajax thread will run on and the complete callback may be executed while the transaction is still in progress.
Look at your code:
db.transaction(getDetails, transaction_error);
getDetails!
Look at the functions you declared:
function getUserDetails(tx) {...}
function getList_success(tx, results) {....}
getDetails is not getUserDetails....
That's why you are getting the error.
Related
I did a couple of projects with node.js and I'm aware of the async behaviour and that one should usually use callback functions, etc. But one thing that bothers me ist the following.
I'm developing an Alexa skill and I have a function that handles the User intent:
'MyFunction': function() {
var toSay = ""; // Holds info what Alexa says
// Lot of checks and calculations what needs to be said by Alexa (nothing special)
if(xyz) {
toSay = "XYZ";
}else if(abc) {
toSay = "ABC";
}else{
toSay = "Something";
}
// Here is the "tricky" party
if(someSpecialEvent) {
toSay += " "+askDatabaseForInput(); // Add some information from database to string
}
this.emit(':ask', toSay, this.t('REPROMT_SPEECH')); // Gives the Info to Alexa (code execution stops here)
}
As mentioned in the code, there is some code which is usually used to find out what the output to Alexa should be.
Only on rare events, "someSpecialEvent", I need to query the database and add information to the String "toSay".
Querying the DB would look something like:
function askDatabaseForInput() { // The function to query the DB
var params = {
TableName: "MyTable",
OtherValues: "..."
};
// Do the Query
docClient.query(params, function(err, data) {
// Of course here are some checks if everything worked, etc.
var item = data.Items[0];
return item; // Item SHOULD be returned
});
return infoFromDocClient; // Which is, of course not possible
}
Now I know, that in the first function "'MyFunction'" I could just pass the variable "toSay" down to the DB Function and then to the DB Query and if everything is fine, I would do the "this.emit()" in the DB Query function. But for me, this looks very dirty and not much reusable.
So is there a way I can use "askDatabaseForInput()" to return DB information and just add it to a String? This means making the asynchronous call synchronous.
Making a synchronous call wouldn't affect the user experience, as the code isn't doing anything else anyway and it just creates the String and is (maybe) waiting for DB input.
Thanks for any help.
So you could do 2 things:
Like the person who commented says you could use a callback:
function askDatabaseForInput(callback) {
var params = {
TableName: "MyTable",
OtherValues: "..."
};
docClient.query(params, function(err, data) {
if (err) {
callback(err, null)
} else {
var item = data.Items[0];
callback(null, item);
}
});
}
or you could use promises:
function askDatabaseForInput() {
var params = {
TableName: "MyTable",
OtherValues: "..."
};
return new Promise(function (resolve, reject) {
docClient.query(params, function(err, data) {
if (err) {
reject(err)
} else {
var item = data.Items[0];
resolve(item);
}
});
});
}
you can then either put a function in where you call askDatabaseForInput or do askDatabaseForInput.then(....).
In the function or the .then you would add what you retrieved from the database to the variable toSay
hope this helps
My question is based on W3C Web SQL Database. I am trying to populate SQL with the data I am getting from API. Here is my code
var db = window.openDatabase("Database", "1.0", "Cordova Demo", 200000);
for(i=0;i<data.responseMsg.length;i++){
var assignid_check = data.responseMsg[i].assignId;//------->3
db.transaction(queryDB, errorCB);//------->4
function errorCB(tx, err)
{
alert("Error processing SQL: "+err);
}
function queryDB(tx)
{
tx.executeSql('SELECT * FROM table_assign WHERE assign_id='+assignid_check, [], querySuccess, errorCB);
}
function querySuccess(tx, results) {
var len = results.rows.length;
if(len>0) //if exist jump to next array
{
alert("exist");
}
else //if not exist insert
{
tx.executeSql('INSERT INTO table_assign (audit_id,assign_id) VALUES (1,'+assignid_check+')');
alert("not exist");
}
}
}
Here problem is in line number 3 after getting "assignid_check" value I am calling transaction method. After transaction method I just want to call callback function i.e queryDB(tx) and if there is any error I want errorCB(tx,results) to get called. But soon after line 3 gets executed line 4 will execute and then again in for loop I value will get incremented and then it will execute line 3 and then 4. Only for the last value i.e only for last I value transaction will call queryDB(tx) callback function.
I am doing this for a phonegap application. Can someone help me on this?
I want to run a transaction inside a for loop. My code is :-
for(var i=0;i<len;i++){
// some code
alert('before transaction');
var db = window.openDatabase("Database", "1.0", "Pin Point", 200000);
db.transaction(fetchSubList, errorLists);
alert('after transaction');
}
function fetchSubList(tx) {
tx.executeSql('some QUERY', [], fetchSubListSuccess, errorLists);
}
function fetchSubListSuccess(tx, results) {
alert("fetchSubListSuccess()...");
// some code
}
But the problem is that after the alert (before transaction) i directly get alert (after transaction) and only when the for loop ends then the transaction start....
I followed the this link to solve but still i'm not able to figure it out....
It is not possible to do it... We need to refactor the code so that code looks like this :-
db.transaction(function(tx)
{
// some value
var loop_limit = 10
for(var i=0; i<loop_limit;i++ ){ // ITERATE HERE
tx.executeSql(Statement, [],Sucess, error);
}
});
I used sqlite to populate a DB with some Tables in it.
I made a function at another javascript page that executes the database & selects some values from the table. The function is called at $(document).ready().
Javascript:
//DB Population
function onDeviceReady() {
var db = window.openDatabase("Database", "1.0", "SqliteTrial", 20000);
db.transaction(populateDB, errorCB, successCB);
}
function populateDB(tx) {
tx.executeSql('DROP TABLE IF EXISTS Subjects');
tx.executeSql('CREATE TABLE IF NOT EXISTS Subjects (id unique, subjectname)');
tx.executeSql('INSERT INTO Subjects (id, subjectname) VALUES (1, "Math")');
tx.executeSql('INSERT INTO Subjects (id, subjectname) VALUES (2, "Science")');
}
function GetSubjectsFromDB()
{
console.log("");
tx.executeSql('SELECT * FROM Subjects', [], queryNSuccess, errorCB);
}
function queryNSuccess(tx, results) {
alert("Query Success");
console.log("Returned rows = " + results.rows.length);
if (!results.rowsAffected) {
console.log('No rows affected!');
return false;
}
console.log("Last inserted row ID = " + results.insertId);
}
function errorCB(err) {
alert("Error processing SQL: "+err.code);
}
Is there some problem with this line?
tx.executeSql('SELECT * FROM Subjects', [], queryNSuccess, errorCB);
The queryNSuccess isn't called, neither is the errorCB so I don't know what's wrong.
This is how I call it at another page:
Javascript:
$(document).ready(function () {
DisplayData();
GetSubjectsFromDB(tx);
});
No, it doesn't work like that. tx variable is actually a parameter that will be sent into the specified callback function by db.transaction method. So you're probably want to do this instead:
$(document).ready(function () {
...
db.transaction(GetSubjectsFromDB);
});
... and rewrite this function definition as...
function GetSubjectsFromDB(tx) { ... something to do with tx ... }
But there's another problem actually, as I see it. Your db variable, which stores the connection handle (created by window.openDatabase call) is local to onDeviceReady function - in other words, it's not visible outside of this function.
The easiest way to solve this is to define this variable at the Global context:
var dbh; // the SQLite connection handle
function onDeviceReady() { ... dbh = window.openDatabase ... }
function GetSubjects() { ... dbh.transaction(getSubjectsFromDb) ... }
function getSubjectsFromDb(tx) { ... tx.executeSql(...) ... }
Here's a great presentation describing general usage of WebSQL DB. But I'd also like to add that WebSQL DB API is considered deprecated; it's recommended to use IndexedDB instead. Here's something to read about it.
I am currently working on a project with 3 friends using nodeJs, expressJs, MongoDB, html5,...
Since we're fairly new to these technologies we bumped into some problems.
A big problem that I can't find a solution for is the asynchronous execution of certain code.
I want a for each loop to finish, so that I have an updated online friends list, and than execute the res.render (in which I pass the online friends list), because currently it does the res.render before it finishes the loop.
Code:
function onlineFriends(req, res) {
var onlinefriends = new Array();
onlinefriends.push("mark");
FriendList.findOne({
owner: req.session.username
}, function (err, friendlist) {
friendlist.friends.forEach(function (friend) { // here forEach starts
OnlineUser.findOne({
userName: friend
}, function (err, onlineFriend) {
if (onlineFriend != null) {
onlinefriends.push(onlineFriend.userName);
console.log("a loop");
}
});
});
console.log("online friends: " + onlinefriends);
console.log("redirecting");
res.render('index', { // this is still inside the forEach function
friendlist: friendlist.friends,
onlinefriendlist: onlinefriends,
username: req.session.username
});// and here it ends
});
}
output will be as follows:
online friends: mark
redirecting
a loop
a loop
a loop
a loop
a loop
a loop
a loop
As discussed here ( JavaScript, Node.js: is Array.forEach asynchronous? ) , the answer is that the for-each is blocking, but in my example it seems to be non-blocking because it executes the res.render before it has finished looping?
How can I make sure that the for each is finished so I have an up to date onlinefriends list (and friendlist) which I can than pass to the res.render instead of the res.render happening way before the for -each loop finishes (which gives me an incorrect list of online users) ?
Thanks very much!
The following console log:
console.log("a loop");
is inside a callback
I believe that the callback of the function OnlineUser.findOne() is called asynchronously, that is why the code will log "a loop" after the redirect log
You should put the redirection after all the loop callbacks have been executed
Something like:
var count = 0;
friendlist.friends.forEach(function (friend) { // here forEach starts
OnlineUser.findOne({
userName: friend
}, function (err, onlineFriend) {
count++;
if (onlineFriend != null) {
onlinefriends.push(onlineFriend.userName);
console.log("a loop");
}
if(count == friendlist.friends.length) { // check if all callbacks have been called
redirect();
}
});
});
function redirect() {
console.log("online friends: " + onlinefriends);
console.log("redirecting");
res.render('index', { // this is still inside the forEach function
friendlist: friendlist.friends,
onlinefriendlist: onlinefriends,
username: req.session.username
});// and here it ends
}
I was able to solve something similar by adding the async package to my project and changing forEach() to async.each(). The advantage is that this provides a standard way to do synchronization for other parts of the application.
Something like this for your project:
function onlineFriends(req, res) {
var onlinefriends = new Array();
onlinefriends.push("mark");
FriendList.findOne({owner: req.session.username}, function (err, friendlist) {
async.each(friendlist.friends, function(friend, callback) {
OnlineUser.findOne({userName: friend}, function (err, onlineFriend) {
if (onlineFriend != null) {
onlinefriends.push(onlineFriend.userName);
console.log("a loop");
}
callback();
});
}, function(err) {
console.log("online friends: " + onlinefriends);
console.log("redirecting");
res.render('index', { // this is still inside the forEach function
friendlist: friendlist.friends,
onlinefriendlist: onlinefriends,
username: req.session.username
});
});
});
}
Running your code through jsbeautifier indents it properly and shows you why that happens:
function onlineFriends(req, res) {
var onlinefriends = new Array();
onlinefriends.push("mark");
FriendList.findOne({
owner: req.session.username
}, function (err, friendlist) {
friendlist.friends.forEach(function (friend) { // here forEach starts
console.log("vriend: " + friend);
OnlineUser.findOne({
userName: friend
}, function (err, onlineFriend) {
if (onlineFriend != null) {
onlinefriends.push(onlineFriend.userName);
console.log("online friends: " + onlinefriends);
}
});
console.log("nu door verwijzen");
res.render('index', { // this is still inside the forEach function
friendlist: friendlist.friends,
onlinefriendlist: onlinefriends,
username: req.session.username
});
}); // and here it ends
});
So... always indent your code properly and you won't have issues like this. Some editors such as Vim can indent your whole file with a single shortcut (gg=G in vim).
However, OnlineUser.findOne() is most likely asynchronous. so even if you move the call to the correct location it won't work. See ShadowCloud's answer on how to solve this.