Ajax call in "for" loops skips odd/even iterations - javascript

If I am here asking it is because we are stuck on something that we do not know how to solve. I must admit, we already searched in StackOverflow and search engines about a solution.. but we didn't manage to implement it / solve the problem.
I am trying to create a JavaScript function that:
detects in my html page all the occurrences of an html tag: <alias>
replaces its content with the result of an Ajax call (sending the
content of the tag to the Ajax.php page) + localStorage management
at the end unwraps it from <alias> tag and leaves the content returned from ajax call
the only problem is that in both cases it skips some iterations.
We have made some researches and it seems that the "problem" is that Ajax is asynchronous, so it does not wait for the response before going on with the process. We even saw that "async: false" is not a good solution.
I leave the part of my script that is interested with some brief descriptions
// includes an icon in the page to display the correct change
function multilingual(msg,i) {
// code
}
// function to make an ajax call or a "cache call" if value is in localStorage for a variable
function sendRequest(o) {
console.log(o.variab+': running sendRequest function');
// check if value for that variable is stored and if stored for more than 1 hour
if(window.localStorage && window.localStorage.getItem(o.variab) && window.localStorage.getItem(o.variab+'_exp') > +new Date - 60*60*1000) {
console.log(o.variab+': value from localStorage');
// replace <alias> content with cached value
var cached = window.localStorage.getItem(o.variab);
elements[o.counter].innerHTML = cached;
// including icon for multilingual post
console.log(o.variab+': calling multilingual function');
multilingual(window.localStorage.getItem(o.variab),o.counter);
} else {
console.log(o.variab+': starting ajax call');
// not stored yet or older than a month
console.log('variable='+o.variab+'&api_key='+o.api_key+'&lang='+o.language);
$.ajax({
type: 'POST',
url: my_ajax_url,
data: 'variable='+o.variab+'&api_key='+o.api_key+'&lang='+o.language,
success: function(msg){
// ajax call, storing new value and expiration + replace <alias> inner html with new value
window.localStorage.setItem(o.variab, msg);
var content = window.localStorage.getItem(o.variab);
window.localStorage.setItem(o.variab+'_exp', +new Date);
console.log(o.variab+': replacement from ajax call');
elements[o.counter].innerHTML = content;
// including icon for multilingual post
console.log(o.variab+': calling multilingual function');
multilingual(msg,o.counter);
},
error: function(msg){
console.warn('an error occured during ajax call');
}
});
}
};
// loop for each <alias> element found
//initial settings
var elements = document.body.getElementsByTagName('alias'),
elem_n = elements.length,
counter = 0;
var i = 0;
for(; i < elem_n;i++) {
var flag = 0;
console.info('var i='+i+' - Now working on '+elements[i].innerHTML);
sendRequest({
variab : elements[i].innerHTML,
api_key : settings.api_key,
language : default_lang,
counter : i
});
$(elements[i]).contents().unwrap().parent();
console.log(elements[i].innerHTML+': wrap removed');
}
I hope that some of you may provide me some valid solutions and/or examples, because we are stuck on this problem :(
From our test, when the value is from cache, the 1st/3rd/5th ... values are replaced correctly
when the value is from ajax the 2nd/4th .. values are replaced
Thanks in advance for your help :)

Your elements array is a live NodeList. When you unwrap things in those <alias> tags, the element disappears from the list. So, you're looking at element 0, and you do the ajax call, and then you get rid of the <alias> tag around the contents. At that instant, element[0] becomes what used to be element[1]. However, your loop increments i, so you skip the new element[0].
There's no reason to use .getElementsByTagName() anyway; you're using jQuery, so use it consistently:
var elements = $("alias");
That'll give you a jQuery object that will (mostly) work like an array, so the rest of your code won't have to change much, if at all.

To solve issues like this in the past, I've done something like the code below, you actually send the target along with the function running the AJAX call, and don't use any global variables because those may change as the for loop runs. Try passing in everything you'll use in the parameters of the function, including the target like I've done:
function loadContent(target, info) {
//ajax call
//on success replace target with new data;
}
$('alias').each(function(){
loadContent($(this), info)
});

Related

How to access and use multiple data from json object? Do I need to make an array?

