ECMAScript Callback method executed after PreSaveAction method - javascript

My requirement is to get value from another list and set it to SharePoint list form and then save the item.
I wrote ECMAScript and PreSaveAction(). However, PreSaveAction() return true is executed before ECMAScript call back method, which is why my field value is not saved when I click Save button. Please note that my call back function has no error and I get correct value in alert. I don't know where I am doing wrong. Below is my code for you reference.
function PreSaveAction()
{
ExecuteOrDelayUntilScriptLoaded(GetTotalAmtAlloted, "sp.js");
return true;**//This line will execute before my callback function onListItemsLoadSuccess or onQueryFailed execute. Because of this, my field value not able to save it.**
}
function GetTotalAmtAlloted()
{
var clientContext = null;
var web = null;
var vID;
clientContext = new SP.ClientContext.get_current();
web = clientContext.get_web();
var list = web.get_lists().getByTitle("Investment");
var camlQuery = new SP.CamlQuery();
var q = "<View><Query><Where><Eq><FieldRef Name='InvestmentSubject' /><Value Type='Text'>abc</Value></Eq></Where></Query></View>";
camlQuery.set_viewXml(q);
listItems = list.getItems(camlQuery);
clientContext.load(listItems);
clientContext.executeQueryAsync(onListItemsLoadSuccess,onQueryFailed);//After this line "return true" in PreSaveAction() will execute and then CallBackMethods will run.
}
function onListItemsLoadSuccess(sender, args)
{
$("input[Title='Total Amount']").val("1000");
}
function onQueryFailed(sender,args)
{
alert("error");
}

You are performing an asynchronous function call but expect it to act synchronous. Instead, you have to wait for your asynchronous function to finish before your PreSaveAction can return true.
Since you're using jQuery, this can easily be achieved using the deferred/promise that jQuery provides.
function PreSaveAction() {
//Get the promise from the function
var promise = GetTotalAmtAlloted();
//This is run when the promise is resolved
promise.done(function(){
return true;
});
//This is run when the promise is rejected
promise.fail(funtion(){
});
}
function GetTotalAmtAlloted() {
var d = $.Deferred();
var clientContext = null;
var web = null;
var vID;
clientContext = new SP.ClientContext.get_current();
web = clientContext.get_web();
var list = web.get_lists().getByTitle("Investment");
var camlQuery = new SP.CamlQuery();
var q = "<View><Query><Where><Eq><FieldRef Name='InvestmentSubject' /><Value Type='Text'>abc</Value></Eq></Where></Query></View>";
camlQuery.set_viewXml(q);
listItems = list.getItems(camlQuery);
//An object to pass to the success and failure handlers
var data = {d: d, list: listItems}
clientContext.load(listItems);
//Execute the query and pass the data with our deferred object
clientContext.executeQueryAsync(Function.createDelegate(data, onListItemsLoadSuccess), Function.createDelegate(data, onQueryFailed));//After this line "return true" in PreSaveAction() will execute and then CallBackMethods will run.
//Return a promise that can either resolve or reject depending on if the async function is a success or failure
return d.promise();
}
function onListItemsLoadSuccess() {
//Access the listItems
var listItems = this.list;
//Do something with the list items
//On success, resolve the promise
this.d.resolve();
}
function onQueryFailed() {
//On failure, reject the promise
this.d.reject("Something went wrong!");
alert("error");
}

Related

wait for arrays to be filled with ajax results

