array is empty even if it was successfully mapped - javascript

I'm running into issues with _.map (using underscore.jshttp://underscorejs.org).
getCalories: function() {
var encode = "1%20";
var calSource = "https://api.edamam.com/api/nutrition-data?app_id=#&app_key=#";
_.map(ingArray, function(elem)
{
return $.ajax(calSource, {
dataType: "json",
jsonp: "jsonp",
data: "ingr=" + encode + elem,
complete: function(r) {
var obj = JSON.parse(r.responseText);
var calorie = obj.calories;
calArray.push(calorie);
console.log(calArray);
}
});
});
},
I need to use the latest iteration of calArray in another function. However, it always comes up as undefined. So I inserted a console.log above and this is what I get:
app.js:177 is the console.log
Is this a scoping issue? Also, if it's logging prior to the push then I can see why it's coming up as undefined. How do I get around it?

I believe underscore's map produces a new array, in your case the new array will contain a bunch promises (ajax-requests)
You may want to assign this to a variable first, something like below:
getCalories: function () {
var encode = "1%20";
var calSource = "https://api.edamam.com/api/nutrition-data?app_id=#&app_key=#";
var requests = _.map(ingArray, function(elem) {
return $.ajax(calSource, {
dataType: "json",
jsonp: "jsonp",
data: "ingr=" + encode + elem
});
});
$.when.apply($, requests).then(function(results) {
console.log(results); // can you take a screenshot of this output
var calories = _.map(results, function(result) {
return JSON.parse(result.responseText).calories;
});
calArray = calArray.concat(calories);
});
}

Related

"Error : Cannot use 'in' operator to search for 'length' in [{"ID":"2","Name":"EAA2"}]" when performing $.each

What ever I do, I keep getting the same error. The only thing I have found that might of helped is the JSON.parse, but I still get the same problem. console log gives data as [{"ID":"2","Name":"EAA2"}]
I split it into two functions as I didn't want to keep going back to the api everytime a user selects/de-selects an option.
I have also tried the following:
Changing vars to lets
Passing data.d from the update to the populate
function populateAvailableAuthorities() {
var list = $('#availableAA');
var data = JSON.parse($('#AAJSON').val());
var auths = $('#tbSelectedAA').val();
list.empty();
$.each(data, function (key, entry) {
if (!~auths.indexOf(entry.ID + ';')) {
list.append($('<option></option>').attr('value', entry.ID).text(entry.Name));
}
});
}
function updateListboxes() {
var teams = '';
let aa = $('#AAJSON');
aa.empty();
$('#cblTeams input:checked').each(function () {
teams += $(this).attr('value') + ',';
});
if (teams.length > 1) {
teams = teams.substr(0, teams.length - 1);
$.ajax({
type: "POST",
url: '<%# ResolveUrl("~/api/Authorities.asmx/FetchByTeam") %>',
data: '{teams: "' + teams + '"}',
dataType: 'json',
contentType: "application/json; charset=utf-8",
success: function (data) {
aa.val(JSON.stringify(data.d));
populateAvailableAuthorities();
}
});
}
}
It would seem that "over-stringifying" is an issue with JSON. If I doubled the JSON.parse or removed the JSON.stringify it all works correctly.
Annoying!!

Fill array by multiple AJAX requests, then pass array to another function

