Localstorage setting through ajax data randomly gets undefined - javascript

I am setting items to localstorage using the below code. The issue I am getting is whenever I refresh the page some of the items are not set when I inspect the localstorage it shows as undefined, this is absolutely random and sometimes all items are set parfectly. How can I ensure that all the items are set without any undefined?
const catdata = [];
var catArr = JSON.parse(localStorage.getItem('cat'));
for (let i = 0; i < catArr.length; i++) {
const catid = catArr[i]['id'];
const catname = catArr[i]['name'];
$('#topitems').prepend('<div ><a class="topitems" href="'+catArr[i]['id']+'">'+catArr[i]['name']+'</a></div>');
(function(i) {
$.ajax( { url : "sales/item_search_cat?cat="+catid
, data : "GET"
// , async: "false",
, dataType : "json"
, success : function(data) {
catdata.push(data);
localStorage.setItem(catid,JSON.stringify(catdata[i]));
}
});
})(i);
}

The execution of the ajax requests are not sequentially executed, and sometimes your code is accessing an index that doesn't exist.
For example, one response arrives with index 2, the code pushes into the empty array at index 0, and catdata[i] is trying to get the index 2.
I think you have to call the ajax request using await or you can use the data directly:
localStorage.setItem(catid, JSON.stringify(data));
Possible solution (I didn't test it):
The execution is async, if you need to wait for the executions, you have to follow an async/await approach.
function callAjax(catid) {
$.ajax({
url: "sales/item_search_cat?cat=" + catid,
data: "GET",
dataType: "json",
success: function(data) {
localStorage.setItem(catid, JSON.stringify(data));
success(data);
}
});
}
let catArr = JSON.parse(localStorage.getItem('cat'));
catArr.forEach(function({id: catid, name: catname}) {
$('#topitems').prepend('<div ><a class="topitems" href="' + catid + '">' + catname + '</a></div>');
callAjax(catid);
});

Related

Cannot read property of undefined after JSON parse

I have done an ajax request in my code and it works good. After that I want to extract only the necessary info and re-post it to another script. Until now here is my code:
$.ajax({
type: "POST",
url: url,
data: {xhr_id: xhr_id},
success: function (jsondata) {
var product_data = [];
for (var i = 0; i <= 3; i++) {
//alert(jsondata.products[i].product_description.toSource());
product_data[i] = {};
product_data[i]["product" + i] = jsondata.products[i].product_description;
//alert(product_data[i]["product" + i].toSource());
}
},
dataType: "json"
});
The problem is that both the alerts work fine, displaying the information I want. However, I get an error message of "Uncaught TypeError: Cannot read property 'product_description' of undefined" which breaks the script and prevents me from doing anything else. What am I doing wrong, any ideas?
'product_description' of undefined" what it means is that your are trying to access property on undefined variable. That implies "jsondata.products[i]" resulted in undefined value which have occured due to index out of range.How many records are returned in jsondata 3 or 4,check and adjust the condition in for loop
The parameter in the success() function of $.ajax is a string. You have to put it through a parse function to make json. See your code below modified but not tested.
$.ajax({
type: "POST",
url: url,
data: {xhr_id: xhr_id},
success: function (jsondata) {
var oData;
try { oData=jQuery.parseJSON(jsondata) }
catch(err) {
alert("Problem parsing json string : " + jsondata)
return false
}
var product_data = [];
for (var i = 0; i <= 3; i++) {
//alert(oData.products[i].product_description.toSource());
product_data[i] = {};
product_data[i]["product" + i] = oData.products[i].product_description;
//alert(product_data[i]["product" + i].toSource());
}
},
dataType: "json"
});

AJAX - Wait for data to append before looping next

I am creating a drop down list in JavaScript, I am loading the data via Ajax and JSON, at the moment my code loops through a set of departments and runs into the ajax call in each iteration.
My problem is that my data seems to be appending in a random order, its likely that its loading in the order of whichever loads quickest.
I want to be able to loop through my Ajax call and append the data in the order that I declare (for each department).
is this something that can be done?
Here is my code:
//-- Ajax --
var departments = ['Accounts', 'Commercial', 'Installation', 'Production', 'Sales'];
var i;
for (i = 0; i < departments.length; i++) {
$.ajax({
type: "POST",
url: "Default.aspx/EmployeesDropDown",
data: '{X: "' + departments[i] + '"}',
contentType: "application/json; charset=utf-8",
dataType: "text json",
async: true,
success: createdropdown,
failure: function () {
alert("FAIL!");
}
});
}
//-- Creates dropdown --
function createdropdown(data) {
...appends all the data to my drop down list...
}
Any help or advice is appreciated, thank you in advance.
EDIT: This question is different from the related ones because I need to be able to loop through the string array, rather than just iterating based on numbers.
If you want to load the departments in the order they appear in the departments array you could load them one by one, waiting for each ajax request to finish until you start the next request.
Here is an example:
var departments = ['Accounts', 'Commercial', 'Installation', 'Production', 'Sales'];
var i = 0;
function reqDep(department) {
/*
Since i can't use ajax here let's use a promise.
*/
var p = new Promise(function(res, rej) {
setTimeout(function() {
res(department)
}, 1000)
})
return p;
// This is what you would actually do.
/*
var data = '{X: "' + department + '"}'
return $.ajax({
type: "POST",
url: "Default.aspx/EmployeesDropDown",
data: data,
contentType: "application/json; charset=utf-8",
dataType: "text json",
});
*/
}
function initDepartments(index) {
reqDep(departments[index])
// Here you would use `.done(function(data...`
// I am using `.then(function(data...`
// because of the promise.
.then(function(data) {
console.log(data)
if(i < departments.length) {
initDepartments(i)
}
})
i++;
};
initDepartments(i)

Use of async on successive AJAX calls

Question here about use of successive AJAX calls and async. Its a bit messed here because of how the data is set up. I need to return listings, but the sever only returns 10 per query, and the only way to determine the total number of listings is a separate query with the boolean returnTotal as true instead of false. This returns the number of listings only, and not the listing results themselves. However, if I run the calls synchronously, the variable startItem (which increments on each loop to load data starting at the next block of listings) doesn't seem to populate before the next call finishes, and results get duplicated. Any way to avoid running both as async? Apologies if my code is batshit ridiculous, as I'm relatively new to jquery.
$.ajax({
type: "POST",
url:server url here,
data:"creativeID=test&CompanyId=BHSR&StartItem=0&streetlocation="+choiceTown+"&Location="+sectCode+"&PriceMin="+choiceMin+"&PriceMax="+choiceMax+"&ListingType="+checkRB+"&OpenHouse=false&NewDev=false&AuthenticationId=id&ReturnTotal=true",
dataType: "html",
async: false,
success: function(data) {
data=convert(data);
$(data).find('Listing').each(function(){
$(this).find('total').each(function(){
totalList = $(this).text();
totalList = parseInt(totalList);
totalPages = totalList/10;
});
});
},
});
for (i = 0; i < totalPages; i++){
$.ajax({
type: "POST",
url:server url here,
data:"creativeID=test&CompanyId=BHSR&StartItem="+startItem+"&streetlocation="+choiceTown+"&Location="+sectCode+"&PriceMin="+choiceMin+"&PriceMax="+choiceMax+"&ListingType="+checkRB+"&OpenHouse=false&NewDev=false&AuthenticationId=id&ReturnTotal=false",
dataType: "html",
success: function(data) {
data=convert(data);
$(data).find('Listing').each(function(){
results_xml.push($(this));
});
result_index=0;
result_image_counter=1;
startItem = startItem + 10;
popResults();
},
});
}
The problem here is that you do not increment startItem until you receive a response. Your code is probably making multiple requests with startItem === 1 before the first response is even received, and so you will get some really weird behavior (probably will get duplicate responses, and you will only get the first few pages of data).
Avoid using synchronous calls because they will tie up other resources (like javascript).
In this case if you want to insure that you get the data in order, you can make it a serial chain of AJAX calls.
To get serial behavior and enjoy the benefits of AJAX, instead of using a loop make your callback function do the next AJAX request after incrementing startItem.
This is easier if you organize your code into functions. To wit:
function GetData()
{
$.ajax({
type: "POST",
url:server url here,
data:"creativeID=test&CompanyId=BHSR&StartItem="+startItem+"&streetlocation="+choiceTown+"&Location="+sectCode+"&PriceMin="+choiceMin+"&PriceMax="+choiceMax+"&ListingType="+checkRB+"&OpenHouse=false&NewDev=false&AuthenticationId=id&ReturnTotal=false",
dataType: "html",
success: GetData_Callback
});
}
function GetData_Callback(data)
{
data=convert(data);
$(data).find('Listing').each(function(){
results_xml.push($(this));
});
result_index=0;
result_image_counter=1;
startItem += 10; // increment startItem
popResults();
if (startItem / 10 < totalPages)
{
GetData(); // get next "page" of data
}
}
var startItem = 1; // global variable will be mutated by GetData_Callback
GetData(); // get first "page" of data
To do this in parallel typically requires management of the parallel responses (you can use semaphores, etc.). For example (psuedo code) you could do something like this:
var pages = [];
var totalPages = GetTotalPages(); // request via ajax like you mentioned (function not shown)
var pagesLoaded = 0;
for(var i = 0; i < totalPages; i++)
{
GetData(pageIdx);
}
function GetData(pageIdx)
{
$.ajax({ ..., success: function(data){GetData_Callback(pageIdx,data);}});
}
function GetData_Callback(pageIdx, data)
{
pages[pageIdx] = data; // assign this specific page of data
pagesLoaded++;
if (pagesLoaded === totalPages)
{
// fully loaded; trigger event or call function to render, etc.
}
}
Do you just mean without the async: false?
$.ajax({
type: "POST",
url:server url here,
data:"creativeID=test&CompanyId=BHSR&StartItem=0&streetlocation="+choiceTown+"&Location="+sectCode+"&PriceMin="+choiceMin+"&PriceMax="+choiceMax+"&ListingType="+checkRB+"&OpenHouse=false&NewDev=false&AuthenticationId=id&ReturnTotal=true",
dataType: "html",
success: function(data) {
console.log('test1'); // first response ok
data=convert(data);
$(data).find('Listing').each(function(){
$(this).find('total').each(function(){
totalList = $(this).text();
totalList = parseInt(totalList);
totalPages = totalList/10;
});
});
var startItem=0;
console.log(totalPages); // total page should be equal too "loop" logged
for (i = 0; i < totalPages; i++){
console.log('loop'); // enter the loop
$.ajax({
type: "POST",
url:server url here,
data:"creativeID=test&CompanyId=BHSR&StartItem="+startItem+"&streetlocation="+choiceTown+"&Location="+sectCode+"&PriceMin="+choiceMin+"&PriceMax="+choiceMax+"&ListingType="+checkRB+"&OpenHouse=false&NewDev=false&AuthenticationId=id&ReturnTotal=false",
dataType: "html",
success: function(data) {
console.log('test2'); // number of test 2 = nb of loop = totalPages
data=convert(data);
$(data).find('Listing').each(function(){
results_xml.push($(this));
});
result_index=0;
result_image_counter=1;
startItem = startItem + 10;
popResults();
},
});
}
},
});
The problem here is that you do not increment startItem until you receive a response. Your code is probably making multiple requests with startItem === 1 before the first response is even received, and so you will get some really weird behavior (probably will get duplicate responses, and you will only get the first few pages of data).
Try this instead. It still uses your loop but it increments startItem in the loop before the next request is made to insure that all pages of data are requested.
$.ajax({
type: "POST",
url:server url here,
data:"creativeID=test&CompanyId=BHSR&StartItem=0&streetlocation="+choiceTown+"&Location="+sectCode+"&PriceMin="+choiceMin+"&PriceMax="+choiceMax+"&ListingType="+checkRB+"&OpenHouse=false&NewDev=false&AuthenticationId=id&ReturnTotal=true",
dataType: "html",
async: false,
success: function(data) {
data=convert(data);
$(data).find('Listing').each(function(){
$(this).find('total').each(function(){
totalList = $(this).text();
totalList = parseInt(totalList);
totalPages = totalList/10;
});
});
},
});
var startItem = 1;
for (i = 0; i < totalPages; i++){
$.ajax({
type: "POST",
url:server url here,
data:"creativeID=test&CompanyId=BHSR&StartItem="+startItem+"&streetlocation="+choiceTown+"&Location="+sectCode+"&PriceMin="+choiceMin+"&PriceMax="+choiceMax+"&ListingType="+checkRB+"&OpenHouse=false&NewDev=false&AuthenticationId=id&ReturnTotal=false",
dataType: "html",
success: function(data) {
data=convert(data);
$(data).find('Listing').each(function(){
results_xml.push($(this));
});
result_index=0;
result_image_counter=1;
popResults();
},
});
// increment start item BEFORE the next request, not in the response
startItem += 10; // now the next request will be for 11, 21, 31, 41, etc...
}
You may want to get familiar with your javascript debugger to see the behavior for yourself.

Why is this $.ajax function successful only the second time it's called?

I'm experimenting with the Rotten Tomatoes API. On a click event, a search returns JSON data for the closest matched movie title and thumbnail image to the search input.
Upon completion of that first $.ajax request, a second $.ajax request is made, using var resultID, which was declared at the top and set upon 'success' of the first $ajax request.
As of now, when I click for the first time, it runs the first request successfully, but it can't run return json.total (upon 'complete') because the resultID is returned as 'undefined'. But the second time I click, resultID has apparently been set, since it will return json.total successfully.
I'm guessing this a simple problem related to how I'm declaring and calling resultID (or the asynchronous firing of ajax), but I'm not sure what it is. Can anyone explain?
var apiKey = '***********';
var resultID = "";
$('#search-submit').click(function(){
var queryText = $('#movie-search').val();
var queryURI = encodeURI(queryText);
var searchApiRequest = "http://api.rottentomatoes.com/api/public/v1.0/movies.json?q=" + queryURI + "&page_limit=10&page=1&apikey=" + apiKey;
var reviewApiRequest = "http://api.rottentomatoes.com/api/public/v1.0/movies/" + resultID + "/reviews.json?review_type=top_critic&page_limit=10&page=1&country=us&apikey=" + apiKey;
$.ajax({
url: searchApiRequest,
type: 'GET',
dataType: 'jsonp',
error: function() {
console.log('there was error in processing the search request.');
},
success: function(json) {
var movieInfo = json.movies[0];
var noResultsMessage = 'your search returned no matches. Please try again.';
var resultTitle = movieInfo.title;
var resultThumb = movieInfo.posters.thumbnail;
var resultDesc = movieInfo.critics_consensus;
var reviewsPublicURL = movieInfo.links.reviews;
var reviewsRequest = reviewsPublicURL;
resultID = movieInfo.id; // supposed to set global var resultID to movieID
$('.search-results').find('img').attr('src',resultThumb).end().find('h1').text(resultTitle).end().find('p').text(resultDesc);
},
complete: function() {
$.ajax({
url:reviewApiRequest,
type: 'GET',
dataType: 'jsonp',
error: function(json) {
console.log('for whatever reason, we could not find any reviews of this movie');
},
success: function(json) {
console.log(json.total);
}
});
}
});
});
You need to not only set a global resultID but also refresh a reviewApiRequest as well. It doesn't magically change its value by itself when you update resultID.

jQuery Ajax call in loop losing local variable reference

I am making several jQuery ajax calls within a loop. Each time one of the ajax calls return I need to reference a value corresponding to the original ajax call. My current code doesn't work properly, in that the value of the lskey variable has been altered by further loop iterations.
Here is the code:
for (var i = 0, len = localStorage.length; i < len; i++) {
var lskey = localStorage.key(i);
if (lskey.substr(0, 4) === 'form') {
var postdata = localStorage.getItem(lskey); // Get the form data
$.ajax({
type: "POST",
async: "false",
url: "/Profile/PostForm",
data: postdata,
success: function (data) {
$('#rollinginfo').append('<br>' + data + ',key=' + lskey);
localStorage.removeItem(lskey); // Remove the relevant localStorage entry
}
, error: function (data) { $('#rollinginfo').append('<br />ERR:' + data); }
});
}
}
The problem is that lskey is being altered each time the loop executes, and therefore the success callback does not have a reference to the value of lskey that existed at the time of the call.
How do I reference the correct value of lskey for each success callback?
for (var i = 0, len = localStorage.length; i < len; i++) {
var lskey = localStorage.key(i);
if (lskey.substr(0, 4) === 'form') {
var postdata = localStorage.getItem(lskey); // Get the form data
$.ajax({
type: "POST",
async: "false",
url: "/Profile/PostForm",
data: postdata,
local_lskey: lskey
success: function (data) {
$('#rollinginfo').append('<br>' + data + ',key=' + lskey);
localStorage.removeItem(this.local_lskey); // Remove the relevant localStorage entry
}
, error: function (data) { $('#rollinginfo').append('<br />ERR:' + data); }
});
}
}
This should work.
In the end I added the key info to the server posting, and then returned it from the server in JSON format so the success function could then simply refer to the key contained in the server response.
Have you considered chaining the AJAX calls? Basically you can make one AJAX call, process the result, modify lskey, etc. Then when you are ready, increment i and issue the second AJAX call. Loop this way instead of using the for loop...
You could put your ajax call into its own function and pass the lskey and postData values in. That way localStorage.removeItem(lskey) will refer to the lskey variable in the context of the function rather than the context of the loop.
Example
Declare the function -
function postForm(postdata, lskey) {
$.ajax({
type: "POST",
async: "false",
url: "/Profile/PostForm",
data: postdata,
success: function(data) {
$('#rollinginfo').append('<br>' + data + ',key=' + lskey);
localStorage.removeItem(lskey); // Remove the relevant localStorage entry
},
error: function(data) {
$('#rollinginfo').append('<br />ERR:' + data);
}
});
}
Then you can call your function from your loop -
for (var i = 0, len = localStorage.length; i < len; i++) {
var lskey = localStorage.key(i);
if (lskey.substr(0, 4) === 'form') {
var postdata = localStorage.getItem(lskey); // Get the form data
postForm(postdata, lskey);
}
}
You could also declare the function just before the loop (assigning it to a variable) and then call it within the loop.
var postForm = function(postdata, lskey) {
$.ajax({
type: "POST",
async: "false",
url: "/Profile/PostForm",
data: postdata,
success: function(data) {
$('#rollinginfo').append('<br>' + data + ',key=' + lskey);
localStorage.removeItem(lskey); // Remove the relevant localStorage entry
},
error: function(data) {
$('#rollinginfo').append('<br />ERR:' + data);
}
});
}
for (var i = 0, len = localStorage.length; i < len; i++) {
var lskey = localStorage.key(i);
if (lskey.substr(0, 4) === 'form') {
var postdata = localStorage.getItem(lskey); // Get the form data
postForm(postdata, lskey);
}
}

Categories

Resources