This question already has answers here:
Wait until all jQuery Ajax requests are done?
(22 answers)
Closed 7 years ago.
I m working on phonegap product using jquery and jquery mobile, the scenario is, when user is logging in for first time, we sync all the data and after done we forward user to another view. The items are retrieved in json format from server. Here are the portion of my code. I have called the webservice and the response is returned as JSON objects in response variable.
$(response.data.module.registration).each(function(k,v){
//insert into app's sqlite database
});
$(response.data.module.attendance).each(function(k,v){
//insert into app's sqlite database
});
$(response.data.items.grocery).each(function(k,v){
//insert into app's sqlite database
});
//and so on. There could be many rows in each loop above.
My question is how to know all rows has been inserted from the loop so that I can forward user to restricted area.
more precisely, how to know all JSON object has been iterated successfully?
What i tried is put counter in each loop and check if sum of all the counters is equal to total items we are iterating. But this didn't work the sum of all the counters are readily available before all items are inserted.
EDIT
Here is my helper function that inserts record into sqlite db. This didn't work, user was logged in before all data are inserted. Can you tell me where I went wrong
var sqlhelper = {
insertJSONData:function(tablename,data,callback){
var dfrd = $.Deferred();
var fields=sqlhelper.separateFieldData(data,"field");
var dataval=sqlhelper.separateFieldData(data,"value");
sqlhelper.db.transaction(function(tx) {
var sqlquery='INSERT INTO '+tablename+' ('+fields+') values('+dataval+')';
console.log(sqlquery);
tx.executeSql(sqlquery,[],function(tx,result){
dfrd.resolve(result);
console.log('Success');
},sqlhelper.errorCB);
if(callback!=undefined){
callback();
}
});
return dfrd.promise();
}
}
And here is the code that fetches server response
function defObj1()
{
if(typeof response.data.module.registration!="undefined"){
$(response.data.module.registration).each(function(i,e){
var data = {
'reg_id': e.reg_id,
'reg_name': e.reg_name,
'reg_desc': e.reg_desc,
'reg_status': e.reg_status
};
sqlhelper.insertJSONData('tbl_registration',data);
}); // end of each loop
}
}
function defObj2()
{
if(typeof response.data.items.grocery!="undefined"){
$(response.data.items.grocery).each(function(i,e){
var data = {
'grocery_id': e.grocery_id,
'item_name': e.item_name,
'item_qty': e.item_qty,
'item_unit_price': e.item_unit_price
};
sqlhelper.insertJSONData('tbl_grocery',data);
}); // end of each loop
}
}
$.when(defObj1() ,defObj2()).done(function(a1,a2){
//sync complete so login user
doLogin();
})
Thanks
try this. (Edited)
var isValid = true, i = 0, sum, callback = function () {
//if all inserting is successfully it is called
};
...
$(response.data.module.registration).each(function (k, v) {
//insert into app's sqlite database
var data = {
'reg_id': e.reg_id,
'reg_name': e.reg_name,
'reg_desc': e.reg_desc,
'reg_status': e.reg_status
};
sqlhelper.insertJSONData('tbl_registration', data, function (data) {
if (!data) {
isValid = false;
sum++;
}
i++;//onSuccess function
checkLast(i);//call this lastly method or each
}, function () {
i++;//onError function
});
});
...
//other codes is identical logic
...
function checkLast(i) {
if (i == sum) {
callback();
}
}
...
I have added successCallbak and errorCallback to your sqlhelper
var sqlhelper = {
insertJSONData: function (tablename, data, successCallbak, errorCallback) {
var dfrd = $.Deferred();
var fields = sqlhelper.separateFieldData(data, "field");
var dataval = sqlhelper.separateFieldData(data, "value");
sqlhelper.db.transaction(function (tx) {
var sqlquery = 'INSERT INTO ' + tablename + ' (' + fields + ') values(' + dataval + ')';
console.log(sqlquery);
tx.executeSql(sqlquery, [], function (tx, result) {
dfrd.resolve(result);
if (successCallback) {
successCallback(result);
}
console.log('Success');
}, sqlhelper.errorCB);
if (errorCallback) {
errorCallback();
}
});
return dfrd.promise();
}
}
Related
I'm having a little issue with my batch request, when the odata model is submitted and triggered, the that.readAndUpdateSercicePeriodPlans(oService).then(function(oSerciceO) in the callback is triggered before the batch return the result
As you can see using my debugger, the call back function is triggered :
but the network didn't return the result yet :
Below is the code, what I am doing wrong? :
odataMod = this.getModel("Service");
odataMod.setUseBatch(true);
var aDeffGroup = odataMod.getDeferredGroups();
//add your deffered group
aDeffGroup.push("deletionGroup");
_.forEach(periodPlanArr, function(periodPlanToDel) {
odataMod.remove('/ProjectTaskServicePeriodPlanCollection(\'' + periodPlanToDel.ObjectID + '\')/', {
groupId: "deletionGroup"
});
});
oGlobalBusyDialog.setText("Deleting Period Plans in progress");
oGlobalBusyDialog.setTitle("Updating data Model");
oGlobalBusyDialog.open();
//This trigger the batch request
odataMod.submitChanges({
// deffered group id
groupId: "deletionGroup",
success: function(oData) {
sap.m.MessageToast.show(oData.toString());
var aErrorData = sap.ui.getCore().getMessageManager().getMessageModel();
var msg = aErrorData.getData();
var oService = _.find(oNoneAssignedTaskModelData, function(oSewrv) {
return oSewrv.ObjectID === uniqueByID[0].ParentObjectID;
});
oGlobalBusyDialog.setText("Updating oModel in progress");
oGlobalBusyDialog.setTitle("Updating data Model");
// ISSUE : This below function is invoked before even the batch request is complete , why ?!
that.readAndUpdateSercicePeriodPlans(oService).then(function(oSerciceO) {
oGlobalBusyDialog.close();
//Logic USER STORY 3423: Get Internal Indicator PeriodPlan and update the employee nternal Indicator PeriodPlan
},
error: function(oError) {
var oResponse = JSON.parse(oError.response.body);
sap.m.MessageToast.show("Fehler: " + oResponse.error.message.value);
}
});
Your Chrome Filter icon will only be red if there is some value in the filter.:)
After debugging all night and drinkind redbull I've finally found the issue :
var aDeffGroup = odataMod.getDeferredGroups();
aDeffGroup.push("deletionGroup");
//I must set the deffered groups after pushing the ID or else it won't be added
this.setDeferredGroups(aDeffGroup);
I'd recommand to avoid adding same group twice - I had some issues because of that.
odataMod = this.getModel("Service");
odataMod.setUseBatch(true);
//var aDeffGroup = odataMod.getDeferredGroups();
//aDeffGroup.push("deletionGroup");
that.setModelDeferredGroup(odataMod, "deletionGroup");
// the function
setModelDeferredGroup: function (oModel, sGroup) {
if (oModel && sGroup) {
var aDeferredGroups = oModel.getDeferredGroups();
if (aDeferredGroups.indexOf(sGroup) < 0) {
aDeferredGroups.push(sGroup);
oModel.setDeferredGroups(aDeferredGroups);
}
}
}
Parse-server doesn't support groupBy for queries. So instead of adapting code to work with the duplicate entries i've decided to create a Job to clean the data.
I've created a cloud function using underscore but the results are not good. It's deleting non-duplicate entries too.
I want to remove a entry if another entry exists with the same post_id and user_id
Parse.Cloud.job("removeDuplicateItems", function(request, status) {
var _ = require("underscore");
var hashTable = {};
function hashKeyForTestItem(testItem) {
var fields = ["user_id", "post_id"];
var hashKey = "";
_.each(fields, function (field) {
hashKey += testItem.get(field) + "/" ;
});
return hashKey;
}
var testItemsQuery = new Parse.Query("Post_shares");
testItemsQuery.each(function (testItem) {
var key = hashKeyForTestItem(testItem);
if (key in hashTable) { // this item was seen before, so destroy this
return testItem.destroy();
} else { // it is not in the hashTable, so keep it
hashTable[key] = 1;
}
}).then(function() {
status.success("removal completed successfully.");
}, function(error) {
status.error("Uh oh, something went wrong.");
});
});
Is there a better way of doing this?
I'm using Angularjs with the dirPagination plugin to connect with an Web API. This seems to work fine. I added a search function, to do a server side search:
$scope.searchChanged = function () {
if ($scope.searchFor.length == 0) {
$scope.calculatedValue = 'e';
} else {
vm.getData(vm.pageno, vm.getSearch($scope.searchFor));
}
}
vm.getSearch = function (query) {
if (query == undefined) {
query = 'tech';
} else {
query = query;
}
return query;
}
See Plnkr for the full code
If I start searching (e.g. sales) the API returns results and the paging is correct, the get request is:
/api/students/?category=sales&begin=1&pageSize=10
But if you want to go to another page number, the get request to the server is:
/api/students/?category=tech&begin=2&pageSize=10
How can the view remember the query 'sales', so that the paging and results are correct?
You are making a common mistake here: You don't need to pass in variable from the view if you are already using a scope variable.
Changing to this would be much less error prone
// change this to var getSearch or function getSearch if you don't need it on the view anymore
vm.getSearch = function () {
var query = vm.searchFor;
// you should only use vm, change ng-model to data.searchFor
if (query == undefined) {
query = 'tech';
}
return query;
}
vm.getData = function () {
vm.users = [];
$http.get("/api/students/?category=" + vm.getSearch() + "&begin=" + vm.pageno + "&pageSize=" + vm.itemsPerPage).success(function (response) {
vm.users = response.data;
vm.total_count = response.total_count;
});
};
Your request id good, you need to optimize the sql query so you can get the right results. it should look something like this:
#begin INT = 0,
#pageSize INT = 10
SELECT *
FROM [TableName]
ORDER BY id
OFFSET (#pageSize * #begin )
ROWS FETCH NEXT #pageSize ROWS ONLY;
I'm making a quiz. I'm loading in the questions from a json file. The models have a 'use: ' attribute. I'm filtering the collection by use and plucking the IDs, then I'm getting a random ID for the question and setting use to false once it has been answered. In this way, a user will go through all the questions in a random order with no repeats. I'd like to keep track of the used questions for each user and when they have done them all reset the question bank.
I'm using backbone.dualstorage and it worked once and then I tried to reset the collection by going through each model and destroying it. Now I can't seem to repopulate the local collection with models because the remote models have the same IDs as the destroyed model IDs.
How can I re-add all the models in the remote collection to localStorage again?
//Question collection
var PlayCollection = Backbone.Collection.extend({
model: PlayModel,
url: "https://raw.githubusercontent.com/robertocarroll/barjeel-app/master/app/data/questions.json",
//when I set remote it works without local at all
//remote: function () {return true;}
});
//define new question collection
var newCollection = new PlayCollection();
// load data
newCollection.fetch({
success: function (newCollection, response, options) {
console.log("fetch questions success");
//this shows all the question IDs which I destroyed
console.log(newCollection.destroyedModelIds());
//this is empty
console.log(newCollection.dirtyModels());
}
});
function getQuestion() {
var theQuestions = newCollection.dirtyModels()[0].collection;
//get the IDs of all questions which haven't been used
var questions = theQuestions.chain()
.filter(function (m) {
return m.get('use')
})
.pluck('id')
.value();
console.log(questions);
if (questions.length > 0) {
// get random ID from question ID array
var rand = questions[_.random(questions.length - 1)];
console.log('chosen ID value: ' + rand);
//get a model from a collection, specified by ID
var currentQuestion = theQuestions.get(rand);
console.log(currentQuestion);
//set the status of that question to used
currentQuestion.set("use", false);
}
//if there's not more questions
else {
console.log("No more questions");
//delete models in local
_.chain(newCollection.models).clone().each(function (model) {
console.log('deleting model ' + model.id);
model.destroy();
});
}
}
Here's the fiddle: http://jsfiddle.net/robertocarroll/10xqvk18/10/
Thanks!
Turns out backbone.dualstorage was overkill. I accessed localStorage directly instead:
//model for questions
PlayModel = Backbone.Model.extend({});
//model for which questions to use
PlayGameQuestionCount = Backbone.Model.extend({
defaults: {
"use": true
}
});
//Question collection
var PlayCollection = Backbone.Collection.extend({
model: PlayModel,
url: "https://raw.githubusercontent.com/robertocarroll/barjeel-app/master/app/data/questions.json"
});
//define new question collection
var newCollection = new PlayCollection();
function fetchQuestions() {
// load data
newCollection.fetch({
success: function (newCollection, response, options) {
console.log("fetch questions success");
//add data to local storage
localStorage.setItem('questions', JSON.stringify(newCollection.toJSON()));
}
});
}
function getQuestion() {
var newLocalCollection = new PlayCollection(JSON.parse(localStorage.getItem('questions')));
console.log(newLocalCollection);
//get the IDs of all questions which haven't been used
var questions = newLocalCollection.chain()
.filter(function (m) {
return m.get('use')
})
.pluck('id')
.value();
console.log(questions);
if (questions.length > 0) {
// get random ID from question ID array
var rand = questions[_.random(questions.length - 1)];
console.log('chosen ID value: ' + rand);
//get a model from a collection, specified by ID
var currentQuestion = newLocalCollection.get(rand);
console.log(currentQuestion);
//set the status of that question to used
currentQuestion.set("use", false);
localStorage.setItem('questions', JSON.stringify(newLocalCollection.toJSON()));
}
//if there's not more questions
else {
console.log("No more questions");
//delete models in local
fetchQuestions();
}
}
//function to fire the questions
$(document).ready(function () {
$("#btnSave").click(
function () {
getQuestion();
});
});
Here's the fiddle: http://jsfiddle.net/hcqse7j4/3/
I'm looping over a set of URLs trying to get their HTML, but it only works for the last one in the list. The "NOT WRITING YET" console.log fires for everything in the urls array, as expected, but the console.logs after that only fire for the last one. They are all valid URLs and "res.on('error'....." returns nothing.
Any help is greatly appreciated!
for (var z in urls) {
var getURL = urls[z];
var copyURL = 'copies/'+getURL;
if (copyURL.indexOf('/') >= 0) {
var copyURLArr = copyURL.split('/');
} else {
var copyURLArr = [copyURL];
}
var copyFile = copyURLArr.pop();
var runningDirs = '';
for (var i in copyURLArr) {
if (runningDirs.length > 0) runningDirs += '/';
runningDirs += copyURLArr[i];
if (!require('fs').existsSync(runningDirs)) {
exec('mkdir '+runningDirs);
}
}
console.log('NOT WRITING YET: '+urlPrefix+getURL);
require('http').get(urlPrefix+getURL, function(res) {
console.log(urlPrefix+getURL);
res.on('data', function(data) {
console.log(copyURL);
require('fs').createWriteStream(copyURL, {flags:'a+'}).write(data);
});
});
}
In the line console.log(urlPrefix+getURL); the value will always be for the last url because when the callback is called the values of these variables will be from the last time they got assigned which is the last iteration in the loop.
instead make the request in a separate function like
function doRequest(url, copyURL) {
require('http').get(url, function(res) {
console.log(url);
res.on('data', function(data) {
console.log(copyURL);
require('fs').createWriteStream(copyURL, {flags:'a+'}).write(data);
});
});
}
and then call this function with doRequest(urlPrefix+getURL, copyURL) then check if the problem still exists