How to make a variable accessible outside a function? - javascript

I am fetching data from a json file using $.getJSON to get an id of a character and then use $.getJSON to fetch it from another page using the id that I go previously.
However, in the console it says
https://prod.api.pvp.net/api/lol/eune/v1.2/stats/by-summoner/undefined/summary?api_key=API_KEY_HERE
It should show the id where it says unidentified - /by-summoner/undefined/summary
This is my current script:
var input = "netuetamundis";
var sID;
$(document).ready(function () {
// get json from this page to get the ID of the input
$.getJSON("https://prod.api.pvp.net/api/lol/eune/v1.1/summoner/by-name/" + input + "?api_key=API_KEY_HERE", function (name) {
obj = name;
sID = obj.id;
console.log(sID);
});
$.getJSON("https://prod.api.pvp.net/api/lol/eune/v1.2/stats/by-summoner/" + sID + "/summary?api_key=API_KEY_HERE", function (stats) {
console.log(stats);
});
});
When I googled, it said to declare the variable outside the function, which I did as you can see in the code, however it still doesn't work.

Your variable declarations and their scope are correct. The problem you are facing is that the first AJAX request may take a little bit time to finish. Therefore, the second URL will be filled with the value of sID before the its content has been set. You have to remember that AJAX request are normally asynchronous, i.e. the code execution goes on while the data is being fetched in the background.
You have to nest the requests:
$.getJSON("https://prod.api.pvp.net/api/lol/eune/v1.1/summoner/by-name/"+input+"?api_key=API_KEY_HERE" , function(name){
obj = name;
// sID is only now available!
sID = obj.id;
console.log(sID);
});
Clean up your code!
Put the second request into a function
and let it accept sID as a parameter, so you don't have to declare it globally anymore!
(Global variables are almost always evil!)
Remove sID and obj variables - name.id is sufficient unless you really need the other variables outside the function.
$.getJSON("https://prod.api.pvp.net/api/lol/eune/v1.1/summoner/by-name/"+input+"?api_key=API_KEY_HERE" , function(name){
// We don't need sID or obj here - name.id is sufficient
console.log(name.id);
doSecondRequest(name.id);
});
/// TODO Choose a better name
function doSecondRequest(sID) {
$.getJSON("https://prod.api.pvp.net/api/lol/eune/v1.2/stats/by-summoner/" + sID + "/summary?api_key=API_KEY_HERE", function(stats){
console.log(stats);
});
}
Hapy New Year :)

$.getJSON is an asynchronous request, meaning the code will continue to run even though the request is not yet done. You should trigger the second request when the first one is done, one of the choices you seen already in ComFreek's answer.
Alternatively you could use jQuery's $.when/.then(), similar to this:
var input = "netuetamundis";
var sID;
$(document).ready(function () {
$.when($.getJSON("https://prod.api.pvp.net/api/lol/eune/v1.1/summoner/by-name/" + input + "?api_key=API_KEY_HERE", function () {
obj = name;
sID = obj.id;
console.log(sID);
})).then(function () {
$.getJSON("https://prod.api.pvp.net/api/lol/eune/v1.2/stats/by-summoner/" + sID + "/summary?api_key=API_KEY_HERE", function (stats) {
console.log(stats);
});
});
});
This would be more open for future modification and separates out the responsibility for the first call to know about the second call.
The first call can simply complete and do it's own thing not having to be aware of any other logic you may want to add, leaving the coupling of the logic separated.

Related

How to get a callback on an Ajax function

I am trying to find out if a text file (note) exists on the server.
I want to return "yes" if it does, "no" if it does not.
I am able to get this into an alert successfully (for each of the 7 occurrences) but I want to return it back into the originating html doc so I can use it in future code. I am reading about callbacks on this site, but not sure how to implement it.
I tried adding some callback code to the success function, that I saw in an example elsewhere here but am unsure how to edit my function call:
tmpNoteFileSun is a text string that matches the format of the text files stored on the server.
The function call (there are 7 of these in separate places, 1 for each day of the week):
CheckNoteExist(tmpNoteFileSun);
var DoesTheNoteExist = ""; //code needs to go here that returns noteexists (as in the alert below).
I tried changing the above to:
var DoesTheNoteExist = CheckNoteExist(tmpNoteFileSun);
console.log("Does Note Exist " + DoesTheNoteExist);
But get undefined in the console.
The Ajax Function:
function CheckNoteExist(ThisNoteName, callback) {
var NoteFileName = ThisNoteName;
// Ajax to call an external php file, pass the notes filename to it and check if the file
// exists. If it does, change noteexists variable to Yes", else it is "no".
$.ajax({
url: 'ajaxfile_note_exists.php',
type: 'GET',
data: {NoteFileName: NoteFileName},
success: function(noteexists) {
alert("Does the note exist: " + noteexists);
callback && callback(noteexists);
}
});
}
The external PHP file:
$filename = "upload/" . $_GET['NoteFileName'];
if (file_exists($filename)) {
$noteexists = "yes";
}
else {
$noteexists = "no";
}
echo $noteexists;
?>
You're not using the callback, that's what it's there for.
CheckNoteExist(ThisNoteName, val => console.log("Does Not Exist " + val));
See also How do I return the response from an asynchronous call? and Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference

