Async AJAX calls overwriting each other - ajax

I've got a dashboard page, and am using jQuery to update each graph with a single ajax call.
If it run AJAX with async:false then everything works, but it's obviously slow as the calls are made one after another.
When I run async:true, the queries execute but they all output to the same element and overwrite each other.
How can I ensure that the jQuery selector in the success and error functions remain pointed to their original desintation and do not all point to the final box?
My code:
//update sparklines on dashboard page
$(".convobox7").each(function() {
id = $(this).attr('id');
$("#convobox-7-"+id).prepend("<img src='img/ajax_loader.gif'/>");
$.ajaxQueue({
url: '_ajax/getFunnelReport',
type: "POST",
dataType: "json",
async: true,
data: {funnel:$(this).attr('id'), dimension:'date'},
timeout: 50000,
success: function(json) {
var data = json;
if (data.success=='true') {
$("#convobox-7-"+id).html(data.htmlconv+"<br/><small>Past week</small>");
gebo_peity.init();
}
},
error: function(x, t, m) {
$("#convobox-7-"+id).html("");
}
})
});
Note I'm using the ajaxQueue plugin here but the same thing happens without it.

You need to localise id :
var id = $(this).attr('id');
There may be other things to fix but that one is a certainty.
EDIT
Try this :
$(".convobox7").each(function() {
var id = $(this).attr('id');
var $el = $("#convobox-7-"+id).prepend("<img src='img/ajax_loader.gif'/>");
$.ajaxQueue({
url: '_ajax/getFunnelReport',
type: "POST",
dataType: "json",
data: {funnel:id, dimension:'date'},
timeout: 50000,
success: function(data) {
if (data.success == 'true') {
$el.html(data.htmlconv+"<br/><small>Past week</small>");
gebo_peity.init();
}
},
error: function(x, t, m) {
$el.html("");
}
});
});

This has to do with function closures because you declared the variable outside the success/error function. A better approach is to use the $(this) reference in the error/success functions instead of assigning it outside the handlers.
Edit: In the context of the error/success handler for ajaxQueue, I'm not absolutely certain what $(this) refers to, you may need to navigate to a parent element. I didn't see any definitive documentation offhand. This is one of my biggest pet peeves with javascript documentation, $(this) is sometimes not what you would think it'd be and isn't documented :/

silly question, but since you already send the element id to the service, is there a reason it cannot send it back? then you can simply use that as a selector, ensuring that you have the item you need.

Related

Observable values disappearing after being pushed into observableArray