(My solution below)
I have several HTML elements with class .canvas-background of which information is stored in the database. I want to get the information of each element and process it via JavaScript. But somehow I can't pass the response of the AJAX request to another function. Here is what I've tried:
function initTabs() {
var tabs = loadTabInformation();
console.log(tabs); // (1)
// do something else
}
function loadTabInformation() {
var requests = new Array();
var tabs = new Object();
var counter = 0;
$(".canvas-background").each(function () {
var tabNumber = $(this).data("tab-number");
var request = $.ajax({
type: 'POST',
url: '../db/GetTabInformation.ashx',
data: String(tabNumber),
dataType: 'json',
contentType: 'text/plain; charset-utf-8'
})
.done(function (response) {
tabs[counter++] = response;
}).fail(function (jqXHR, textStatus, errorThrown) {
console.log("request error in loadTabInformation()");
console.log(textStatus);
console.log(errorThrown);
});
requests.push(request);
});
$.when.apply($, requests).done(function () {
console.log(tabs); // (2)
return tabs;
});
}
At (1) I get undefined, but at (2) everything seems to be alright.
THE SOLUTION:
Thanks to the answer and the link in the comment #Kim Hoang provided I got this working. The clue seemed to put the done() function in the calling function, that is initTabs() in my case. Another thing I got wrong was to try to do the logic that should be executed after the AJAX requests had finished outside the done callback function. They must be inside (makes sense, if you think about it). And a lot of conosle output helped, to see what function returns what kind of object.
function initTabs() {
var tabInfoRequest = loadTabInfo();
tabInfoRequest[0].done(function() {
var results = (tabInfoRequest[1].length > 1) ? $.map(arguments, function(a) { return a[0]; }) : [arguments[0]];
for (var i = 0; i < results.length; i++) {
// do something with results[i]
}
});
}
function loadTabInfo() {
var tabNumbers = new Array();
$(".canvas-background").each(function () {
tabNumbers.push($(this).data("tab-number"));
});
var requests = $.map(tabNumbers, function (current) {
return $.ajax({
type: 'POST',
url: '../db/GetTabInformation.ashx',
data: String(current),
dataType: 'json',
contentType: 'text/plain; charset-utf-8'
});
});
var resultObject = new Object();
resultObject[0] = $.when.apply($, requests);
resultObject[1] = requests;
return resultObject;
}
Note: I only did the resultObject-thing because I needed the array requests in the initTabs() function.
Thank you very much for helping me!
You do not return anything in loadTabInformation, so of course you will get undefined. You should do it like this:
function loadTabInformation() {
...
return $.when.apply($, requests);
}
function initTabs() {
loadTabInformation().done(function (tabs) {
console.log(tabs); // (1)
// do something else
});
}

jQuery updating class variable values not working

I am writing a class in JavaScript for the first time and I am having some trouble writing new data to a class variable. I've been trying all sorts for hours but nothing seems to work!
function ClassName(productId) {
//create variables
this.productId = productId;
this.shop = [];
this.product = [];
//method that calls for response. On success will return {"status" : "success", "shop" : "someshop.com"}
this.auth = function() {
$.ajax({
url: "http://website.com/api/auth/",
dataType: "jsonp",
success: function(data) {
authCallback(data); //use callback to handle response
},
error: function() {
console.log("bad auth");
}
});
}
var authCallback = function(r) {
//using console.log(r) output the response OK
this.shop = r; //this runs with no errors
}
}
Now, as yo can see in the authCallback method I'm setting this.shop = r; but then if i refer back to this variable its still at its default value of [] .
var class = new ClassName(1);
class.auth();
console.log(class.shop); //this outputs []
I've also tried this in the Javascript console writing each line after each stage had been completed(waited for a response from class.auth() and output from authCallback() before then calling console.log(class.shop);
So, what am I doing wrong? Why isn't the variable updating to its new value?
When you just write:
authCallback(data);
then within authCallback you will have the wrong value of this, it'll either be null or the global object (depending on whether you're in strict mode or not).
Use:
success: authCallback.bind(this)
to ensure that this inside the callback actually represents your object.
You should also note that you cannot access this.shop until after the callback has completed. A more idiomatic implementation using modern jQuery techniques would be this:
this.auth = function() {
return $.ajax({
url: "http://website.com/api/auth/",
dataType: "jsonp"
}).done(this.authCallback.bind(this)).fail(function() {
console.log("bad auth");
});
};
this.authCallback = function(r) {
this.shop = r;
return this;
}
followed by:
var clazz = new ClassName(1);
clazz.auth().then(function(c) {
console.log(c.shop);
});

Passing array to ajax

How I can pass a list or array to ajax?
var intArray = [];
$.ajax({
url: '/User/GetGroup',
type: 'GET',
data: intArray,
traditional: true,
success: function (result) {
$(result).each(function () {
var id = this.Id;
var nome = this.Nome;
$("#Default").append($('<option></option>').val(id).html(nome));
});
}
});
Like this way, still doesn't work.
Thanks!
You can pass the array like that, or use data: JSON.stringify(intArray). In ASP.NET MVC, we use the JSON 2 javascript library, available in a Nuget package.
success: function (serverResult) {
// create an object array from json string.
var results = JSON.parse(serverResult);
for ( var item in results){
$("#Default").append($('<option></option>').val(item.Id).html(item.Nome));
}
}
I used data: { i: intArray }, and works too!