I'm trying to make my page wait untill all data is loaded and put into the right arrays. I load the data using 3 seperate ajax calls but since the loading sometimes takes a little too long, the page continues while having empty arrays, and I need this data before I can do anything on the page.
I've been looking into the jQuery.when function but I can't seem to get it working.
this is 1 of the ajax calls:
function getWerktijden(){
var AllArrays = [];
var dagen = [];
var start = [];
var end = [];
$.ajax({
type: "GET",
url: '#Url.Action("getWerktijden")',
data: {
id: #Model.ID,
dag: d,
maandNr: m,
jaarNr: y,
getJson: true,
},
success: function (result) {
//console.log(result);
for(var v = 0; v < result.length; v++){
var resultItem = result[v];
var ingangsDatum = resultItem.activatieDatum;
var uitgangsDatum = resultItem.stopDatum;
if(ingangsDatum != ""){
ingangsDatum = new Date(changeDateTimeFormat(ingangsDatum));
ingangsDatum = ingangsDatum.toString('yyyy-MM-dd HH:mm:ss');
};
if(uitgangsDatum != ""){
uitgangsDatum = new Date(changeDateTimeFormat(uitgangsDatum));
uitgangsDatum = uitgangsDatum.toString('yyyy-MM-dd HH:mm:ss');
};
dagen.push({
werktijdenID: resultItem.id,
ingang: ingangsDatum,
uitgang: uitgangsDatum,
maandag: resultItem.maandag,
dinsdag: resultItem.dinsdag,
woensdag: resultItem.woensdag,
donderdag: resultItem.donderdag,
vrijdag: resultItem.vrijdag,
zaterdag: resultItem.zaterdag,
zondag: resultItem.zondag
});
start.push({
werktijdenID: resultItem.id,
ma_van: resultItem.ma_van,
di_van: resultItem.di_van,
wo_van: resultItem.wo_van,
do_van: resultItem.do_van,
vr_van: resultItem.vr_van,
za_van: resultItem.za_van,
zo_van: resultItem.zo_van,
})
end.push({
werktijdenID: resultItem.id,
ma_tot: resultItem.ma_tot,
di_tot: resultItem.di_tot,
wo_tot: resultItem.wo_tot,
do_tot: resultItem.do_tot,
vr_tot: resultItem.vr_tot,
za_tot: resultItem.za_tot,
zo_tot: resultItem.zo_tot,
})
}
}
});
AllArrays.push(dagen, start, end);
return AllArrays;
};
Then I call the function on a variable to return those results into that variable and check wether it's done with the jQuery.when function but the page continues no matter if the function has completed.
var allArrays = getWerktijden();
var event = getMeldingen(); //I put this here to show I have more ajax calls
var ziektedatums = getZiektedatums(); //I put this here to show I have more ajax calls
$.when.apply($, allArrays, event, ziektedatums).then(function(){
dagenPerWeek = allArrays[0];
startPerDag = allArrays[1];
endPerDag = allArrays[2];
}).done(function(){
console.log(dagenPerWeek, startPerDag, endPerDag, event, ziektedatums);
});
Can somebody explain to me what I am doing wrong?
Thanks!
Return the $.ajax promise instead of the array from each function
The array will be immediately returned before the ajax is complete as it is asynchronous. Also an array is not a promise so $.when won't wait for it to be populated
It's also easier to use Promise.all() vs $.when() in modern browsers
So it will look something like:
function getWerktijden() {
var AllArrays = [];
var dagen = [];
var start = [];
var end = [];
// return the promise
return $.ajax({ /* config options*/ })
.then(function(result) {
// do the processing into various arrays
// return to be used in next part of promise chain
return AllArrays;
});
}
Promise.all([getWerktijden(), getMeldingen(), getZiektedatums()])
.then(function(results){
// results will be array of whatever is returned from `then()`
// in each function and is in same order as they are called
var getWerktijden_Arrays = results[0],
dagenPerWeek = getWerktijden_Arrays[0]
console.log(dagemPerWeek);
})

Javascript angular parse queries synchronously (in sequence)