I'm grabbing data from the server and pushing them into an observable array.
I'm pushing observables into an observable array.
As I push the data into the observables, the observables contain the data.
However as soon as I push the observables into the observable Array, a few of the observables are missing data.
self.mealFoods([]);
$.ajax({
url: "/mealsurl/1",
async: false,
dataType: 'json',
success: function(datad) {
for(var lia = 0; lia < datad.length; lia++){
var cats_url = "/catsurl/" + datad[lia].category_id;
var units_by_food_url = "/unitsurl/" + datad[lia].ndb_no;
var foodThing = new NewFood();
foodThing.foodId(parseInt(datad[lia].id)); //works
foodThing.category(parseInt(datad[lia].category_id)); //works
$.ajax({
url: cats_url,
dataType: 'json',
success: function(dat) {
foodThing.category_foods(dat); //works
}
});
foodThing.food(datad[lia].ndb_no); //works
$.ajax({
url: units_by_food_url,
dataType: 'json',
success: function(dat) {
foodThing.food.units(dat); //works
}
});
foodThing.unit(parseInt(datad[lia].seq)); //works
foodThing.number_of_unit(datad[lia].this_much); //works
self.mealFoods.push(foodThing);
// At this point when looking inside the mealFoods array: self.mealFoods()[0].food(), self.mealFoods()[0].unit(), self.mealFoods()[0].food.units(), self.mealFoods()[0].category_Foods() ALL ARE EMPTY
}
}
});
You, sir, are having a classic case of async-brain-melt. It is a common sympton in beginners but never fear for the recovery rate is nearly 100%. :)
I would wager your experience is with synchronous languages, that is, where if one line is written after the other, the lines written before are executed before, always.
A normal JavaScript function is synchronous. For example:
console.log(1);
console.log(2);
As expected, this prints 1 and then 2.
However, asynchronous code is not necessarily executed in the order it was declared. Consider this example using a setTimeout function, which schedules a function for later execution:
setTimeout(function(){ console.log(1); }, 1000);
console.log(2);
Now, the output will be 2 and 1, because 1 only ran 1000 millis after the setTimeout call.
So, I imagine you are beginning to understand how this applies to your problem.
Your calls to cats_url and units_by_food_url are asynchronous. Therefore, the following code does not wait for them to finish. So, when you access self.mealFoods()[0].food.units(), the success function has not yet grabbed the data!
What you need to do is to coordinate your asynchronous calls appropriately. There are many ways to achieve that. First, I'll teach you the most simple strategy, using only functions:
Grab the list from the server
When you have the list, iterate over each meal and start two ajax calls (up to here, you are already doing everything right)
Now comes the magic: when you have the results for either ajax call, you call an "itemComplete" function. This function will sync the two calls - it will only proceed if the two calls finished.
Finally, call a "listComplete" function each time any item is complete. This function must also check if all items are complete before proceeding.
So, it would look something like this:
$.ajax({
url: "/meals/1",
dataType: 'json',
success: function(list) {
var observableArray = ko.observableArray([]); // this will hold your list
var length = list.length;
var tries = 0;
var listComplete = function () {
tries++;
if (tries == length) {
// Hooray!
// All your items are complete.
console.log(observableArray());
}
};
list.forEach(function(item){
var propertyOneUrl = item.propertyOneUrl;
var propertyTwoUrl = item.propertyTwoUrl;
var propertyOneComplete = false;
var propertyTwoComplete = false;
var food = new Food(item.id);
var itemComplete = function () {
if (propertyOneComplete && propertyTwoComplete) {
// This item is complete.
observableArray.push(food);
// Let's warn list complete so it can count us in.
listComplete();
}
};
// Start your ajax calls
$.ajax({
url: propertyOneUrl,
dataType: 'json',
success: function (propertyOne) {
food.propertyOne(propertyOne);
// Declare that your first property is ready
propertyOneComplete = true;
// We can't know which property finishes first, so we must call this in both
itemComplete();
}
});
$.ajax({
url: propertyTwoUrl,
dataType: 'json',
success: function (propertyTwo) {
food.propertyTwo(propertyTwo);
// Declare that your second property is ready
propertyTwoComplete = true;
// We can't know which property finishes first, so we must call this in both
itemComplete();
}
});
}); //for each
} // success
});
Now, you probably realize how tiresome that pattern can be. That's why there are other ways to better solve this problem. One of these is a pattern called "Promises". You can learn more about them in these links:
https://www.promisejs.org/
http://blog.gadr.me/promises-are-not-optional/
And you'll be happy to know that jQuery.ajax() returns a Promise! So, now you can try and solve that problem using Promises. You'll end up with a much cleaner code.
Hope you make it!
It's because you are doing async ajax calls in a loop. Because whenever an ajax call is made it the loop continues it means that by the time the response comes back the object assigned to foodThing is now no longer what it was set to before the ajax call. Because a for loop is so quick is most likely that only the last object created in the loop is updated.
If you have a look at this simple loop it has the same problem:
for (var i = 0; i < 10; i++){
var a = new NewFood(i);
$.ajax({
url: "/catsurl/1",
dataType: 'json',
success: function(dat) {
console.debug(a.id);
}
});
}
By the time the ajax call comes back a has changed and what ends up happening is only 9 gets written out 10 times: http://jsfiddle.net/r6rwbtb9/
To fix this we would use a closure which is essentially wrapping the ajax call in a function in which we self contain the item we want to do something with:
for (var i = 0; i < 10; i++){
var a = new NewFood(i);
(function (a) {
$.ajax({
url: "/catsurl/1",
dataType: 'json',
success: function(dat) {
console.debug(a.id);
}
});
})(a);
}
And then you can see that the numbers 0-9 are output to the console: http://jsfiddle.net/r6rwbtb9/1/. It's also interesting to note that you can't ensure that each request will necessarily come back in the the same order. That is why sometimes the numbers could come back in a different order to 0-9 because some requests are quicker than others.
SO back to your code. In order to make sure you are updating the correct item for each callback you need to use a closure for each ajax call. There was also a problem with foodThing.food.units(dat) which needed to be foodThing.food().units(dat) as foodThing.food() is an observable.
So to wrap in closures we need to change the two ajax calls to this:
(function(category_foods){
$.ajax({
url: cats_url,
dataType: 'json',
success: function(dat) {
category_foods(dat);
}
});
})(foodThing.category_foods);
(function(units){
$.ajax({
url: units_by_food_url,
dataType: 'json',
success: function(dat) {
units(dat);
}
});
})(foodThing.food().units);