Issue with code inner variable

I have a code like the one stated below, please how do I get the value for (getData), using a code like:
var instanceArray = myGraph.getInstances(component)
I was thinking myGraph.getInstances(component).getData will do it, but it failed
this.getInstances = function(component) {
var getData = {};
$.ajax({
url: "/rpc/alerts2/commonObj_rpc.cfc?method=getInstances",
data: {"component":component},
type: "POST",
async: true,
success: function(data) {
getData = $.parseJSON(data);
console.log("hey");
var $render_component_instance = $("#instances").empty();
$("#instances").append($("<option />").val("all").text("All Instances (Summed)"));
$.each(getData, function (cIndex, cItem){
var $instance = $("<option />").val(cItem.si_instance).text(cItem.si_label.toUpperCase());
$render_component_instance.append($instance);
})
$("#instances").multiselect("refresh");
}
});
};`
You can't, the get is asynchronous. getInstances returns before the GET completes, so it's impossible for getInstances to return the data. (See further note below.)
You have (at least) three options:
Use a callback
Return a blank object that will get populated later, and have the code that needs it poll it periodically
Use a synchronous get (not a good idea)
1. Use a callback
What you can do instead is accept a callback, and then call it when the data arrives:
this.getInstances = function(component, callback) {
$.ajax({
url: "/rpc/alerts2/commonObj_rpc.cfc?method=getInstances",
data: {"component":component},
type: "POST",
async: true,
success: function(data) {
var getData = $.parseJSON(data);
console.log("hey");
var $render_component_instance = $("#instances").empty();
$("#instances").append($("<option />").val("all").text("All Instances (Summed)"));
$.each(getData, function (cIndex, cItem){
var $instance = $("<option />").val(cItem.si_instance).text(cItem.si_label.toUpperCase());
$render_component_instance.append($instance);
})
$("#instances").multiselect("refresh");
callback(getData);
}
});
};
And call it like this:
myGraph.getInstances(component, function(data) {
// Use the data here
});
2. Return a blank object that will get populated later
Alternately, you can return an object which will be blank to start with, but which you'll add the data to as a property later. This may be closest to what you were looking for, from your comments below. Basically, there's no way to access a function's local variables from outside the function, but you can return an object and then add a property to it later.
this.getInstances = function(component) {
var obj = {};
$.ajax({
url: "/rpc/alerts2/commonObj_rpc.cfc?method=getInstances",
data: {"component":component},
type: "POST",
async: false, // <==== Note the change
success: function(data) {
var getData = $.parseJSON(data);
console.log("hey");
var $render_component_instance = $("#instances").empty();
$("#instances").append($("<option />").val("all").text("All Instances (Summed)"));
$.each(getData, function (cIndex, cItem){
var $instance = $("<option />").val(cItem.si_instance).text(cItem.si_label.toUpperCase());
$render_component_instance.append($instance);
})
$("#instances").multiselect("refresh");
// Make the data available on the object
obj.getData = getData;
}
});
return obj; // Will be empty when we return it
};
And call it like this:
var obj = myGraph.getInstances(component);
// ...later...
if (obj.getData) {
// We have the data, use it
}
else {
// We still don't have the data
}
3. Use a synchronous get
I do not recommend this, but you could make the call synchronous. Note that synchronous ajax requests will go away in a future version of jQuery. But just for completeness:
this.getInstances = function(component) {
var getData;
$.ajax({
url: "/rpc/alerts2/commonObj_rpc.cfc?method=getInstances",
data: {"component":component},
type: "POST",
async: false, // <==== Note the change
success: function(data) {
var getData = $.parseJSON(data);
console.log("hey");
var $render_component_instance = $("#instances").empty();
$("#instances").append($("<option />").val("all").text("All Instances (Summed)"));
$.each(getData, function (cIndex, cItem){
var $instance = $("<option />").val(cItem.si_instance).text(cItem.si_label.toUpperCase());
$render_component_instance.append($instance);
})
$("#instances").multiselect("refresh");
}
});
return getData;
};
And call it like this:
var getData = myGraph.getInstances(component);
But again, I don't advocate that. Synchronous ajax calls lock up the UI of the browser, leading to a bad user experience.

Categories

Resources