I am trying to use Parse in javascript to make two queries synchronously in sequence:
var innerQuery = new Parse.Query(Attendees);
innerQuery.equalTo("user_id",$localStorage.username);
innerQuery.equalTo("status","confirmed")
innerQuery.equalTo("code_verified",false)
var innerQuerytrue = new Parse.Query(Attendees);
innerQuerytrue.equalTo("user_id",$localStorage.username);
innerQuerytrue.equalTo("status","confirmed")
innerQuerytrue.equalTo("code_verified",true)
innerQuery.find({})
innerQuerytrue.find({})
var eventDetails = []
var queryfalse = new Parse.Query(Events)
queryfalse.matchesKeyInQuery("id_event","event_id",innerQuery);
var querytrue = new Parse.Query(Events)
querytrue.matchesKeyInQuery("id_event","event_id",innerQuerytrue);
queryfalse.find().then(function(results){
for (var i in results) {
var object = results[i];
var eventId = object.get("id_event");
var eventname = object.get("event_name");
var datestart = object.get("date_start");
var location = object.get("location");
var eventimagefile = object.get("event_image");
var eventimageurl = eventimagefile.url();
eventDetails.push({'name':eventname,'eventId':eventId, 'location':location, 'datestart':datestart, 'eventphoto':eventimageurl,'verified':false})
}
}).then(function(){
querytrue.find().then(function(results1){
for (var i in results1) {
var object = results1[i];
var eventId = object.get("id_event");
var eventname = object.get("event_name");
var datestart = object.get("date_start");
var location = object.get("location");
var eventimagefile = object.get("event_image");
var eventimageurl = eventimagefile.url();
eventDetails.push({'name':eventname,'eventId':eventId, 'location':location, 'datestart':datestart, 'eventphoto':eventimageurl,'verified':true})
}
})
}).then(function(){
$scope.events = eventDetails;
})
The second query, queryfalse, is not always executed. I am using promises and not sure if it is the right way to use them.
Just add return to pass the promise returned by querytrue.find().then() down the chain so the next then() won't execute until the promise is resolved. This also keeps your code flatter to avoid deeply nested promises.
queryfalse.find().then(function(results){
// code clipped for brevity
}).then(function(){
return querytrue.find().then(function(results1){
// code clipped for brevity
});
}).then(function(){
$scope.events = eventDetails;
});
Here's a working plunker that demonstrates this: https://plnkr.co/edit/SwPIC1K6yEhEgpIlxoEo?p=preview
You have to move the angular result to the 2nd promise.
var innerQuery = new Parse.Query(Attendees);
innerQuery.equalTo("user_id",$localStorage.username);
innerQuery.equalTo("status","confirmed")
innerQuery.equalTo("code_verified",false)
var innerQuerytrue = new Parse.Query(Attendees);
innerQuerytrue.equalTo("user_id",$localStorage.username);
innerQuerytrue.equalTo("status","confirmed")
innerQuerytrue.equalTo("code_verified",true)
innerQuery.find({})
innerQuerytrue.find({})
var eventDetails = []
var queryfalse = new Parse.Query(Events)
queryfalse.matchesKeyInQuery("id_event","event_id",innerQuery);
var querytrue = new Parse.Query(Events)
querytrue.matchesKeyInQuery("id_event","event_id",innerQuerytrue);
queryfalse.find().then(function(results){
for (var i in results) {
var object = results[i];
var eventId = object.get("id_event");
var eventname = object.get("event_name");
var datestart = object.get("date_start");
var location = object.get("location");
var eventimagefile = object.get("event_image");
var eventimageurl = eventimagefile.url();
eventDetails.push({'name':eventname,'eventId':eventId, 'location':location, 'datestart':datestart, 'eventphoto':eventimageurl,'verified':false})
}
}).then(function(){
querytrue.find().then(function(results1){
for (var i in results1) {
var object = results1[i];
var eventId = object.get("id_event");
var eventname = object.get("event_name");
var datestart = object.get("date_start");
var location = object.get("location");
var eventimagefile = object.get("event_image");
var eventimageurl = eventimagefile.url();
eventDetails.push({'name':eventname,'eventId':eventId, 'location':location, 'datestart':datestart, 'eventphoto':eventimageurl,'verified':true})
}
}).then(function(){
$scope.events = eventDetails;
});
})
querytrue.find() -> returns a promise, and that promise eventually returns a list of results that you're interested in.
.then() sets up some action after a promise, and returns another promise immediately. You can also pass a success handler & error handler function
so, when you say
var p = querytrue.find().then(onSuccess, onError)
p is a promise that will become the return value of the success or failure function, whichever one runs. You can chain another promise by adding another .then()
This chained promise gets the return value of the first promise.
If the first promise success function ran, the second chained success one will also run, likewise with error functions.
If your success handler creates another promise (like your question), you can return that promise from the success handler, and any subsequent chained promises will wait for that promise. Sort of adding a link to the chain.
var p = query1.find();
p.then( function(){
... // runs after query1.find()
var q = query2.find().then(...);
return q;
})
.then( function() {
... // runs after query1.find() and query2.find()
});

NodeJs forEach request-promise wait for all promises before returning

