I have a save button that calls a function to open a modal dialog with two buttons; "Save Timelines" and "Cancel". The "Save Timeline" button calls two functions AFTER WHICH the page needs to reload. I've tried a few different ways to get this done...
1 Just calling the functions pragmatically:
function genSaveTimelinesModal() {
$("#saveTimelineDialog").dialog({
resizable: false,
height: 250,
modal: true,
buttons: {
"Save timelines": function() {
editSavedTimelines();
saveTimelines();
$(this).dialog("close");
location.reload();
},
Cancel: function() {
$(this).dialog("close");
}
}
});
}
2 Setting up a callback:
function genSaveTimelinesModal() {
$("#saveTimelineDialog").dialog({
resizable: false,
height: 250,
modal: true,
buttons: {
"Save timelines": function() {
editSavedTimelines(saveTimelines());
location.reload();
},
Cancel: function() {
$(this).dialog("close");
}
}
});
}
3 Using JQuery $.when().do():
function genSaveTimelinesModal() {
$("#saveTimelineDialog").dialog({
resizable: false,
height: 250,
modal: true,
buttons: {
"Save timelines": function() {
$.when(editSavedTimelines(), saveTimelines()).do(location.reload());
},
Cancel: function() {
$(this).dialog("close");
}
}
});
}
My issue, on all three attempts, comes about when the "Save Timelines" button is clicked... the page reloads and none of the functions are run. When I pull the location.reload() call out of each example, the functions run as I want them.
Is there a way to reload the page ONLY AFTER the functions are completed?
For reference, here are the functions I'm calling:
function saveTimelines() {
console.log("start save");
for (i=1; i < timelineIndex + 1; i++) {
var dow = startdow;
var clientValue = $("#clientNameSelect" + i).val();
var projectValue = $("#projectSelect" + i).val();
var taskValue = $("#taskSelect" + i).val();
var billingValue = $("#billingSelect" + i).val();
var activityValue = $("#activitySelect" + i).val();
var stateValue = $("#states" + i).val();
var sundayValue = $("#sun" + i).val();
var mondayValue = $("#mon" + i).val();
var tuesdayValue = $("#tue" + i).val();
var wednesdayValue = $("#wed" + i).val();
var thursdayValue = $("#thu" + i).val();
var fridayValue = $("#fri" + i).val();
var saturdayValue = $("#sat" + i).val();
$.ajax({
type: "GET",
url:"biqqyzqyr?act=API_DoQuery&query={'6'.EX.'" + projectValue + "'}AND{'16'.TV.'" + currUserEmail + "'}&clist=3&includeRids=1&fmt=structured",
dataType: "xml",
success: function (xml) {
$(xml).find("record").each(function () {
var resourceMap = new Array();
$(this).children().each(function () {
var name = $(this).attr("id");
var value = $(this).text();
resourceMap[name] = value;
});
resourceRecords.push(resourceMap);
});
console.log("hi");
var resourceRId = '3';
for (var j = 0; j < resourceRecords.length; j++) {
resourceOptions = resourceRecords[j][resourceRId];
console.log(resourceOptions);
}
$.ajax({
type: "GET",
url: "biha4iayz?act=API_AddRecord&_fid_12=" + dow + "&_fid_36=" + clientValue + "&_fid_9=" + projectValue + "&_fid_7=" + taskValue + "&_fid_10=" + billingValue + "&_fid_15=" + activityValue + "&_fid_11=" + stateValue + "&_fid_13=" + sundayValue + "&_fid_57=" + mondayValue + "&_fid_58=" + tuesdayValue + "&_fid_59=" + wednesdayValue + "&_fid_60=" + thursdayValue + "&_fid_61=" + fridayValue + "&_fid_62=" + saturdayValue + "&_fid_17=" + resourceOptions,
dataType: "xml",
success: function () {
console.log(i+ "new")
},
fail: loadFail
});
},
fail: loadFail
});
}
alert(timelineIndex+savedTimelineIndex+" timelines have been saved to the system...");
}
function editSavedTimelines(callback) {
console.log("start edit");
for (j=1; j < savedTimelineIndex + 1; j++) {
var dow = startdow;
var savedRId = $("#recordsaved" + j).val();
var sundayValue = $("#sunsaved" + j).val();
var mondayValue = $("#monsaved" + j).val();
var tuesdayValue = $("#tuesaved" + j).val();
var wednesdayValue = $("#wedsaved" + j).val();
var thursdayValue = $("#thusaved" + j).val();
var fridayValue = $("#frisaved" + j).val();
var saturdayValue = $("#satsaved" + j).val();
console.log(savedRId);
$.ajax({
type: "GET",
url: "biha4iayz?act=API_EditRecord&rid=" + savedRId + "&_fid_13=" + sundayValue + "&_fid_57=" + mondayValue + "&_fid_58=" + tuesdayValue + "&_fid_59=" + wednesdayValue + "&_fid_60=" + thursdayValue + "&_fid_61=" + fridayValue + "&_fid_62=" + saturdayValue,
dataType: "xml",
success: function () {
},
fail: loadFail
});
}
}
The problem with your usage of when is that both of your functions don't return anything. Your call essentially amounts to this:
$.when(undefined, undefined)
Your saveTimelines function is the more complicated function because you make a 2nd ajax call in the callback of the first. And to make matters even worse, these ajax calls are in a loop. So your function isn't "complete" until the inner ajax calls for each iteration of the loop are complete.
I would strongly suggest trying to completely redesign this to simplify things. If you can eliminate the loop as well as the nested ajax calls, this would be much easier.
That being said, lets look at how we could overcome this issue. First to deal with the issue of the inner ajax call, you can solve this by creating your own deferred object. Something like this (ignoring the loop for the moment):
function saveOneTimeline(/* any params here, such as i */) {
// create a deferred object which will be returned by this function and resolved once all calls are complete
var def = $.Deferred();
/* ... */
$.ajax({
/* ... */
success: function (xml) {
/* ... */
$.ajax({
/* ... */
success: function () {
// we are done, resolve the deferred object
def.resolve();
}
});
}
});
// return the deferred object so that the calling code can attach callbacks/use when
return def;
}
Then finally, our previous method can be called in a loop, placing the returned deferred objects into an array and then using when to return a promise that will only resolve once all of the deferreds resolve. It would look something like this:
function saveTimelines() {
// an array to store all of the deferreds
var defs = [];
for (i=1; i < timelineIndex + 1; i++) {
defs.push(saveOneTimeline(i));
}
// call when on the array of deferred objects and return the resulting promise object
return $.when.apply($, defs);
}
Your editSavedTimelines is slightly less complex due to the fact that you don't have nested ajax calls. However, you still have a loop. A very similar approach can be used, except the helper function can simply return the object returned by the ajax call directly.
As you can see, this all very complex. It would probably be a much better idea to instead try to eliminate some of your complexity to avoid having to go to these lengths. Perhaps if you can make one bulk ajax call instead of many in a loop then allow the backend code to handle the separation.
This should work for you. It uses an array of Deferred objects for the for ajax calls in for loops, and has one final deferred object for when all of those are complete. The outer function listens for completion of those deferred objects. Note that I have snipped a lot of code that isn't relevant, and changed from success callbacks to .done() for your ajax calls.
function genSaveTimelinesModal() {
$("#saveTimelineDialog").dialog({
resizable: false,
height: 250,
modal: true,
buttons: {
"Save timelines": function() {
$.when(editSavedTimelines(), saveTimelines()).done(function() {
location.reload();
});
},
Cancel: function() {
$(this).dialog("close");
}
}
});
}
function saveTimelines() {
var finalDef = $.Deferred();
var defs = [];
for (i=1; i < timelineIndex + 1; i++) {
var def = $.Deferred();
defs.push(def);
$.ajax({...}).done(function(xml) {
$.ajax({...}).done(function() {
def.resolve(true);
});
});
}
$.when.apply(null, defs).done(function() {
finalDef.resolve();
});
return finalDef.promise();
}
function editSavedTimelines() {
var finalDef = $.Deferred();
var defs = [];
for (j=1; j < savedTimelineIndex + 1; j++) {
var def = $.Deferred();
defs.push(def);
$.ajax({...}).done(function() {
def.resolve(true);
});
}
$.when.apply(null, defs).done(function() {
finalDef.resolve(true);
});
return finalDef.promise();
}
Related
Simply i just loop an array, and submit data with get in the loops, but i runs so fast that the server stops running. I mini Ddos myself doing this. How i can i make the loop wait until the calls finish, perhaps adding a 1 sek break between loops
$( document ).on("submit", "#add_links", function() {
var error = 0;
var success = 0;
var total = 0;
//Gets data from input field
var new_urls = $("#new_urls").val();
var array_urls = new_urls.split("\n");
var promiss = [];
array_urls.forEach(function(entry) {
var request = $.get("action.php",
{
add_link: "1",
url: encodeURIComponent(entry.trim()),
},
function(data, status){
console.log("Data: " + data + "\nStatus: " + status);
if (data == 1)
{
success++;
total++;
//update fields removed in this post
$("#success_count").html((success));
$("#total_count").html((total));
}
if (data == 2) {
error++;
total++;
//update fields removed in this post
$("#error_count").html((error));
$("#total_count").html((total));
}
});
promiss.push(request);
});
$.when.apply(null, promiss).done(function(){
//do something when done;
});
return false;
});
You could use recursive function to achieve this.
Example
$(document).on("submit", "#add_links", function() {
var error = 0;
var success = 0;
var total = 0;
var new_urls = $("#new_urls").val();
var array_urls = new_urls.split("\n");
var promiss = [];
let index = 0;
function sendAjaxCall() {
if(count >= array_urls.length) return;
var request = $.get(
"action.php",
{
add_link: "1",
url: encodeURIComponent(array_urls[index].trim())
},
function(data, status) {
console.log("Data: " + data + "\nStatus: " + status);
if (data == 1) {
success++;
total++;
$("#success_count").html(success);
$("#total_count").html(total);
}
if (data == 2) {
error++;
total++;
$("#error_count").html(error);
$("#total_count").html(total);
}
count++;
promiss.push(request);
sendAjaxCall();
}
);
}
$.when.apply(null, promiss).done(function() {
$("#close_bug_reportwindow").html(
"Import done, close tab by clicking here"
);
$("#close_icon").html('(<i class="fas fa-times"></i>)');
$("#progress").remove();
});
return false;
});
I have 2 functions that fetch data via the jQuery AJAX method.
Both look identical save for the URL. Both requests are successful and show the data in console, but only one returns the data through the parent function.
saveLoc fetches data that says "OK", and the "OK" is returned if printed to console in the parent code.
getLoc fetches data that is a number, say "17". The number is printed to console from within the function, but in the parent code, the variable (savedLoc) simply returns undefined
Any advice? Am I missing something?
function saveLoc(story,chapter,loc) {
jQuery.ajax({
type: "GET",
url: "index.php?action=saveloc&story="+story+"&chapter="+chapter+"&loc="+loc,
data: "",
cache: false,
success: function (data2) {
console.log("Location saved: "+loc);
return data2;
}
});
}
function getLoc(story,chapter) {
jQuery.ajax({
type: "GET",
url: "index.php?action=getloc&story="+story+"&chapter="+chapter,
data: "",
cache: false,
success: function (data) {
console.log("Location retrieved: "+data);
return data;
}
});
}
$.urlParam = function(name){
var results = new RegExp('[\?&]' + name + '=([^&#]*)').exec(window.location.href);
if (results==null){
return null;
}
else{
return decodeURI(results[1]) || 0;
}
}
var story = $.urlParam('story');
var chapter = $.urlParam('chapter');
$(document).ready(function(){
var start = 1;
var savedLoc = getLoc(story,chapter);
console.log("savedLoc: "+savedLoc);
if(savedLoc > 0) {
var d = $(document).height(),
c = $(window).height();
var scrollPos = Math.floor((savedLoc / 100) * (d - c));
window.scrollTo(0, scrollPos);
}
setTimeout(function() {
$(window).on('scroll', function(){
console.log("scroll detected");
setTimeout(function() {
var s = $(window).scrollTop(),
d = $(document).height(),
c = $(window).height();
var scrollPercent = (s / d) * 100;
saveLoc(story,chapter,scrollPercent);
},3000);
});
},6000)
});
The ajax getLoc is a asynchronous task, so your savedLoc = getLoc(); will not get the return value of it's success function.
For managin asynchronous tasks, like ajax, there are some solutions:
Original and Simple way: If you want to get the return value of ajax, you should use a global variable, and transfer a callback into the ajax function, like getLoc, then call the callback in success;
Promise, manage the asynchronous tasks with synchronous way, refer to https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise;
manage the asynchronous tasks with synchronous way provided in ES6, refer to https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Generator
async await, a replacement for generator, refer to https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
and for more information, refer to the blog, https://blog.risingstack.com/asynchronous-javascript/
function saveLoc(story,chapter,loc) {
jQuery.ajax({
type: "GET",
url: "index.php?action=saveloc&story="+story+"&chapter="+chapter+"&loc="+loc,
data: "",
cache: false,
success: function (data2) {
console.log("Location saved: "+loc);
return data2;
}
});
}
function getLoc(story,chapter, callback) {
jQuery.ajax({
type: "GET",
url: "index.php?action=getloc&story="+story+"&chapter="+chapter,
data: "",
cache: false,
success: function (data) {
console.log("Location retrieved: "+data);
savedLoc = data;
callback && callback();
}
});
}
$.urlParam = function(name){
var results = new RegExp('[\?&]' + name + '=([^&#]*)').exec(window.location.href);
if (results==null){
return null;
}
else{
return decodeURI(results[1]) || 0;
}
}
var savedLoc;
var story = $.urlParam('story');
var chapter = $.urlParam('chapter');
$(document).ready(function(){
var start = 1;
getLoc(story,chapter, afterLocCallback);
function afterLocCallback() {
console.log("savedLoc: "+savedLoc);
if(savedLoc > 0) {
var d = $(document).height(),
c = $(window).height();
var scrollPos = Math.floor((savedLoc / 100) * (d - c));
window.scrollTo(0, scrollPos);
}
setTimeout(function() {
$(window).on('scroll', function(){
console.log("scroll detected");
setTimeout(function() {
var s = $(window).scrollTop(),
d = $(document).height(),
c = $(window).height();
var scrollPercent = (s / d) * 100;
saveLoc(story,chapter,scrollPercent);
},3000);
});
},6000)
}
});
<script
src="https://code.jquery.com/jquery-2.2.4.min.js"
integrity="sha256-BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44="
crossorigin="anonymous"></script>
Firstly, getLoc is not returning anything. so put a return statement in there.
secondly, $.ajax returns jqXHR object. You can use then, done, fail methods on this object. If you are not familiar with these read about promise concepts.
Once your async call is successful do the rest of the operations inside the then method.
function saveLoc(story,chapter,loc) {
//return the ajax promise here
return jQuery.ajax({
type: "GET",
url: "index.php?action=saveloc&story="+story+"&chapter="+chapter+"&loc="+loc,
data: "",
cache: false,
success: function (data2) {
console.log("Location saved: "+loc);
return data2;
}
});
}
function getLoc(story,chapter) {
//return the ajax promise here
return jQuery.ajax({
type: "GET",
url: "index.php?action=getloc&story="+story+"&chapter="+chapter,
data: "",
cache: false,
success: function (data) {
console.log("Location retrieved: "+data);
return data;
}
});
}
$.urlParam = function(name){
var results = new RegExp('[\?&]' + name + '=([^&#]*)').exec(window.location.href);
if (results==null){
return null;
}
else{
return decodeURI(results[1]) || 0;
}
}
var story = $.urlParam('story');
var chapter = $.urlParam('chapter');
$(document).ready(function(){
var start = 1;
getLoc(story,chapter).then(function(data){
var savedLoc = data;
console.log("savedLoc: "+savedLoc);
if(savedLoc > 0) {
var d = $(document).height(),
c = $(window).height();
var scrollPos = Math.floor((savedLoc / 100) * (d - c));
window.scrollTo(0, scrollPos);
}
setTimeout(function() {
$(window).on('scroll', function(){
console.log("scroll detected");
setTimeout(function() {
var s = $(window).scrollTop(),
d = $(document).height(),
c = $(window).height();
var scrollPercent = (s / d) * 100;
// I think you are not using the return value of this call. So not using any promise then here.
saveLoc(story,chapter,scrollPercent);
},3000);
});
},6000)
});
});
I am stuck in a situation where I am hitting multiple ajax calls on controller while working in debugger mode everything works fine but in normal mode it showing argument out of range exception
for (var i = 0; i < artdata.length; i++) {
addNewStepMultiple(artdata[i], i)
}
function addNewStepMultiple(artifactData, index) {
if (artifactData != null) {
var tcIndex, data, url;
var suiteId = serviceFactory.getComponentInfo().id;
var gridInstance = $("#Suite_Grid").data("kendoGrid");
if (gridInstance._data.length == 0) {
tcIndex = -1 + index + 1;
} else {
tcIndex = $("#Suite_Grid").data("kendoGrid").select().index();
if (tcIndex == -1) {
tcIndex = tcIndex + index;
} else {
tcIndex = tcIndex + index + 1;
}
}
console.log('tcIndex' + tcIndex);
var newTcIndex = tcIndex;
var treeBinding = JSON.stringify(artifactData);
url = "/Suite/AddNewStep";
data = { SuiteID: suiteId, position: tcIndex, artifactModel: treeBinding };
$.ajax({
type: "POST",
url: url,
data: data,
success: function (res) {
debugger; //$scope.SuiteData.data(res);
bindSuiteGrid(res); //$scope.SuiteData.data(result)
$scope.setChanges();
//var tr = grid.element.find('tbody tr:eq(' + (newindex) + ')'); //.addClass('k-state-selected')
// grid.select(tr);
var tr = $('#Suite_Grid table tr:eq(' + (res.length) + ')')
$("#Suite_Grid").data("kendoGrid").select(tr);
loadingStop("#vertical-splitter", ".btnTestLoader");
},
error: function (error) {
debugger
loadingStop("#vertical-splitter", ".btnTestLoader");
serviceFactory.showError($scope, error);
}
});
}
}
Please let me know how to solve the problem.
in your scenario loop can not wait up to your ajax request in normal mode. so click here to know the how to make a multiple ajax calls
I want to set global variable from function and loop ajax to get distance.
However the nearestIndex variable is always undefined.
First solution I got was to use async: false - this is work in my pc browser, but this project is webservice to android, and this solution not work to webview.
And of course async: false not recommended. I need this example in my case, I've been looking for this problem in stack overflow, but i always failed to understand about callback.
var allDestination = ["A", "B", "C"];
var nearestIndex;
function getNearest(){
var from = myPosition.getLatLng().lat + "," + myPosition.getLatLng().lng;
var tempDistance;
for(var i=0; i<allDestination.length; i++){
var destination = allDestination[i].getLatLng().lat + "," + allDestination[i].getLatLng().lng;
$.ajax({
type: "GET",
url: "http://localhost:8989/route?point=" + from + "&point=" + destination + "&points_encoded=false&instructions=false",
dataType: 'json',
contentType: "application/json",
success: function (data) {
var distance = data.distance;
if(i == 0){
tempDistance = distance;
nearestIndex = i;
} else {
if(distance < tempDistance){
tempDistance = distance;
nearestIndex = i;
}
}
}
});
}
}
function onMapClick(e) {
myPosition.setLatLng(e.latlng);
myPosition.addTo(map);
getNearest();
allDestination[nearestIndex].addTo(map);
}
As you are dealing with Async call; your relevant code has to get called from success handler of ajax call as follows:
var allDestination = ["A", "B", "C"];
var nearestIndex;
var tempDistance;
var successReceived = 0; //counter to keep watch on ajax success callback
//modify the function signature to receive index as well as callback function
function getNearest(index, callbackFunction) {
var from = myPosition.getLatLng().lat + "," + myPosition.getLatLng().lng;
var destination = allDestination[index].getLatLng().lat + "," + allDestination[index].getLatLng().lng;
$.ajax({
type: "GET",
url: "http://localhost:8989/route?point=" + from + "&point=" + destination + "&points_encoded=false&instructions=false",
dataType: 'json',
contentType: "application/json",
success: function(data) {
successReceived++; //increment the success counter
var distance = data.distance;
if (index == 0) {
tempDistance = distance;
nearestIndex = index;
} else {
if (distance < tempDistance) {
tempDistance = distance;
nearestIndex = index;
}
}
//check if we got all the ajax response back. If yes then call the callback function
if(successReceived == allDestination.length && typeof callbackFunction == 'function')
{
callbackFunction();
}
}
});
}
function onMapClick(e) {
myPosition.setLatLng(e.latlng);
myPosition.addTo(map);
for (var i = 0; i < allDestination.length; i++) {
//pass the current index and callback function
getNearest(i,function(){
allDestination[nearestIndex].addTo(map);
});
}
}
I ever have got the same problem like you,
it because asincrounous function cant return anything.
so I think you shoud inject allDestination[nearstIndex].addTo(map); into ajax success
if(i == 0){
tempDistance = distance;
allDestination[i].addTo(map);
} else {
if(distance < tempDistance){
tempDistance = distance;
allDestination[i].addTo(map);
}
}
or you create function to handle ajax success,,, CMIIW
I have an input text box which fires each time when the user enters data and fills the input text.I'm using bootstrap typehead. Problem is when i enter a letter a it does fire ajax jquery call and fetch the data but the input text box is not populated.Now when another letter aw is entered the data fetched against letter a is filled in the text area.
I have hosted the code here http://hakunalabs.appspot.com/chartPage
Ok so here is part of my html code
<script type="text/javascript">
$(document).ready(function () {
$('#txt').keyup(function () {
delay(function () {
CallData();
}, 1000);
});
});
var delay = (function () {
var timer = 0;
return function (callback, ms) {
clearTimeout(timer);
timer = setTimeout(callback, ms);
};
})();
</script>
<input type="text" id="txt" runat="server" class="span4 typeahead local remote" placeholder="Search..." />
And here is my javascript code
var DataProvider;
function CallData() {
DataProvider = [];
var vdata = $('#txt').val();
if (vdata != "") {
var urlt = "http://examples/search?keyword=" + vdata + "&callback=my_callback";
$.ajax({
type: "GET",
url: urlt,
jsonpCallback: "my_callback",
dataType: "jsonp",
async: false,
error: function (xhr, errorType, exception) {
var errorMessage = exception || xhr.statusText;
alert("Excep:: " + exception + "Status:: " + xhr.statusText);
}
});
}
}
function my_callback(data) {
var NameArray = new Array();
var descArray = new Array();
for (var i = 0; i < data.count; i++) {
NameArray.push(data.response[i].days_till_close + " Days Left | " + data.response[i].name + " | " + data.response[i].description);
}
for (var i = 0; i < data.count; i++) {
descArray.push(data.response[i].description);
}
DataProvider = [];
for (var i = 0; i < data.count; i++) {
var dataObject = { id: i + 1, name: NameArray[i], description: descArray[i] };
DataProvider.push(dataObject);
}
var vdata = $('#txt').val();
var urlp = "http://example.com/v1/members/search?keyword=" + vdata + "&my_callbackMember";
$.ajax({
type: "GET",
url: urlp,
jsonpCallback: "my_callbackMember",
dataType: "jsonp",
error: function (xhr, errorType, exception) {
var errorMessage = exception || xhr.statusText;
alert("Excep:: " + exception + "Status:: " + xhr.statusText);
}
});
}
function my_callbackMember(data) {
var memberArray = new Array();
for (var i = 0; i < data.count; i++) {
memberArray.push(data.response[i].name);
}
for (var i = 0; i < data.count; i++) {
var dataObject = { id: i + 1, name: memberArray[i] };
DataProvider.push(dataObject);
}
localStorage.setItem("name", JSON.stringify(DataProvider));
var sources = [
{ name: "local", type: "localStorage", key: "name", display: "country" }
];
$('input.typeahead.local.remote').typeahead({
sources: [{ name: "", type: "localStorage", key: "name", display: "name"}],
itemSelected: function (obj) { alert(obj); }
});
}
Your issue is that typeahead can only present to you the results that are already in localstorage at the moment when you do a key press. Because your results are fetched via AJAX, they only show up in localstorage a second or so AFTER you've pressed the key. Therefore, you will always see the results of the last successful ajax requests in your typeahead results.
Read the bootstrap documentation for type aheads http://twitter.github.com/bootstrap/javascript.html#typeahead and read the section about "source". You can define a "process" callback via the arguments passed to your source function for asynchronous data sources.