jQuery AJAX data to HTML

EDIT: Ok, so the solution i came up with, is basically count the characters and see the difference between the numbers. One headache i had was related with the fact that the .html() didn't showed me the with the slash, instead, . Annoying....
function verifica(){
$.ajax({
type: "GET",
datatype: "html",
url: 'icallverifica.php',
data: "valor=0",
success: function(data) {
var verificando = $('#results').html();
var verificandox = (verificando.length);
var verificador = data.length;
if(verificandox != verificador){
$('#results').html(data);
}
}
});
}
I'm creating a little script using AJAX that retrieves data from a database. The problem is that I've used setInterval and it's refreshing all the time.
I don't have a problem with too many accesses to the database, my problem is that I want the content as static as possible until there are new entries on the database:
function verifica() {
$.ajax({
type: "GET",
datatype: "html",
url: 'icallverifica.php',
data: "valor=0",
success: function(data) {
var verificando = $('#results').html();
if (verificando != "<html>"+data+"</html>") {
$('#results').html(data);}
}
});
}
The function changes the #results div introducing the database information, the thing is that I don't want to change the div content unless there are any new entries.
What I did was check on the database and compare the previous content on the div, if it's the same, it will not overwrite.
BUT, i can't put data in html format...
Try changing the success handler to:
success: function(data) {
var verificando = $('#results').html();
if (verificando != data) {
$('#results').html(data);}
}
}
Did you try this instead ?
if ( verificando != data ){
$('#results').html(data);
}
You shouldn't need to concatenate <html> to the data while comparing it.
Such comparison is a overkill. You can basically check the ids associated to each result and then just refresh only the part which is new.
May be you can send the ids along with the ajax request itself and then filter out the response and send only which are the new results.

jquery bind functions and triggers after ajax call