callback function removes two items instead of single one

What is wrong here:
function deluser() {
var id = $('.nameact').attr('data-id');
$.post('users-pro-del.php', {id}, function(data) {
$('.nameact').remove();
$('.namesingle').eq(0).addClass('nameact');
});
}
users-pro-del.php - deletes a user from database.
So I want to remove the corresponding item from screen
$('.nameact').remove();
And give the active status to another (the first) item:
$('.namesingle').eq(0).addClass('nameact');
Sometimes it works fine.
But more often - $('.namesingle').eq(0) is also removed !
As I can see in console - there is only one single .nameact at each moment.
Any help?
Ajax has asynchronous nature. id = $('.nameact') before $.post may be differ from the $('.nameact') after ajax request. It's better to refer nameact by Id everywhere.
function deluser() {
var id = $('.nameact').attr('data-id');
console.log('id to delete: ' + id);
$.post('users-pro-del.php', {id}, function(data) {
$('.nameact[data-id="' + id + '"]').remove(); // to be sure we delete the same object in php and JS
console.log('deleted id: ' + id);
$('.namesingle').eq(0).addClass('nameact');
});
}

Cannot access elements in object returned by Javascript Function in AngularJS

Thanks to #asgoth, I am able to use AngularJS $http service to retrieve stock prices from Yahoo as described here: Cannot read response from AngularJS $resource JSONP get from Yahoo Finance
In the "getHistoricalPrice" function, it puts the price inside an array, which is inside an object. From inside that function, I am able to access the price and write it to console.
The function returns the object to where it is called from. From there, I can successfully write the entire object out to console. However, I cannot access the elements of this object. I tried many different ways, but still cannot access the data in the object. You can see the code at http://jsfiddle.net/curt00/LTazR/2/ or below:
angular.module('app', ['ngResource']);
function AppCtrl($scope, $http, $resource) {
var historical_price = getHistoricalPrice("AAPL", 'start date is hard coded', 'end date is hard coded');
console.log("after calling historical price: ", historical_price); // historical_price is an object and all of the correct data is outputted to console here, but I cannot access its elements directly from Javascript.
for(var key in historical_price) {
console.log("key =",key); // this outputs "key = list"
}
console.log("after calling getHistoricalPrice: ", historical_price.list[0][1]); // Cannot access this as browser console gives error: TypeError: Cannot read property '1' of undefined
console.log("after calling getHistoricalPrice: ", historical_price['list'][0][1]); // Cannot access this as browser console gives error: TypeError: Cannot read property '1' of undefined
console.log("after calling getHistoricalPrice: ", historical_price[0][1]); // Cannot access this as browser console gives error: TypeError: Cannot read property '1' of undefined
function getHistoricalPrice(symbol, start, end) {
var query = 'select * from csv where url=\'http://ichart.yahoo.com/table.csv?s=' + symbol + '&a=' + '11' + '&b=' + '19' + '&c=' + '2012' + '&d=' + '11' + '&e=' + '19' + '&f=' + '2012' + '&g=d&ignore=.csv\'';
var url = 'http://query.yahooapis.com/v1/public/yql?q=' + fixedEncodeURIComponent(query) + '&format=json&callback=JSON_CALLBACK';
var histData = {};
$http.jsonp(url, {timeout: 30000}).success(function(json) {
var list = [];
var result = json.query.results.row;
result.shift(); // remove the header (columns) row
angular.forEach(result, function(row) {
list.push([(new Date(row.col0)).getTime()/1000, parseFloat(row.col4)]);
});
list.sort(function(val1, val2) {
return val1[0] - val2[0];
});
histData.list = list;
console.log('Loaded historical data',histData.list[0][1],', for ' + symbol); // This works and gives the price
});
return histData;
}
var fixedEncodeURIComponent = function(str) {
return encodeURIComponent(str).replace(/[!'()]/g, escape).replace(/\*/g, "%2A");
};
}
​
Any help or suggestions to solve this problem is greatly appreciate!
It's a matter of timing.
In lines 12-14 you are trying to access histData.list before it has been populated. This is because this code is run before the success callback to the $http.jsonp function is executed.
Any code that depends on that callback being completed must be in the callback or in a function called in the callback.
See my answer on https://stackoverflow.com/a/13967709/1916258
A great way to debug the Yahoo api is using the YQL Console: http://developer.yahoo.com/yql/console/
Info about the different posibilities (which stock info) can be found on http://www.gummy-stuff.org/Yahoo-data.htm
Edit: there was still a problem with function fixedEncodeURIComponent. It should encode quotes (") too:
var fixedEncodeURIComponent = function(str) {
return encodeURIComponent(str).replace(/[!'()]/g, escape).replace(/\*/g, "%2A").replace(/\"/g, "%22");
};
BobS is right, you aren't timing things correctly. Also you declared fixedEncodeURIComponent after you had called it. This was resulting in an immediate error when I loaded up the jsfiddle.
While you were passing the callback through to your function correctly, you weren't actually calling it. I stripped out all the post processing of the json as you have some other errors involving the query and just implemented the callback so you can see it working.
After the request is finished and you're still in the success function you need to add
if(typeof(callback) === "function"){
callback();
}
This calls that function you passed in and runs it. Here is a working jsFiddle of it:
http://jsfiddle.net/LTazR/22/
I also updated a new variable i created call output so you can see it changing.
Thanks to everybody for providing suggestions.
I solved the problem by using AngularJS' $scope variable, such as $scope.symbol[user].price. I created this variable before calling the getHistoricalPrice function and then in that function, after the result is returned from $http.jsonp, I put the value into the $scope variable, as such:
$scope.symbol[user].price = row.col4;

Javascript: Set the order of functions

I'm writing a titanium app but I'm having an issue with the execution order of my javascript.
I have an event listener on a button. It's a reload button that clears a table, uses HTTPClient to GET a JSON array of 'appointments', saves each appointment, and refreshes a table list. The problem is I am executing the table delete first which should clear the table, then I get the appointments but when the app refreshes the datatable it's like it's doing it too soon and the new appointments haven't been saved yet because I'm getting an empty list. Now if I comment out the db.deleteAll line, each time I click reload the list is refreshed with the new (and existing) appointment data.
I need to make sure everything is done in order and only when the previous task is dfinished. So appointments.download() has to be executed AFTER db.DeleteAll and the list refresh has to be executed AFTER var allAppointments = db.All();
I think the problem is that the appointments.download() function has to make a HTTP GET call and then save the results and the other functions are not waiting until it's finished.
Here is the code:
btnReload.addEventListener('click', function(e){
var affected = db.deleteAll();
appointments.download();
var allAppointments = db.all();
Ti.API.info(allAppointments);
appointmentList.setData(allAppointments);
});
Here are the functions that are being called:
db.deleteAll():
api.deleteAll = function(){
conn.execute('DELETE FROM appointments');
return conn.rowsAffected;
}
appointments.download():
var appointments = (function() {
var api = {};
api.download = function(){
var xhr = Titanium.Network.createHTTPClient();
xhr.onload = function()
{
var data = JSON.parse(this.responseText);
var dl = (data.length);
for(i=0; i<dl;i++)
{
//p = addRow(data,i); // returns the **arr array
//Ti.API.info('Saving : '+data[i].first_name);
var contact_name = data[i].first_name + ' ' + data[i].last_name;
var start_date = data[i].start_date;
var reference = data[i].reference;
var comment = data[i].comment;
var appointment_id = data[i].quote_id;
var lastid = db.create(appointment_id, start_date, reference, contact_name, comment);
//Ti.API.info(lastid);
}
};
xhr.open('GET','http://********.co.uk/appointments/download/');
xhr.send();
return;
}
Any help most appreciated!
Billy
Synchronous calls give you coordination (code won't execute until any computation it depends on finishes) for free. With asynchronous calls, you have to take care of coordination. This generally means passing the dependent code as a function to the asynchronous code. The passed code is known as a "continuation", which means "the rest of the calculation, from a given point forward". Passing continuations around is known as (unsurprisingly) "continuation passing style".
To rewrite code in CPS, identify the point(s) where you need to coordinate the code (the call to appointments.download), then wrap the rest of the code in a function.
btnReload.addEventListener('click', function(e){
var affected = db.deleteAll();
appointments.download();
function () {
var allAppointments = db.all();
Ti.API.info(allAppointments);
appointmentList.setData(allAppointments);
}
});
In the general case, the return value becomes the argument to the continuation. Here, no return value for appointments.download is used, so the continuation takes no arguments.
Next, rewrite the asynchronous function to take the continuation and pass the continuation in the call.
btnReload.addEventListener('click', function(e){
var affected = db.deleteAll();
appointments.download(
function () {
var allAppointments = db.all();
Ti.API.info(allAppointments);
appointmentList.setData(allAppointments);
});
});
...
api.download = function(_return){
var xhr = Titanium.Network.createHTTPClient();
xhr.onload = function() {
var data = JSON.parse(this.responseText);
var dl = (data.length);
for (i=0; i<dl;i++) {
//p = addRow(data,i); // returns the **arr array
//Ti.API.info('Saving : '+data[i].first_name);
var contact_name = data[i].first_name + ' ' + data[i].last_name;
var start_date = data[i].start_date;
var reference = data[i].reference;
var comment = data[i].comment;
var appointment_id = data[i].quote_id;
var lastid = db.create(appointment_id, start_date, reference, contact_name, comment);
//Ti.API.info(lastid);
}
_return();
};
xhr.open('GET','http://********.co.uk/appointments/download/');
xhr.send();
return;
}
The continuation is named _return because the return statement can be modeled as a continuation (the default continuation). Calling _return in the asynchronous version would have the same affect as calling return in the synchronous version.
Currently you are making requests asynchronously which means you make a request and return from the function immediately, you don't wait for an answer. You should make your calls synchronous, I don't know what your conn and xhr really are but they might provide ways to make the execute() and send() methods synchronous. For example if you set the third argument of JavaScript's own XMLHttpRequest's open() method to false then send() method will not return until a response is received from the server, your connection classes might have the same option.
Move the call to delete the current appointments into the onload handler. That way you will delete the old and immediately add the new data.

Nested getJson() & PHP PDO

I have looked at other questions and answers regarding this, but can't seem to wrap my head around it...
I have a javascript function:
function getStates(theDiv){
var stateGroupData;
var stateData;
var theGHtml = "";
var theHtml = "<h4>MyPage</h4>";
theHtml = theHtml+"<h5>select a state...</h5>";
$.getJSON("getStateGroups.php", function(data) {
stateGroupData = data;
theHtml = theHtml+"<ul>";
$.each(stateGroupData, function(i,jsonData) {
theHtml = theHtml+"<li><a href='#"+jsonData.groupName+"'>"+jsonData.groupID+"</a></li><br/>";
var theSQL = "getStates.php?gid="+jsonData.groupName;
theGHtml = theGHtml+"<div id='"+jsonData.groupName+"'>";
$.getJSON(theSQL, function(data2) {
stateData = data2;
$.each(stateData, function(i,jsonData2) {
alert(jsonData2.stateName);
theGHtml = theGHtml+"<span sname='"+jsonData2.stateName+"' lat='"+jsonData2.centerLat+"' lon='"+jsonData2.centerLon+"' zom='"+jsonData2.zoom+"'>"+jsonData2.stateName+"</span> ";
});
});
theGHtml = theGHtml+"</div>";
});
theHtml = theHtml+"</ul>";
});
theDiv.html = theHtml+theGHtml;
}
The second (ie. nested) getJson does not return any thing... Both PHP files just use PDO to request data from the SAME table. I run the SQL in each file without any issues, so the SQL seems OK.
Is this an sync v. async issue with the calls to getJson?
Is this an sync v. async issue with
the calls to getJson?
Probably. I think this is your problem:
stateData = data2;
Try changing that to:
var stateData = data2;
The first one sets a global variable. The second one sets a variable that is local to that function.
You might benefit from refactoring this whole process such that you only need to make one AJAX call. It looked like you were pulling individual people associated with a group. You'd get better performance on the server from a single script which can, when needed, return people associated with the group but otherwise just returns the group.
Remember, every AJAX call is another hit to your server.

Categories

Resources