I am a beginner and using $.get to retrieve data from a rest API such as:
[{"id":"1","url":"http:\/\/123.456.78.910\/workforce\/images\/item1.jpg","price":"99","description":"Mobile Phone"},
{"id":"2","url":"http:\/\/123.456.78.910\/workforce\/images\/item2.jpg","price":"98","description":"Laptop"}
{"id":"3","url":"http:\/\/123.456.78.910\/workforce\/images\/item3.jpg","price":"92","description":"Console"}] }
$.get('http://xxxxxxxxxxx,
function (data) {
var obj = $.parseJSON(data);
So from what I understand I have retrieved the data from the REST API and parsed it so it is stored in a variable called obj.
My question is, how do I access and use each unique record in the obj variable?
Each record has it's own picture (item1.jpg, item2.jpg etc).
Whem my app loads I want it to show the item1.jpg image, and I want to be able to navigate to the other item pictures using buttons (previous / next).
I also want the description and price to be displayed underneath in some text input fields.
What I have figured so far is that I should:
Iterate through the obj variable, and store each record into an array.
Upon app initialisation I can set the default value for the image placeholder to array[index0].url, and set the description and price fields.
I can then set the previous and next buttons to array[currentIndex-1] or array[currentIndex+1].
Would this be the best way to do it?
Or can I just do this without using an array and manipulate the obj.data directly?
Thanks!!!
I may not be understanding what exactly what you want to do but I think I have the gist. If you just want to show the picture then the array of just images probably wouldn't be a bad idea. However, it looks like the Jason you're getting is already in an array. You can just use array index notation to get to what you want.
ie)
var arr = //your json response ;
var current = 0; //sets currently displayed object to the first in the array
var setCurrent = function () {
var image = arr[current]["url"];
}
You can then modify current however you want (on click on arrow iterate up/down, etc) then call the setCurrent function to set your image the the one you want. Hope that helps!
You can use the response you have from $.get() directly.
It is an array of objects.
You can use it like this:
console.log(data[2].description);
// outputs: "Console"
I've made a CodePen demo where it has a 4th object with a real image url to show you how to use the url info...
EDIT
Just in case you wouldn't know this:
You can use the response inside the scope of the $.get() callback...
You can not use it straith after the $.get() outside the callback since $.get() is asynchronous.
You can use it in some other handler wich will happen after the response is received.
var getResponse;
$.get('http://xxxxxxxxxxx', function (data) {
getResponse = data;
console.log(data[2].description);
// outputs: "Console"
console.log(getResponse[2].description);
// outputs: "Console"
});
console.log(getResponse[2].description);
// outputs: "Undefined"
// But since this handler will be triggered long after the response is obtained:
$("#somebutton").click(function(){
console.log(getResponse[2].description);
// outputs: "console"
});
In order for your page javascript to be able to access the data retrieved from your ajax request, you'll need to assign it to some variable which exists outside the callback function.
You will need to wait until the ajax request has been processed before you can read the array. So you might want to set the actual default image to be something that doesn't rely on the ajax request (a local image).
Here's a simple approach
// fake testing ajax func
function fakeget (url, callback) {
setTimeout(callback(JSON.stringify([
{"id":"1","url":"http:\/\/123.456.78.910\/workforce\/images\/item1.jpg","price":"99","description":"Mobile Phone"}, {"id":"2","url":"http:\/\/123.456.78.910\/workforce\/images\/item2.jpg","price":"98","description":"Laptop"},
{"id":"3","url":"http:\/\/123.456.78.910\/workforce\/images\/item3.jpg","price":"92","description":"Console"}
])), 1000);
}
// real code starts here
// global variables for ajax callback and setImg func to update
var imageData, currentImg;
// change this back to $.get for real
fakeget('http://xxxxxxxxxxx',
function (data) {
imageData = $.parseJSON(data);
setImg(0);
}
);
function setImg(index) {
// turns negative indices into expected "wraparound" index
currentImg = (index % imageData.length + imageData.length) % imageData.length;
var r = imageData[currentImg];
$("#theImg").attr('src', r.url);
$('#theDescription').text(r.price + " " + r.description);
}
$("#prev").click(function () {
setImg(currentImg - 1);
});
$("#next").click(function () {
setImg(currentImg + 1);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>
<img id='theImg' src='somedefault.jpg'>
<div id='theDescription'></div>
</div>
<button id='prev'>Prev</button>
<button id='next'>Next</button>
Few observations :
Your JSON Object is not a valid JSON.
No need to parse it again your data is already a JSON Object.
Working fiddle
var data = [{"id":"1","url":"http:\/\/123.456.78.910\/workforce\/images\/item1.jpg","price":"99","description":"Mobile Phone"},{"id":"2","url":"http:\/\/123.456.78.910\/workforce\/images\/item2.jpg","price":"98","description":"Laptop"}, {"id":"3","url":"http:\/\/123.456.78.910\/workforce\/images\/item3.jpg","price":"92","description":"Console"}];
for (var i in data) {
var imgUrl = data[i].url;
console.log(imgUrl);
}

Run JQuery when data is loaded in Firebase

i have a problem that i need first to get the image links from the Firebase data base then i call a JQuery code that will organize the images in a beautiful way >> But it seems that the Jquery runs before i get the images,
Help Please ..!
JS Function
new Firebase("https://zoominp.firebaseio.com/photos/"+imageID)
.once('value', function(snap)
{
link = snap.child('imageLink').val();
link = 'images/'+link;
var id = "img";
div.innerHTML += "";
});
JQuery
jQuery("#gallery").unitegallery(
{
tiles_type:"nested",
tiles_nested_optimal_tile_width:200
});
Firebase loads (and synchronizes) the data asynchronously. So the jQuery code you have, will indeed execute before the data has come back from the server.
To fix this, move the jQuery code into the Firebase callback:
var ref = new Firebase("https://zoominp.firebaseio.com/photos/"+imageID);
ref.on('value', function(snap) {
link=snap.child('imageLink').val();
link='images/'+link;
var id="img";
div.innerHTML = div.innerHTML +"";
jQuery("#gallery").unitegallery({
tiles_type:"nested",
tiles_nested_optimal_tile_width:200
});
});
I also changed once() to on(). With that tiny change, your HTML will be updated whenever the data in the database changes. Try changing the data and you'll experience the "magic" of Firebase.
Since asynchronous loading is hard to wrap your head around when you first encounter it, I highly recommend that you read the more in-depth answers to these questions:
How do I return the response from an asynchronous call?
Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference
Handling Asynchronous Calls (Firebase) in functions
Returning value from a jQuery function
i have never worked with a Firebase, but you will need to have your actual resources ready before running the jQuery - you cannot do this in a synchronous way, as when you call your jquery unitedGallery it is called before the .once('value') event triggers.
do you call that new Firebase(.... thing more times in a loop or something? you could do something like keeping information about whether have all the images loaded in an array. something like this: let's assume, your images are stored in an array allOfYourImages. then,
define a global variable like this
var images_loaded=[];
for(var i=0; i<allOfYourImages.length; i++){ images_loaded[i]=false; }
then i assume you somehow iterate over your pictures since you are using imageID. add an incrementing variable var image_number=0; before the iterator and do image_number++ after each image iteration. like
var image_number=0;
...iteratorofyourchoiseihavenoideawhatareyouusing...{
new Firebase("https://zoominp.firebaseio.com/photos/"+imageID).once('value', function(snap){
...DOM stuff previously did ...
images_loaded[image_number]=true;
checkAllImagesLoaded();
});
image_number++;
}
notice the checkAllImagesLoaded() function. this will look whether have all your images already loaded and fire the jQuery gallery thing, like this
checkAllImagesLoaded(){
var all_loaded=true;
for(var i=0; i<allOfYourImages.length; i++){
all_loaded &= images_loaded[i]; //in case any of the items is false, it will set the all_loaded to false
}
if(all_loaded){
..your jQuery.("#gallery").unitegallery stuff..
}
}

Javascript How to check if a call is being made, and kill it if it is

Using jQuery I'm writing a website api call in Javascript, which so far works pretty well. When a person updates a number in a text input it does a call to the API and updates a field with the response. It gets problematic however, when I user quickly makes a lot of changes. The javascript then seems to pile up all queries, and somehow does them side by side, which gives the field to be updated kind of a stressy look.
I think one way of giving the user a more relaxed interface, is to only start the API call after the user finished editing the input field for more than half a second ago. I can of course set a timeout, but after the timeout I need to check if there is not already a call under way. If there is, it would need to be stopped/killed/disregarded, and then simply start the new call.
First of all, does this seem like a logical way of doing it? Next, how do I check if a call is underway? And lastly, how do I stop/kill/disregard the call that is busy?
All tips are welcome!
[EDIT]
As requested, here some of the code I already have:
function updateSellAmount() {
$("#sellAmount").addClass('loadgif');
fieldToBeUpdated = 'sellAmount';
var buyAmount = $("#buyAmount").val();
var sellCurrency = $("#sellCurrency").val();
var buyCurrency = $("#buyCurrency").val();
var quoteURL = "/api/getQuote/?sellCurrency="+sellCurrency
+"&buyAmount="+buyAmount
+"&buyCurrency="+buyCurrency;
$.get(quoteURL, function(data, textStatus, jqXHR){
if (textStatus == "success") {
$("#sellAmount").val(data);
$("#sellAmount").removeClass('loadgif');
}
});
if (fieldToBeUpdated == 'sellAmount') {
setTimeout(updatesellAmount, 10000);
}
}
$("#buyAmount").on("change keyup paste", function(){
updateSellAmount();
});
If you make your AJAX call like this:
var myAjaxDeferred = $.ajax("....");
You can check it later with:
if (myAjaxDeferred.state() === "pending") {
// this call is still working...
}

jQuery and Ajax script

So i have a jQuery script that i should explain it line by line, i already do that and i want to make sure that is correct, so this is my script :
//Here we use the jQuery selector ($) to select the servers_id which is located into
//the delivers_id and we attaches a function to run when a change event occurs
$("#delivers #servers").change(function(){
//Here we look if the servers_id value was changed and the value is different of 0
if($(this).val() != '0') {
//Here we create a new variable sid and we stored the servers_id value in it
var sid = $("#delivers #servers").val();
//Here we use the Ajax $.get to get the sid value and send it by Ajax request then
//we set the data into the o_vmats_id html and empty the vmtas_id
$.get("/deliverability/get_vmtas/" + sid,
function(data) { $('#o_vmtas').html(data); $('#vmtas').html(''); });
}
//Here the else statement, we select the vmtas_id and set the html content like in the code (value=0)
//and empty the o_vmtas_id html content
else { $('#vmtas').html('<option value="0">All Classes</option>');
$('#o_vmtas').html(''); }
});
so please if someone has any remark i will be very appreciative
You're looking for the #servers element twice, no need for that. You can and should cache items that are going to be looked up more than once, so store that element in a var at the very beginning.
Other than that... there's not much to it, other than you wouldn't actually need much jQuery to do this :)

Ajax not firing on prototype - I think I need to unset or remove a javascript method - causing havoc help!

I'm working using scriptaculous library. However I'm facing some issues with inclusion of the JSON library for the prototype library. It adds a toJSONSTring and parseJSONSTRING method to all objects automatically and frankly this is causing havoc in places. Like I can't seem to use the Ajax Updater function and I suspect its because of this toJSONSTring method that has been attached to my options object which I pass to it.
Is there anyway to unset or atleast somehow remove a function which has been added to the Object.
EDIT:::
Actually I'm trying to make an ajax request and I'm facing an issue in the
Ajax.Updater = Class.create(Ajax.Request,....
part of the prototype library. At the part where its supposed to execute and post an AJAX request it doesn't - especially at:
$super(url, options);
To be precise I'm using this sortable and editable table grid here at this url:
http://cloud.millstream.com.au/millstream.com.au/upload/code/tablekit/index.html
Basically you clcik on a table cell to edit it and push the OK button to confirm. Upon clicking the button an ajax request is made.
The editable feature of the table calls the Ajax updater as follows in a submit function:
submit : function(cell, form) {
var op = this.options;
form = form ? form : cell.down('form');
var head = $(TableKit.getHeaderCells(null, cell)[TableKit.getCellIndex(cell)]);
var row = cell.up('tr');
var table = cell.up('table');
var ss = '&row=' + (TableKit.getRowIndex(row)+1) + '&cell=' + (TableKit.getCellIndex(cell)+1) + '&id=' + row.id + '&field=' + head.id + '&' + Form.serialize(form);
this.ajax = new Ajax.Updater(cell, op.ajaxURI || TableKit.option('editAjaxURI', table.id)[0], Object.extend(op.ajaxOptions || TableKit.option('editAjaxOptions', table.id)[0], {
postBody : ss,
onComplete : function() {
var data = TableKit.getCellData(cell);
data.active = false;
data.refresh = true; // mark cell cache for refreshing, in case cell contents has changed and sorting is applied
}
}));
},
The problem is that the request is never made and I keep pushing the OK button to no avail.
EDIT::::::::::::::::
I'm still stumped here - I've even tried calling the Ajax.Updater function on my own and it won't work at all. Its like this function has officially been rendered as useless all of a sudden. I've made the changes you said but all to no avail :( frankly I'm running out of options here - another idea would be to ditch this tablekit and look for something else which has similar functionality in the hopes that THAT MIGHT work!
It sounds like those methods are being added to the prototype of Object. By adding to Object's prototype, the library is automatically giving everything that derives from Object (in other words, everything) those methods as well. You might want to take do some reading on Prototypal inheritance in Javascript to get a better handle on this.
Anyway, you can remove those methods by doing this:
delete Object.prototype.toJSONString;
delete Object.prototype.parseJSONString;
You can delete anything from an object using "delete":
a.toJSON = function () {};
delete a.toJSON;
a.toJSON() => error: toJSON is not a function
a.toJSON => undefined
However I don't think that what is happening happens because of what you think is happening :) Maybe give more details on the problem you have with Ajax.Updater?
Seen the edit. OK, can you also post the actual line of code that calls Ajax.Updater and, also important, explain in detail how the options object you feed to it is made?
Also, please make sure you're doing something like this:
new Ajax.Updater(stuff)
and NOT just:
Ajax.Updater(stuff)
You NEED to create a new Updater object and use "new" (most probably you're already doing that, just making sure).
OK I'm still not sure what is getting passed to ajax.Updater since you extend stuff that I can't see, but try this: remove the "&" from the beginning of the variable "ss"; in the options object use parameters: ss instead of postBody: ss.
delete obj.property
In this case:
delete obj.toJSONSTring;
delete obj.parseJSONSTRING;

Categories