Problem is I'm not able to get the promises to return anything. they.. just come empty.
Every answer I see here on SO is telling me to do just this, though for some reason this is not working. I'm at my wits end, pulling hair and smashing keyboards; Can someone pin-point my dumbness?
var q = require('q');
var request = require('request-promise'); // https://www.npmjs.com/package/request-promise
function findSynonym(searchList) {
var defer = q.defer();
var promises = [];
var url = "http://thesaurus.altervista.org/service.php?word=%word%&language=en_US&output=json&key=awesomekeyisawesome";
var wURL;
searchList.forEach(function(word){
wURL = url.replace('%word%',word);
promises.push(request(wURL));
});
q.all(promises).then(function(data){
console.log('after all->', data); // data is empty
defer.resolve();
});
return defer;
}
var search = ['cookie', 'performance', 'danger'];
findSynonym(search).then(function(supposedDataFromAllPromises) { // TypeError: undefined is not a function [then is not a function]
console.log('->',supposedDataFromAllPromises); // this never happens
});
You're returning the Deferred object defer, which does not have a .then method, instead of the Promise object defer.promise.
But anyway, that's the deferred antipattern, there's no need of using deferreds here. Just return the promise that Promise.all gets you:
function findSynonym(searchList) {
var url = "http://thesaurus.altervista.org/service.php?word=%word%&language=en_US&output=json&key=awesomekeyisawesome";
var promises = searchList.map(function(word) {
return request(url.replace('%word%', word));
});
return q.all(promises).then(function(data){
console.log('after all->', data); // data is empty
return undefined; // that's what you were resolve()ing with
});
}
So, turns out I was resolving the promises or something something. returning the q.all() worked pretty well :)
function findSynonym(searchList) {
var promises = [];
var url = "http://thesaurus.altervista.org/service.php?word=%word%&language=en_US&output=json&key=REDACTED";
var wURL;
searchList.forEach(function(word){
wURL = url.replace('%word%',word);
promises.push(request({url:wURL}));
});
return q.all(promises);
}
var search = ['cookie', 'performance', 'danger'];
findSynonym(search)
.then(function(a){
console.log('->',a);
});

sequential deferred promise with ajax call running out of order

If you look at showResults(), you will see it first creates a promise, then runs getSelectedIds() which returns a promise, then it makes the api call with those selected ids, which is an ajax call, then console logs sr df3 finished. the problem im having an extreme hard time figuring out is i think i have some missing return statements somewhere, so the promise is getting resolved too early or something.
Can somebody PLEASE spot where im going wrong here, and tell me what is needed in either the getApi ajax call or the previous, or following event so that "sr df3 finished" DOES NOT happen until after the ajax call has returned its promise.
app.core.getSelectedIds = function(){
var dfd = $.Deferred();
//eliminates having to make an unnecessary ajax call if we already have them
if( $.isEmptyObject(app.core.selectedIds) ){
//gather up selected ids
$('input[type=checkbox]').each(function (key, value) {
//look for checked checkboxes only
if ($(this).is(':checked') && $(this).hasClass('results')) {
//get id from hidden input next to checkbox, set ids into an object for later use
stdIds[key] = $(this).next().attr('value');
}
});
app.core.selectedIds = stdIds;
}
dfd.resolve();
return dfd.promise();
};
app.test.getApi = function(newToken, nextPageNum){
var df1,df2;
if(nextPageNum){ app.test.nextPageNum = nextPageNum; }
df1 = app.test.ajaxCall1();
df2 = df1.then( function(){
console.log('test df2 started');
console.log(app.test.standardsObj); //the ajax calls results after being applied to the methods property
});
df2.done(function(){
console.log('test df2 finished');
});
};
app.ui.showResults = function(newToken){
var df1,df2,df3;
df1 = $.Deferred();
df2 = df1.then(function(){
console.log('sr df2 started');
return app.core.getSelectedIds(); //is a promise
});
df2.done(function(){
console.log(app.core.selectedIds);
console.log('sr df2 finished');
});
df3 = df2.then(function(){
console.log('sr df3 started');
app.test.getApi(newToken);
});
df3.done(function(){
console.log('sr df3 finished');
});
df1.resolve();
};
app.test.ajaxCall1 = function(){
var idArr = [];
//For now set up an array to cast those values to
$.each(app.core.selectedIds, function( key, value ){
idArr[key] = value;
});
return app.core.methodByRoute('ajax_call_1', {ids: idArr}, 'GET')
.done(function(retrievedResults){
app.test.standardsObj = retrievedResults;
})
};
console log output is
sr df2 started
Object { 1="498", 2="501", 3="502", more...}
sr df2 finished
sr df3 started
sr df3 finished
test df2 started
["valid data"]
test df2 finished
app.core.getSelectedIds = function(){
var dfd = $.Deferred();
…
dfd.resolve();
return dfd.promise();
}
Looks like this function doesn't need to return a promise at all if it doesn't do anything asynchronous, and would resolve the deferred immediately anyway?
i think i have some missing return statements somewhere
Yes, exactly. The basic rule is: return a promise from every function that does something asynchronous. In your case, that includes the app.test.getApi function and the df2.then(function(){…}) callback that calls it - if the callback doesn't return anything, df3 is resolved immediately after df2, as it doesn't know what to wait for.
Btw, you don't need that df1 - just start the chain with the first promise that a function returns.
app.core.getSelectedIds = function() {
if ($.isEmptyObject(app.core.selectedIds)) {
$('input[type=checkbox]').each(function (key, value) {
if ($(this).is(':checked') && $(this).hasClass('results')) {
stdIds[key] = $(this).next().attr('value');
}
});
app.core.selectedIds = stdIds;
}
return $.when(app.core.selectedIds); // a promise. just to start the chain.
// maybe this function needs to do
// something async in the future
};
app.test.getApi = function(newToken, nextPageNum) {
if (nextPageNum) {
app.test.nextPageNum = nextPageNum;
}
var df1 = app.test.ajaxCall1();
var df2 = df1.then( function(){
console.log('test df2 started');
console.log(app.test.standardsObj); //the ajax calls results after being applied to the methods property
});
df2.done(function(){
console.log('test df2 finished');
});
return df2;
// ^^^^^^^^^^^
};
app.ui.showResults = function(newToken) {
var df1 =app.core.getSelectedIds(); // is a promise
df1.done(function() {
console.log(app.core.selectedIds);
console.log('sr df2 finished');
});
var df2 = df1.then(function() {
console.log('sr df2 started');
return app.test.getApi(newToken);
// ^^^^^^
});
df2.done(function(){
console.log('sr df2 finished');
});
return df2;
// ^^^^^^^^^^^ just for convenience, if we want to chain something
// after a .showResults() call
};

