I have a custom executeSql statement where I pass an array of queries and an array of parameters to make it run faster in a single transaction. When it executes, I'm able to see the rowsAffected per executeSql is populating correctly (I get some "0 records affected", also some "1 records affected").
After I output that for my own confirmation, I want to keep track of how many records were updated in total. So I just add a counter, then output that value once the loop is done. But the end result is showing me 0 records affected everytime.
What's wrong here?
ExecuteQueryWhereQueryAndParamsBothArrays: function (queryArray, paramsArray, success, fail) {
var hasError = false;
var counter = 0;
$rootScope.db.transaction(function (tx) {
for (var i = 0; i < paramsArray.length; i++) {
var query = queryArray[i];
var params = paramsArray[i];
tx.executeSql(query, params, function (tx, results) {
window.logger.logIt("results.rowsAffected: " + results.rowsAffected); // this is showing the correct results
counter += results.rowsAffected;
}, function() {
hasError = true;
onError(tx, r);
});
}
});
if (hasError) {
fail();
} else {
window.logger.logIt("successCounter: " + counter); // this always displays 0 (records)
success(counter);
}
},
You can try to add the following code to the success handler of the db.transaction and check the value of counter.
var counter = 0;
$rootScope.db.transaction(function (tx) {
...... // Your code.
},errorDB,succesDB)
function successDB(){
window.logger.logIt("successCounter: " + counter);
console.log("successCounter: " + counter); // Your counter variable should contain the total
}
function errorDB(err){
console.log("Error processing SQL:Message:" + err.message + "CODE:" + err.code);
}
I ended up implementing the transaction callbacks like #frank suggested, but my counters were still 0. Though the results.rowsAffected was always accurate, for some reason the counter variable was never accessible outside the for loop. I ended up implementing global variables ($rootScope.{variablename} for you angular people) and incremented that everytime from the results.rowsAffected and that worked.
var inc = function(incCounter) {
$rootScope.counter = $rootScope.counter + incCounter;
};
ExecuteQueryWhereQueryAndParamsBothArrays: function (queryArray, paramsArray, success, fail) {
var hasError = false;
var counter = 0;
$rootScope.db.transaction(function (tx) {
for (var i = 0; i < paramsArray.length; i++) {
var query = queryArray[i];
var params = paramsArray[i];
tx.executeSql(query, params, function (tx, results) {
window.logger.logIt("results.rowsAffected: " + results.rowsAffected); // this is showing the correct results
inc(results.rowsAffected);
}, function(tx, r) {
hasError = true;
onError(tx, r);
});
}
}, function () {
fail();
}, function () {
success();
});
},
Related
Simply my code looks like this:
var thevariable = 0;
For(){
//somecode using thevariable
$.getJSON('',{},function(e){
//success and i want to set the returned value from php to my variable to use it in the forloop
thevariable = e.result;
});
}
my main problem that the variable value stays "0", during the whole For loop, while i only want it to be "0" at the first loop, then it takes the result returned from PHP to use it on for loop.
here it my real code if you need to take a look:
var orderinvoice = 0;
for(var i=0; i<table.rows.length; i++){
var ordername = table.rows[i].cells[5].innerText;
var orderqty = ((table.rows[i].cells[1].innerText).replace(/\,/g,'')).replace(/Qty /g,'');
var orderprice = (table.rows[i].cells[2].innerText).replace(/\$/g,'');
var ordertype = table.rows[i].cells[3].innerText;
var orderlink = table.rows[i].cells[4].innerText;
$.getJSON('orderprocess.php', {'invoice': orderinvoice, 'pay_email': email, 'ord_name': ordername, 'ord_qty': orderqty, 'ord_price': orderprice, 'ord_type': ordertype, 'ord_link': orderlink}, function(e) {
console.log();
document.getElementById("result").innerText= document.getElementById("result").innerText + "Order #"+e.result+" Created Successfully ";
document.getElementById("invoker").innerText = ""+e.invoice;
orderinvoice = e.invoice;
if(i+1 == table.rows.length){
document.getElementById("result").innerText= document.getElementById("result").innerText + "With invoice #" + e.invoice;
}
});
in a loop block, before one ajax complete other one will be run and this's javascript natural treatment. For your case you can call a function at the end of success event. Do something like this:
var i = 0;
doSt();
function doSt() {
var orderinvoice = 0;
var ordername = table.rows[i].cells[5].innerText;
var orderqty = ((table.rows[i].cells[1].innerText).replace(/\,/g, '')).replace(/Qty /g, '');
var orderprice = (table.rows[i].cells[2].innerText).replace(/\$/g, '');
var ordertype = table.rows[i].cells[3].innerText;
var orderlink = table.rows[i].cells[4].innerText;
$.getJSON('orderprocess.php', { 'invoice': orderinvoice, 'pay_email': email, 'ord_name': ordername, 'ord_qty': orderqty, 'ord_price': orderprice, 'ord_type': ordertype, 'ord_link': orderlink }, function(e) {
console.log();
document.getElementById("result").innerText = document.getElementById("result").innerText + "Order #" + e.result + " Created Successfully ";
document.getElementById("invoker").innerText = "" + e.invoice;
orderinvoice = e.invoice;
if (i + 1 == table.rows.length) {
document.getElementById("result").innerText = document.getElementById("result").innerText + "With invoice #" + e.invoice;
}
i++;
if (i < table.rows.length) doSt();
});
}
I think you need a recursive function that always deals with the first element in your rows array and then splices it off and calls itself. For example, something like this:
function getStuff(rows, results) {
if (rows.length > 0) {
var ordername = rows[0].cells[5].innerText;
$.getJSON('orderprocess.php', { 'ord_name': ordername }, function (e) {
// do some stuff
results.push('aggregate some things here?');
rows.splice(0, 1);
return getStuff(rows, results);
});
} else {
return results;
}
}
When the array is spent, results will be returned with whatever aggregate you wanted at the end of the cycle. Then, you can do as you please with the results. I think you can also manipulate the DOM inside the function as you see fit if that makes more sense. Hope this helps.
I'm trying to use Phonegap function executeSql to fetch a SQL Query with a select statement , i'm just want to return a query results from my OOP function
i read this question
Return executeSQL function
but i can't undrestand it well so i can't apply it on my code
function Invoice() {
this.read = function(){
var stmnt = "SELECT * FROM invoices ORDER BY ID DESC";
function callback(callback_fun){
var myrows = [];
db.transaction(function(tx){
tx.executeSql(stmnt, [], function(tx,result){
for(var i = 0; i < result.rows.length; i++){
var row = result.rows.item(i);
myrows.push(row);
}
//console.log(myrows);
callback_fun(myrows);
}, function(tx,error){
alert('Error: '+error.message);
return;
});
});
}
callback(function(newResult){
return newResult;
});
};
};
var newInvoice = new Invoice();
var myResult = newInvoice.read();
alert(myResult);
You need to set your callback() plus callback_fun() as an return value like this:
function Invoice() {
this.read = function(){
var stmnt = "SELECT * FROM invoices ORDER BY ID DESC";
function callback(callback_fun){
var myrows = [];
db.transaction(function(tx){
tx.executeSql(stmnt, [], function(tx,result){
for(var i = 0; i < result.rows.length; i++){
var row = result.rows.item(i);
myrows.push(row);
}
//console.log(myrows);
return callback_fun(myrows); // <<----- RETURN
}, function(tx,error){
alert('Error: '+error.message);
return;
});
});
}
return callback(function(newResult){ // <<<<<<<<----------- RETURN
return newResult;
});
};
};
What Javascript Callstack looks like:
you are calling Invoice.read(), but read-Function has no return keyword so it doesn't return anything, what leads to undefined
You are calling your callback function. Which also returns nothing.
EDIT2:
Oh ok you are invoking another function db.transaction which currently returns the value to your callback function. So you could probably do that:
function callback(callback_fun){
var myrows = [];
return db.transaction(function(tx){
tx.executeSql(stmnt, [], function(tx,result){
for(var i = 0; i < result.rows.length; i++){
var row = result.rows.item(i);
myrows.push(row);
}
//console.log(myrows);
return callback_fun(myrows); // <<----- RETURN
}, function(tx,error){
alert('Error: '+error.message);
return;
});
});
}
EDIT:
This is your code. Oh Well a little bit simplified: JSBIN
This is my code. JSBIN-Working Example
You need to press Run with JS in the right corner.
I am trying to read a file on parse.com and using a for loop iterate over all the records present in it. On each record, I need to perform 4 operations, each dependent on the other. Can someone please guide how I can do that so that each record is processed in the for loop.
Parse.Cloud.httpRequest({
url: urlValue
}).then(function(fileResponse) {
console.log("processUploadFile:httpRequest:response:" + JSON.stringify(fileResponse.buffer.length));
// console.log("processUploadFile:Text:" + fileResponse.text);
var records = fileResponse.text.split("\r");
for (var i = 0; i < records.length; ++i) {
// console.log("Record:" + i + " detail:" + records[i] + "\n\n");
var record = records[i];
console.log("processUploadFile:adding patient");
Parse.Cloud.run("addPatient", {
record:record
}, {
success: function(objectId) {
console.log("Created objectId:" + JSON.stringify(objectId));
Parse.Cloud.run("addProvider", {
record:record
}, {
success: function(objectId) {
console.log("Created objectId:" + JSON.stringify(objectId));
Parse.Cloud.run("addLocation", {
record:record
}, {
success: function(objectId) {
console.log("objectId:" + JSON.stringify(objectId));
},
error: function(error) {
console.log(JSON.stringify(error));
}
});
},
error: function(error) {
console.log(JSON.stringify(error));
}
});
},
error: function(error) {
console.log(JSON.stringify(error));
}
});
};
}
}
response.success();
});
The right right answer depends on the semantics of those operations, whether they depend on each other in any way. The other part of a right right answer accounts for transaction rate limits and timeouts imposed by parse.com. That also depends on what happens in the cloud operations and how much data is being processed.
But the right answer (as opposed to right right) is to perform operations serially by chaining promises' then(), and to perform groups of operations concurrently (or in arbitrary order) with Parse.Promise.when().
One such ordering would look like this:
var patientQs = [];
var providerQs = [];
var locationQs = [];
var records;
Parse.Cloud.httpRequest({url: urlValue}).then(function(fileResponse) {
console.log("processUploadFile:httpRequest:response:" + JSON.stringify(fileResponse.buffer.length));
records = fileResponse.text.split("\r");
for (var i = 0; i < records.length; ++i) {
// console.log("Record:" + i + " detail:" + records[i] + "\n\n");
var record = records[i];
patientQs.push(Parse.Cloud.run("addPatient", {record:record}));
providerQs.push(Parse.Cloud.run("addProvider", {record:record}));
locationQs.push(Parse.Cloud.run("addLocation", {record:record}));
}
return Parse.Promise.when(patientQs);
}).then(function() {
// since the result of addPatient is an objectId, arguments
// will be the corresponding objectIds for each run
for (var i=0; i<arguments.length; i++) {
console.log(arguments[i] + " is the object id for input record " + JSON.stringify(records[i]));
}
return Parse.Promise.when(providerQs);
}).then(function() {
return Parse.Promise.when(locationQs);
}).then(function() {
response.success();
}, function(error) {
response.error(error);
});
This says, "go thru the http-retrieved records, and first add all of the patients for those records, then add all of the providers, and so on".
Or, you could group the operations by input record, like this:
Parse.Cloud.httpRequest({url: urlValue}).then(function(fileResponse) {
console.log("processUploadFile:httpRequest:response:" + JSON.stringify(fileResponse.buffer.length));
var records = fileResponse.text.split("\r");
var recordQs = [];
for (var i = 0; i < records.length; ++i) {
// console.log("Record:" + i + " detail:" + records[i] + "\n\n");
var record = records[i];
recordQs.push(processARecord(record));
}
return Parse.Promise.when(recordQs);
}).then(function() {
response.success(arguments);
}, function(error) {
response.error(error);
});
function processARecord(record) {
var result = {};
return Parse.Cloud.run("addPatient", {record:record}).then(function(objectId) {
console.log(objectId + " is the object id for input record " + JSON.stringify(record));
result.patientId = objectId;
return Parse.Cloud.run("addProvider", {record:record});
}).then(function (providerId) {
result.providerId = providerId;
return Parse.Cloud.run("addLocation", {record:record});
}).then(function(locationId) {
result.locationId = locationId;
return result;
});
}
I have code with following structure (pseudocode):
Page init (app starts):
a] getDateRange -> Get array of date range (for example array with 10 items)
b] getResultFromDatabaseForDateRange -> Assync task which return JSON object after each call
I would like to call method b in for each cycle and each returned JSON Array object store into class global variable. After receiving the all responses from b i would like to set whole filled object into scope.
Could somebody give me the example how can I do it in Angular right way?
Thanks for any advice.
I tried to do this by this way but it doesn't works (code is simplified because of length):
$scope.getDateRange = function(direction) {
console.log('Trying to set date range for ' +direction);
switch(direction) {
case 'today':
console.log("Trying to set date range for this week - "+countOfWeeksInPast+" weeks");
dateTo = moment().day(7).format('YYYY-MM-DD');
dateFrom = moment().day(1).format('YYYY-MM-DD');
console.log(dateTo);
console.log(dateFrom);
// CREATE THIS WEEK - 10 WEEKS RANGE IN PAST
for (var i = 0; i < countOfWeeksInPast; i++) {
var row = {};
if(i==0) {
row.ID = i;
row.DATE_TO = dateTo;
row.DATE_FROM = dateFrom;
dateRanges.push(row);
} else {
row.ID = i;
row.DATE_TO = dateTo = moment(dateTo).subtract(7, 'days').format('YYYY-MM-DD');
row.DATE_FROM = dateFrom = moment(dateFrom).subtract(7, 'days').format('YYYY-MM-DD');
dateRanges.push(row);
}
}
$scope.getResultFromDatabaseForDateRange('create_new').then(function(result){
// THIS GIVES THE VALUE:
//alert("Result is" + JSON.stringify(result));
console.log("Returned Result is: " + JSON.stringify(result));
//return result;
}, function(e){
$ionicLoading.show({
template: $translate.instant('ERROR_DATABASE'),
duration:1000
});
});
}
};
$scope.getResultFromDatabaseForDateRange = function(listAction) {
console.log('trying to get data for selected date ranges');
// SHOW LOADING MESSAGE
$ionicLoading.show({
template: 'Loading data'
});
var deferred = $q.defer();
// INSTANTIATE DB CONNECTION
db = window.sqlitePlugin.openDatabase({name:"callplanner"});
var ic=0;
for(ic; ic < dateRanges.length; ic++) {
var sqlQuery =
"SELECT '"+dateRanges[ic].DATE_FROM+"' as DATE_FROM, "+
" '"+dateRanges[ic].DATE_TO+"' as DATE_TO, "+
" COUNT(*) AS DIALS_CNT, "+
" SUM(CASE WHEN dc.call_result = '"+CALL_RESULT_STATE_APPT+"' THEN 1 ELSE 0 END) AS '"+APPT_CNT+"', "+
" SUM(CASE WHEN dc.call_result = '"+CALL_RESULT_STATE_CONV_NO_APPT+"' THEN 1 ELSE 0 END) AS '"+CONVERS_CNT+"' , "+
" SUM(CASE WHEN dc.call_result = '"+CALL_RESULT_STATE_CANNOT_REACH+"' THEN 1 ELSE 0 END) AS '"+CANNOT_REACH_CNT+"' "+
" FROM "+DIALED_CALLS_TABLE+" dc "+
" WHERE dc.date BETWEEN '"+dateRanges[ic].DATE_FROM+"' AND '"+dateRanges[ic].DATE_TO+"';";
console.log(sqlQuery);
db.transaction(function(tx) {
// init empty array for results
tx.executeSql(sqlQuery, [], function(tx,results){
for (var i=0; i < results.rows.length; i++){
row = results.rows.item(i);
//Udpate date for writeout
//row.DATE = moment(row.DATE).format('ddd DD.M');
//row.SUCCES_RATE = DialsCompute.computeSuccessRateDaily(row);
listData.push(row);
console.log("row is " + JSON.stringify(row));
}
console.log(JSON.stringify(listData));
});
},function (e) {
console.log("ERROR: " + e.message);
deferred.reject(e);
});
}
deferred.resolve(row);
$ionicLoading.hide();
return deferred.promise;
};
You need to count the number of rows processed, and when it is equal to number of rows needs to be read, you should resolve your promise. So in your case:
var numberOfProcessed = 0;
for(ic; ic < dateRanges.length; ic++) {
var sqlQuery ="..."
db.transaction(function(tx) {
// init empty array for results
tx.executeSql(sqlQuery, [], function(tx,results){
//process your result from sql
numberOfProcessed++;
if(numberOfProcessed == dateRanges.length){
deferred.resolve(resultObject); // resolve your promise when you are sure you handled everything
}
console.log(JSON.stringify(listData));
});
},function (e) {
console.log("ERROR: " + e.message);
deferred.reject(e);
});
}
And btw getResultFromDatabaseForDateRange needs to be part of a service, business logic, or connection to backend services etc. should not be part of controllers.
For my news ticker directive I have the following created:
//method call
fetchData($scope.verbs).then(function (messageModel) {
$scope.messageModel = messageModel;
$scope.toggleTimer();
}, function (data) {
$element.css("display", "none");
$log.error("error: " + data.message);
});
//helper functions
function fetchData(verbs) {
return checkRequiredVerbs(verbs)
.then(getDataFromSource)
.then(prepareMessages);
};
function checkRequiredVerbs(verbs) {
var deferred = $q.defer(),
arrRequiredVerbs = configManager.baseUrl.split(/\/:/g);
if (angular.isObject(verbs)) {
for (var i = 1; i < arrRequiredVerbs.length; i++) {
if (!verbs.hasOwnProperty(arrRequiredVerbs[i])) {
deferred.reject({message: "Verb '" + arrRequiredVerbs[i] + "' is unknown"});
break;
}
}
deferred.resolve(verbs);
} else {
deferred.reject({message: "Wrong verb syntax!"});
}
return deferred.promise;
};
function getDataFromSource(verbs) {
return messageLoaderFactory.query(verbs).$promise;
};
function prepareMessages(messageModel) {
var deferred = $q.defer(),
tmpMessageModel = [];
for (var i = 0; i < messageModel.length; i++) {
messageModel[i].visible = (i === 0);
}
deferred.resolve(messageModel);
return deferred.promise;
};
}}
In my case before fetching the data from service I have to check the input params. At first the function fetchData will be called. Inside this function thecheckRequiredVerbs function will be called and returns an Promise. This promise will be transfer to the next function getDataFromSource and returns an another Promise. Lastly the function prepareMessages will be called. When something breaks all actions stop and the news ticker will be hidden.
The full project can you find here
I have the following condition where syncAnalytics is called first. Inside this function, there is another function, retreiveAnalyticsData, written to retrieve the locally stored data.
But before the value is returned from retreiveAnalyticsData, the rest of function syncAnalytics gets executed.
DemoModule.factory('ApplicationAnalytics', function ($http, $location, DBO) {
return {
syncAnalytics: function () {
console.log("syncAnalytics called");// Getting displayed 1st
// Retrieve analytics data available data from db
var inputData = this.retreiveAnalyticsData();
console.log("Input Data : " + JSON.stringify(inputData)); // Getting displayed 4th
},
retreiveAnalyticsData: function () {
console.log('retreiveAnalyticsData called');// Getting displayed 2nd
// Select all rows from app_analytics table in db
var selectQuery = "SELECT * FROM app_analytics";
var analyticsData = [];
DBO.execQry(selectQuery, function (results) {
var len = results.rows.length,
i;
for (i = 0; i < len; i++) {
analyticsData.push(results.rows.item(i).text);
}
console.log(analyticsData); //Getting displayed 5th
return analyticsData;
});
console.log('retreiveAnalyticsData ended');// Getting displayed 3rd
}
};
});
So basically :
var inputData = this.retreiveAnalyticsData(); //This should be executed before the below line.
console.log("Input Data : " + JSON.stringify(inputData)); // Getting displayed 4th
Any insight will be greatly appreciated.
Note : I am using AngularJS
DBO.execQry is an asynchronous function. You may see this because of the callback pattern - e.g. the second paramter of execQry is a function that is called if execQry is ready retrieving the data. I guess what you see is, that console.log('retreiveAnalyticsData ended'); is printed out before console.log(analyticsData);
How to handle this?
1) The oldschool way would be using a callback function:
syncAnalytics: function () {
this.retreiveAnalyticsData(function(inputData){
console.log("Input Data : " + JSON.stringify(inputData));
});
},
retreiveAnalyticsData: function (callback) {
var selectQuery = "SELECT * FROM app_analytics";
var analyticsData = [];
DBO.execQry(selectQuery, function (results) {
var len = results.rows.length,
for (var i = 0; i < len; i++) {
analyticsData.push(results.rows.item(i).text);
}
callback(analyticsData);
});
}
But this way has a lot of disadvantages. What if you would like to handle erros or need to to make multiple asynchronous calls or sync them together? So we came to the promise pattern.
2) The Promise Pattern by $q
syncAnalytics: function () {
this.retreiveAnalyticsData().then(function(inputData){
console.log("Input Data : " + JSON.stringify(inputData));
});
},
retreiveAnalyticsData: function () {
var selectQuery = "SELECT * FROM app_analytics";
var analyticsData = [];
var deferred = $q.defer();
DBO.execQry(selectQuery, function (results) {
var len = results.rows.length,
for (var i = 0; i < len; i++) {
analyticsData.push(results.rows.item(i).text);
}
deferred.resolve(analyticsData);
});
return deferred.promise;
}