function bindALLFunctions() {
..all triggers functions related go here
};
$.ajax({
type: 'POST',
url: myURL,
data: { thisParamIdNo: thisIdNo },
success: function(data){
$(".incContainer").html(data);
bindALLFunctions();
},
dataType: 'html'
});
I am new to ajax and JQuery.
I have the above ajax call in my js-jquery code. bindALLFunctions(); is used to re-call all the triggers and functions after the ajax call. It works all fine and good as expected. However, I have read somewhere that is better to load something after the initial action is finished, so I have tried to add/edit the following two without any success.
Any ideas?
1) -> $(".incContainer").html(data, function(){
bindALLFunctions();
});
2) -> $(".incContainer").html(data).bindALLFunctions();
Perhaps you should have a look to the live and delegate functions. You can set a unique event handler at the beggining of your app and all your loaded ajax code will be automatically binded:
$("table").delegate("td", "hover", function(){
$(this).toggleClass("hover");
});
But if you prefer to use Jquery.ajax call you have to do something like this:
$.ajax({
type: 'POST',
url: myURL,
data: { thisParamIdNo: thisIdNo },
success: function(data){
$(".incContainer").html(data);
bindALLFunctions(".incContainer");
},
dataType: 'html'
});
and transform bindALLFunctions as:
function bindALLFunctions(selector) {
..all triggers functions related go here. Example:
$('#foo', selector).bind('click', function() {
alert('User clicked on "foo."');
});
};
that will only bind events "under" the given selector.
Your initial code was fine. The new version does not work because html() function does not have a callback function.
It's hard to tell from your question just what you intend to ask, but my guess is that you want to know about the ready function. It would let you call your bindALLFunctions after the document was available; just do $(document).ready(bindALLFunctions) or $(document).ready(function() { bindALLFunctions(); }).

jQuery filtering AJAX data and then replaceWith data

I am calling pages via AJAX in jQuery.
The content of these pages needs to be filtered so that i only grab a certain DIV class. In this instance 'Section1'.
This filtered data needs to replace the same data on the current page in the DIV of the same class.
I currently have this but it is not really working for me:
$("#dialog_select").live('change', function() {
//set the select value
var $optionVal = $(this).val();
//$(this).log($optionVal);
$.ajax({
type: "GET",
url: $optionVal,
dataType: "html",
cache: false,
success: function(data) {
var $filteredData = $(data).filter('.Section1');
$('.Section1').replaceWith($filteredData);
},
error: function(xhr, ajaxOptions, thrownError) {
alert(xhr.status);
alert(thrownError);
}
});
});
I think your problem is likely here:
var $filteredData = $(data).find('.Section1');
$('.Section1').replaceWith($filteredData);
.filter() would only find top level elements in the response (if it's a page, that's <html>, and wouldn't have any results). .find() looks for decendant elements. Also keep in mind that if you have multiple .Section1 elements, this won't behave as expected and will have some duplication going on.
This is a tricky thing to do and I would recommend placing the data into something like a DOMParser or document.implementation.createDocument or MSXML.
if (window.DOMParser) {
parser=new DOMParser();
xmlDoc=parser.parseFromString(text,"text/xml");
}
else {
xmlDoc=new ActiveXObject("Microsoft.XMLDOM");
xmlDoc.async="false";
xmlDoc.loadXML(text);
}
is a basic code example. jQuery itself can filter on a selector with the load function. http://api.jquery.com/load/ This however has several limitations such as not being able to filter on html, head, or body tags. This is why the above method is safer.

Jquery: ajaxSend & ajaxStop events not working?

can't seem to get the ajaxSend and Stop to work... These are global variables no? .. but I should be able to use like this... but I never get an alert??
I wanted to use these events to display an ajax animation.. although in my code I wish to position the ajax animation depending on what I am doing a what element it is.
$.ajax({
type: "POST",
url: "MyService.aspx/TestMe",
data: JSON.stringify(data),
contentType: "application/json; charset=utf-8",
dataType: "json",
ajaxSend: function (r, s) {
alert('i am starting');
}
,
ajaxStop: function (r, s) {
alert('i am stopping');
}
,
success: function (msg) {
}
});
Those are globals, and the way I typically see them assigned is:
$('element').ajaxStart(function() {
... do something ...
}
Assigning them to a specific ajax request, I'm not sure if that will do what you want it to do.
So you don't use this functionality properly. ajaxStop, ajaxComplete and etc. is not a parameters of $.ajax function. Let say you have an ajax icon which you want to remove on request completion.
$("#ajax_icon").ajaxStop(function(){
$(this).hide();
});
You have a good reference here
PS. With other function is the same.

Resources