Wait for openCursor to finish in IndexedDB

I have this code:
html5DB.indexedDB.addSomething = function(foo) {
var db = html5DB.indexedDB.db;
var trans = db.transaction(["something"], "readwrite");
var store = trans.objectStore("something");
var isExisting = IsAlreadyExist(foo);
// How to wait for that instruction to finish?
if (!isExisting){
var request = store.put({
"text": foo,
"timeStamp" : new Date().getTime()
});
}
};
I'm trying to understand how can I wait for the function IsAlreadyExist to finish. This function opens a cursor to iterate in the object store to determinate if a particular value exist.
Actually, there is no error, but I can't reach the code inside the if (!isExisting) because the value of the variable never changes.
This is the code of that second function:
function IsAlreadyExist(foo){
var db = html5DB.indexedDB.db;
var objectStore = db.transaction("something").objectStore("something");
objectStore.openCursor().onsuccess = function(event) {
var cursor = event.target.result;
if (cursor) {
if (cursor.value.text == foo)
return true;
cursor.continue();
}
};
return false;
}
Is there a way to wait for the end of the execution? Or maybe it is not a good way to proceed for checking if a value exist?
As IndexedDB is an asynchronous API you have to wait until operations are completed using callbacks.
First off we need to refactor the IsAlreadyExist method:
function IsAlreadyExist(foo, oncomplete) {
var db = html5DB.indexedDB.db;
var transaction = db.transaction("something");
var objectStore = transaction.objectStore("something");
var exists = false;
objectStore.openCursor().onsuccess = function(event) {
var cursor = event.target.result;
if (cursor) {
if (cursor.value.text == foo) {
exists = true;
return;
}
cursor.continue();
}
};
transaction.oncomplete = function () {
oncompleted(exists);
};
}
Now we'll refactor your other method:
html5DB.indexedDB.addSomething = function(foo) {
IsAlreadyExist(foo, function (isExisting) {
if (!isExisting){
var db = html5DB.indexedDB.db;
var trans = db.transaction(["something"], "readwrite");
var store = trans.objectStore("something");
var request = store.put({
"text": foo,
"timeStamp" : new Date().getTime()
});
}
});
};
So here you'll see we're passing the function to execute when the cursor search is done as a callback, which receives an argument of the status.
This can start getting a bit ugly, which is why Promise's are popular in JavaScript. I wrote a wrapper for IndexedDB that uses a Promise-based API called db.js.

Categories

Resources