I've two problems with this code.
1 $(container + ' meta[data-level="' + level + '"]').length == 0 is always zero
I know this because I create extra call create_views(1); which should not get added, but it gets appended anyhow.
2 dpUniSlider doesn't work as it doesn't see li created via ajax call. If I move it into success message it works fine, but outside function does not. Problem is if I include it inside ajax success it will get called several times as it is under a loop
//Show levels using ajax, before slider is activated
function create_views(level) {
$.ajax({
url: "actions.php",
type: "GET",
data: "show_level=" + level,
cache: false,
success: function (data) {
var view = '<li data-level="' + level + '">' + data + '</li>';
var container = ".slides_container";
if ($(container + ' meta[data-level="' + level + '"]').length == 0) {
$(container).append(view);
} else { //check if element exists, if yes overwrite it.
//$(container + ' meta[data-level="' + level + '"]').replaceWith(view);
alert("Exists");
}
}
});
}
//Loop through all levels and display views
//level count can be rewritten to come from DB and not be hardcoded like now
var levels = 2;
for (var i = 1; i <= levels; i++) {
create_views(i);
} // for loop
create_views(1); //test, delete this
//Activate slide
var unislider = $(".slides_container").dpUniSlider({
//loop: false,
draggable: false
});
For handling multiple parallel asynchronous ajax calls where you want to perform something after they have all completed, you could keep a counter and check in the success callback to see when the last ajax call has succeeded.
It would look like this:
$(function() {
var TOTAL_TASKS = 2,
completedTasks = 0;
function performAjaxTask(taskNumber) {
$.ajax({
url: url,
type: 'GET',
data: data,
cache: false,
success: function(data) {
// Process the data
completedTasks++;
if (completedTasks == TOTAL_TASKS) {
// Perform actions that need to wait until all
// ajax calls have returned successfully.
}
}
});
}
for (var i = 1; i <= TOTAL_TASKS; i++) {
performAjaxTask(i);
}
});
I believe you can use jQuery deferred objects for handling the multiple parallel asynchronous ajax calls where you want to perform something after they have all completed successfully.
Try this:
$(function() {
var LEVELS = 2,
$container = $('.slides_container'),
deferreds = []; // This will hold the deferred objects.
// This function returns a deferred object.
function getViewAndReturnDeffered(level) {
return $.ajax({
url: 'actions.php',
type: 'GET',
data: 'show_level=' + level,
cache: false,
success: function(data) {
var $currentView = $container.find('[data-level="' + level + '"]'),
$newView = '<li data-level="' + level + '">' + data + '</li>';
if ($currentView.length > 0) {
$currentView.replaceWith($newView);
} else
$container.append($newView);
}
}
});
}
for (var i = 1; i <= LEVELS; i++) {
// Put the deferred objects in the array.
deferreds.push(getViewAndReturnDeffered(i));
}
// The function passed to `.done()` will execute when all the deferred
// objects have completed successfully.
$.when.apply($, deferreds).done(function() {
var unislider = $container.dpUniSlider({
//loop: false,
draggable: false
});
});
});
Do you have a <meta></meta> inside .container? Probably not. Use
$(container + ' li[data-level="' + level + '"]')
Related
I have this function
function actInfo(fund) {
var acct = $('.account');
$.ajax({
url: "account.php",
type: 'get',
dataType: 'json',
data: {fund: fund}
}).done(function (response) {
var len = response.length;
acct.empty();
for (var i = 0; i < len; i++) {
var acctNum = response[i]['ID'];
var acctName = response[i]['NAME'];
acct.append("<option value='" + acctNum + "'>" + acctNum + ' -- ' + acctName + "</option>");
}
acct.prepend("<option value=''>- Select An Account -</option>").val('');
}).fail(function (jqXHR, textStatus, error) {
console.log("actInfo: " + error);
});
}
I call this function in another ajax call because I need to send the function variable that I get from the server. In the same ajax call that I use to call actInfo(fund) I am also trying to select a value. Here is the main ajax call.
function getPoInfo(trnum) {
$.ajax({
url: "edit.php",
data: {trnum: trnum},
type: "GET",
cache: false,
dataType: "json"
}).done(function (poInfo) {
$('#fund').val(poInfo[0]['TRANFUND']);
actInfo(poInfo[0]['TRANFUND']);
$('#account1').val(poInfo[0]['TRANACCOUNT'].trim());
}).fail(function (jqXHR, textStatus, error) {
console.log("getPoInfo(tran_num): " + error);
});
}
The actInfo(fund) function works fine. It builds my dropdown menu. But I don't know how to select a value. I am assuming I am trying to select a value before the dropdown is loading. I don't understand callbacks or promises, and I tried .bind and tried to append .done and .load but I can't get a value selected. How do I do tell when the dropdown menu is finished loading and then select a value?
Since actInfo() method contains an asynchronous operation (basically the <select> element is only built after you have received and parsed the returned JSON response). Therefore, what you want is to:
create a new deferred object, i.e. var deferred = new $.Deferred()
return its immutable promise at the end, i.e. return deferred.promise().
The new deferred object should be resolved (deferred.resolve()) or rejected (deferred.reject()) based on the outcome of the inner AJAX request.
If we take all these points into account, your code can be easily be refactored as follow (I have added comments where I have added the suggested changes):
function actInfo(fund) {
var acct = $('.account');
// Create new deferred object
var deferred = new $.Deferred();
// Perform AJAX call as usual
$.ajax({
// Truncated for brevity
// ...
}).done(function (response) {
var len = response.length;
acct.empty();
for (var i = 0; i < len; i++) {
var acctNum = response[i]['ID'];
var acctName = response[i]['NAME'];
acct.append("<option value='" + acctNum + "'>" + acctNum + ' -- ' + acctName + "</option>");
}
acct.prepend("<option value=''>- Select An Account -</option>").val('');
// Now that the DOM has been built, we can resolve the promise and return it!
deferred.resolve();
}).fail(function (jqXHR, textStatus, error) {
console.log("actInfo: " + error);
// If we encounter an error, we pass it on!
deferred.reject(error);
});
// Return immutable promise
return deferred.promise();
}
Then, in your getPoInfo() method, simply check for the resolution of the returned promise from actInfo():
function getPoInfo(trnum) {
$.ajax({
// Truncated for brevity
// ...
}).done(function (poInfo) {
$('#fund').val(poInfo[0]['TRANFUND']);
// actInfo returns a promise!
var actInfoPromise = actInfo(poInfo[0]['TRANFUND']);
// Wait for the promise to resolve using $.when
$.when(actInfoPromise)
.then(function() {
// If successful, we select the correct <option>
$('#account1').val(poInfo[0]['TRANACCOUNT'].trim());
}, function(error) {
// If failed, we log the error message that has been passed on
console.log(error);
});
}).fail(function (jqXHR, textStatus, error) {
console.log("getPoInfo(tran_num): " + error);
});
}
This question already has answers here:
Wait until all jQuery Ajax requests are done?
(22 answers)
Closed 5 years ago.
I have been trying to redirect a page after the for loop has finished but it executes it before the for loop even if the code is outside the for loop. So am wondering if there is some way of executing code and redirecting to another page after the for loop is done in JavaScript. This is my code.
$('#submit').click(function(e) {
e.preventDefault();
var total = $('#total').val();
for (var i = 0; i < total; i++) {
if ($('#check_' + i).is(':checked')) {
// The string to be posted to the submit file
var dataString = 'month=' + month + '&year=' + year + '&patient_id=' + patient_id;
// AJAX code to submit form.
$.ajax({
type: "POST",
url: "pages/views/payroll/bulk_payroll_functions.php",
data: dataString,
cache: false,
success: function(result) {
alert("good");
}
});
}
}
alert("All members payrolls made");
window.location = ("index.php?lang=en&page=view_payroll");
})
The code is working as you're expecting - the AJAX requests are being made. However, because they are asynchronous, you are not guaranteed that they'll have finished before you redirect.
The cleanest way to do this would be to use the Promises which $.ajax returns.
You can then use $.when to redirect when all ajax requests are completed:
$('#submit').click( function(e) {
e.preventDefault();
// array to store the promises
var promises = [];
var total = $('#total').val();
for(var i = 0; i < total; i++){
if($('#check_' + i).is(':checked')){
// The string to be posted to the submit file
var dataString = 'month=' + month + '&year=' + year + '&patient_id=' + patient_id ;
// AJAX code to submit form.
promise = $.ajax({
type: "POST",
url: "pages/views/payroll/bulk_payroll_functions.php",
data: dataString,
cache: false,
success: function (result) {
alert("good");
}
});
// add ajax request to the promises
promises.push(promise);
}
}
// redirect when all promises have resolved
$.when(promises).then(function () {
alert("All members payrolls made");
window.location = ("index.php?lang=en&page=view_payroll");
});
});
The following code I am running and during execution I receive an error.
public void GetCategoriesSelenium() {
string javascript = System.IO.File.ReadAllText(#"GetCategory.js");
CrawlerWebSeleniumJS.ExecuteScript("var finished;");
CrawlerWebSeleniumJS.ExecuteScript("var all_categories;");
CrawlerWebSeleniumJS.ExecuteScript("finished = false;");
CrawlerWebSeleniumJS.ExecuteScript("all_categories = [];");
CrawlerWebSelenium.Manage().Timeouts().SetScriptTimeout(TimeSpan.FromDays(1));
CrawlerWebSelenium.Manage().Timeouts().SetPageLoadTimeout(TimeSpan.FromDays(1));
CrawlerWebSelenium.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromDays(1));
AddToConsole("CRAWLER: GET - Categories");
try {
CrawlerWebSeleniumJS.ExecuteScript(javascript);
}
catch {
}
int ready = 2;
for (int i = 0; i < ready; i++) {
try {
if (CrawlerWebSeleniumJS.ExecuteScript("return finished").ToString() == "True") {
i = i++ + ready++;
}
else {
ready++;
}
}
catch {
}
}
AddToCatsTreeSelenium();
}
$('.p-pstctgry-lnk-ctgry').each(function (i) {
var idBits = this.id.split('_');
var theId = idBits[1];
var theTitle = this.text;
var subcategories = [];
//initiate ajax request for json results
$.ajax({
async: false,
type: 'GET',
dataType: 'json',
url: 'URL REMOVED',
data: {
nodeType: 'cat',
level1id: theId
}
}).done(function (theJSON1) {
var thelength1 = Object.keys(theJSON1['items']).length;
//loop through found subs
for (var i = 0; i < thelength1; i++) {
//start of next recursive block to copy and paste inside
var subsubcategories = [];
//initiate ajax request for sub json results
$.ajax({
async: false,
type: 'GET',
dataType: 'json',
url: 'URL REMOVED',
data: {
nodeType: 'cat',
level1id: theId,
level2id: theJSON1['items'][i]['id']
}
}).done(function (theJSON2) {
var thelength2 = Object.keys(theJSON2['items']).length;
for (var k = 0; k < thelength2; k++) {
//start of next recursive block to copy and paste inside
var subsubsubcategories = [];
//initiate ajax request for sub json results
if ((theJSON2['items'][k]['id'] != 'OFFER') && (theJSON2['items'][k]['id'] != 'WANTED')) {
$.ajax({
async: false,
type: 'GET',
dataType: 'json',
url: 'URL REMOVED',
data: {
nodeType: 'cat',
level1id: theId,
level2id: theJSON1['items'][i]['id'],
level3id: theJSON2['items'][k]['id']
}
}).done(function (theJSON3) {
var thelength3 = Object.keys(theJSON3['items']).length;
for (var l = 0; l < thelength3; l++) {
console.log('---' + theJSON3['items'][l]['value'] + ' ' + theJSON3['items'][l]['id']);
//store this subsub
subsubsubcategories.push({
title: theJSON3['items'][l]['value'],
id: theJSON3['items'][l]['id'],
sub: ''
});
}
//end done theJSON
});
}
//end of next recursive block to copy and paste inside
console.log('--' + theJSON2['items'][k]['value'] + ' ' + theJSON2['items'][k]['id']);
//store this subsub
subsubcategories.push({
title: theJSON2['items'][k]['value'],
id: theJSON2['items'][k]['id'],
sub: subsubsubcategories
});
}
//end done theJSON
});
console.log('-' + theJSON1['items'][i]['value'] + ' ' + theJSON1['items'][i]['id']);
//store this sub with -> subsub
subcategories.push({
title: theJSON1['items'][i]['value'],
id: theJSON1['items'][i]['id'],
sub: subsubcategories
});
//end of next recursive block to copy and paste inside
//end sub loop
}
console.log('' + theTitle + ' ' + theId);
//store this cat with -> sub -> subsub
all_categories.push({
title: theTitle,
id: theId,
sub: subcategories
});
console.log(all_categories);
//end first json subcat loop
});
//end main cat scan loop
});
finished = true;
The above code is the method that I run and the code that is under it is pure javascript that is being run through selenium.
So issue one, when the code is run selenium locks up. Which I can understand. This process takes about 4mins. After 60secs it times out with the error
The HTTP request to the remote WebDriver server for URL timed out after 60 seconds.
Which is really annoying and locks the system up. I know a really quick and easy way to get this fixed. (Thread.Sleep(300000) which is disgusting...
My thoughts are, maybe it is running a javascript query and waiting for it to finish and I am constantly pounding Selenium with more javascript requests which time out as expected.
Any other thoughts?
The driver's constructor should have an overload that includes a TimeSpan indicating the timeout for the HTTP client used by the .NET bindings to communicate with the remote end. Setting that to an appropriately large value should be sufficient to let the operation complete.
I have doubt regarding the execution order for a jquery function i created for my project. The function is given below.
$('#s_cust').change(function(event) {
var custId = $("select#s_cust").val();
$.get('ContactAjax', {
custId: custId
}, function(jsonResponse) {
alert("jsonresp: " + jsonResponse);
cconjson = jsonResponse;
var select = $('#s_ccon');
$(select).find('option').remove();
$('<option>').text("Select").appendTo(select);
$.each(jsonResponse, function(key, value) {
$('<option>').val(key).text(value).appendTo(select);
});
});
if (cconjson != null) {
for (var j = 1; j <= i; j++) {
var select1 = $('#s_ccon' + j);
$(select1).find('option').remove();
alert("test");
$('<option>').text("Select").appendTo(select1);
$.each(cconjson, function(key, value) {
alert("key: " + key + " value:" + value);
$('<option>').val(key).text(value).appendTo(select1);
});
}
}
});
"'#s_ccon' + j" is given because I'm dynamically generating a text box based on the click of a button.
The problem that I have got while using this function is that, after refresh of the form page I change the value in my select list, s_cust, it goes into the ajax call retrieves my data and populate the s_ccon correctly. When I change value of s_cust again it executes the if loop first, then goes back and does the ajax function get, I understood this beacause the first alert that comes after the second change is alert("test"), then the alert("key: " + key + " value:" + value), after this alert("jsonresp: " + jsonResponse).
I don't know why this happens, please tell me the mistake I have made here.
Since Ajax is asynchronous java script does not wait for the ajax request to come back to solve this problem as mention in the question above I had to put the condition evaluation regarding ajax inside the json response function itself, so finally my code looks like this.
$('#s_cust').change(function(event) {
var custId = $("select#s_cust").val();
$.get('ContactAjax', {
custId: custId
}, function(jsonResponse) {
alert("jsonresp: " + jsonResponse);
cconjson = jsonResponse;
var select = $('#s_ccon');
$(select).find('option').remove();
$('<option>').text("Select").appendTo(select);
$.each(jsonResponse, function(key, value) {
$('<option>').val(key).text(value).appendTo(select);
});
if (cconjson != null) {
for (var j = 1; j <= i; j++) {
var select1 = $('#s_ccon' + j);
$(select1).find('option').remove();
alert("test");
$('<option>').text("Select").appendTo(select1);
$.each(cconjson, function(key, value) {
alert("key: " + key + " value:" + value);
$('<option>').val(key).text(value).appendTo(select1);
});
}
}
});
});
According to the documentaion of jQuery, $.get is an asynchronous function. It is equivalent to
$.ajax({
url: url,
data: data,
success: success,
dataType: dataType
});
So you cannot predict the order of execution.
If you want it to happen in a synchronous manner, use $.ajax directly and turn off async.
$.ajax({
url: url,
data: data,
async: false,
success: success,
dataType: dataType
});
Also agree, that it is recommended to place all the code inside the success callback and follow the async process.
I'm trying to iterate through elements of .ball class and check if their associated url exists:
$(".ball").each(function(i){
var url;
var c_day = $(this).data('day');
var c_mon = $(this).data('mon');
var c_year = $(this).data('year');
url = c_year + "/" + c_mon + "/" + c_day + ".html";
$.ajax({
url: url,
error: function()
{
alert('file: ' + url + ' does not exist');
},
success: function()
{
alert('file: ' + url + 'EXXXXXXISTS!!!!!');
blogA[ blog_count ] = url;
blog_count++;
$(this).css("color", "red" );
}
});
});
I've done some research and read that using .ajax in .each causes a lot of problems but I couldn't wrap my head around on how to fix it.
The problem is that I get really weird results (has to do with asynchronous?). If I alert url outside of ajax, it correctly iterates through the elements. If I alert url in ajax, it spits out urls that belong to the last element of the class.
Something like this, in order to simplify your code
function successHandler(url, ball) {
return function(ret) {
alert('file: ' + url + 'EXXXXXXISTS!!!!!');
ball.css('color','red')
}
}
var balls = $('.ball'), requests = []
balls.each(function(index, ball) {
var url = ...
requests.push($.ajax({ url : url , success : successHandler(url, ball) })
})
$.when.apply($, requests).done(function() {
alert('all balls are checked')
})
Or with ES6:
const success = (url,ball)=>(ret)=>ball.css('color','red')
const balls = $('.balls')
, reqs = balls.map( (b, i) => {
const url = ...
return $.ajax({url, success:success(url,ball)})
})
$.when.apply($, reqs).done( (resps) => alert('all done'))
A Little explanation: You are blindly using this in your callback, not knowing in which context it is going to be executed. In order to work around it and has your URL into callback we are creating function that returns a function, so it will have URL of current .ball DOM object in the context.
You'll probably also need to execute code after all ajax requests are done, so using $.when is the simplest way of doing it. And we are storing all promises just for this reason.
If you aren't concerned about the order of execution of each ajax call and just want to know when they are all done and the array is fully populated, then you can get this to work by fixing your reference to this and by adding a callback function that is called when all items are done:
// this function called when the ajax calls for all balls have finished
// at this point, blogA and blog_count will be populated
function ballsDone() {
// put your code here
}
var balls = $(".ball");
var cnt = balls.length;
balls.each(function(i){
var url;
var self = $(this);
var c_day = self.data('day');
var c_mon = self.data('mon');
var c_year = self.data('year');
url = c_year + "/" + c_mon + "/" + c_day + ".html";
$.ajax({
url: url,
error: function()
{
alert('file: ' + url + ' does not exist');
if (--cnt <= 0) {
ballsDone();
}
},
success: function()
{
blogA[ blog_count ] = url;
blog_count++;
self.css("color", "red" );
if (--cnt <= 0) {
ballsDone();
}
}
});
});
Keep in mind that the ajax calls are asynchronous so the ONLY place you can use the results from the ajax call is in the success handler. You can't use the results of the ajax calls right after the .each() call in a synchronous fashion because the ajax calls have not yet finished. You must wait for the success handler to be called and when cnt success handlers have been called, then they are all done and you can then process the results.