I'm working on getting jsonp information back from a page and I want to run various functions on that information. The information comes back fine but I can't seem to find a way to make it accessible outside of the function. I know it's something to do with closure and function scope but I can't figure out how to make it work, any ideas?
I can achieve what I'm trying to do in the rest of the script by making multiple calls to the json file but I assume it's better to just query the json once and pop it into a variable and try work off that? I'm relatively new to this set up so any suggestions appreciated.
Effectively from the code below I want to be able to get the allMatches variable accessible outside of the getData method after it runs.
Thanks for your time, all help greatly appreciated.
var AppInfo = {
getData : function(){
var responseJsonVar;
var callbackName, script, newInfo, mydata,allMatches;
// Get a random name for the callback
callbackName = "checkGames" + new Date().getTime() + Math.floor(Math.random() * 10000);
// create the jsonP script call on the page
script = document.createElement('script');
script.src = "http://www.hookhockey.com/index.php/temp-gillian/?callback=" + callbackName;
document.documentElement.appendChild(script);
// call the json
window[callbackName] = function(data) {
responseJsonVar = data; // this is the info back from the json file
//the filtered data source from json
var allMatches = responseJsonVar["matches"];
console.dir('allMatches inside the function: ' + allMatches); //this comes back fine
// Remove our callback ('delete' with 'window properties fails on some versions of IE, so we fall back to setting the property to 'undefined' if that happens)
try {
delete window[callbackName];
}
catch (e) {
window[callbackName] = undefined;
}
//I've tried putting a return value (return allMatches) in here and then calling window[callbackName]() outside of the function but I get undefined for matches
}; // end window[callbackName] function
//this is what I think I should be doing to get the info out on its own
console.dir('allMatches OUTSIDE the function: ' + allMatches); //this doesn't come back 'allMatches is not defined'
} //end getdata method
} //end AppInfo
AppInfo.getData();
You could just create a property on your AppInfo object called allMatches and set that property when the data comes back from the jsonp call:
var AppInfo = {
allMatches: null, // NEW PROPERTY TO HOLD RETURNED DATA
confirmDataAvailableOutsideFunction: function () { // NEW FUNCTION TO VERIFY DATA AVAILABLE OUTSIDE getData()
console.dir('AppInfo.allMatches OUTSIDE the function AFTER jsonp call executes: ' + AppInfo.allMatches); //this doesn't come back 'allMatches is not defined'
},
getData: function () {
var responseJsonVar;
var callbackName, script, newInfo, mydata, allMatches;
// Get a random name for the callback
callbackName = "checkGames" + new Date().getTime() + Math.floor(Math.random() * 10000);
// create the jsonP script call on the page
script = document.createElement('script');
script.src = "http://www.hookhockey.com/index.php/temp-gillian/?callback=" + callbackName;
document.documentElement.appendChild(script);
// call the json
window[callbackName] = function (data) {
responseJsonVar = data; // this is the info back from the json file
//the filtered data source from json
AppInfo.allMatches = responseJsonVar["matches"]; // store data in allMatches property
console.dir('allMatches inside the function: ' + AppInfo.allMatches); //this comes back fine
AppInfo.confirmDataAvailableOutsideFunction(); // call test method to verify allMatches property is set
// Remove our callback ('delete' with 'window properties fails on some versions of IE, so we fall back to setting the property to 'undefined' if that happens)
try {
delete window[callbackName];
}
catch (e) {
window[callbackName] = undefined;
}
//I've tried putting a return value (return allMatches) in here and then calling window[callbackName]() outside of the function but I get undefined for matches
}; // end window[callbackName] function
//this is what I think I should be doing to get the info out on its own
console.dir('AppInfo.allMatches OUTSIDE the function BEFORE jsonp call executes: ' + AppInfo.allMatches); //this doesn't come back 'allMatches is not defined'
} //end getdata method
}; //end AppInfo
AppInfo.getData();
Note that I modified the text of your second console.dir to indicate that it's running before the jsonp call returns, and therefore the allMatches property is still null at that point.
That's why, after implementing #peter-b's suggestion to use window.allMatches instead of local variable allMatches, window.allMatches OUTSIDE the function was undefined--you were checking it before it was set.
#peter-b's solution would work fine, provided that you didn't try to access window.allMatches before it was set. So if you want the data stored in a global variable, you can use his method; if you'd rather have it stored on your AppInfo object, you can use mine.
Alternatively, you can wrap everything in an immediate function that has allMatches as a local variable:
(function () {
var allMatches = null;
var AppInfo = {
getData: function (dataReadyCallback) {
/* ... */
allMatches = responseJsonVar["matches"];
dataReadyCallback();
/* ... */
}
};
AppInfo.getData(allMatchesReady);
function allMatchesReady() {
console.dir('allMatches OUTSIDE the function AFTER jsonp call executes: ' + allMatches);
}
}());
One easy way to get what you want is to assign allMatches as a property of the window, making it accessible everywhere. Replace
var allMatches = responseJsonVar["matches"];
with
window.allMatches = responseJsonVar["matches"];
and then access it later using window.allMatches.
Related
I am getting an undefined when I try the post to twitter function. Should the quote_text variable be global and therefore accessible by the quoteTwitter function?
$(document).ready(function () {
loadJSON();
getQuote();
console.log(quote_text);
});
// Declare variables
var json_obj;
var num = 0;
var quote_text = "";
// Display a quote - this method is not perfect since the random number will repeat itself and it appears as if no new quote is delivered
function getQuote(callback) {
var html = "";
num = randNum();
quote_text = json_obj[num].quote;
html += "<strong> " + quote_text + " </strong>";
$("#quote").html(html);
$("#author").html(json_obj[num].author);
};
// Post the current quote on twitter
function quoteTwitter(quote_text){
var tweet = quote_text;
window.open('https://twitter.com/home?status=' +encodeURIComponent(tweet),"_blank");
}
Your function definition includes quote_text as a parameter, so inside the function it's trying to use that instead of the global variable with the same name. You're presumably not passing anything to the function when you call it, so it comes out as undefined.
You can fix this by changing this:
function quoteTwitter(quote_text){
to this:
function quoteTwitter(){
...but it'd probably be better in the long run to pass the correct value in as a parameter, if possible, instead of depending on global variables.
I have a module with four functions that call one after the other. I am trying to follow the Revealing Module Pattern. One of the functions is public, the remaining are private. It goes like this:
publicMethod is called from another module
queryNames is called from publicMethod
execute(parameters, callback?, errback?) is called from queryNames
addNamesList is called as the callback? argument of execute
Several dijit/form/CheckBox's are created and the method querySegments is triggered onChange
querySegments needs to call a method of an object created in publicMethod.
The problem is in step 6, I can't reach the object created in step 1.
I have tried to use dojo hitch to define the callback? argument in step 3, but I can't get it to work. I tried putting this in its first argument, but even then I can't reach the required scope to call addNamesList.
Here is some code to demonstrate this issue.
define([
'dojo/dom',
'dijit/form/CheckBox',
'esri/layers/ArcGISDynamicMapServiceLayer',
'esri/tasks/query',
'esri/tasks/QueryTask',
'dojo/_base/lang'
],
function (
dom,
CheckBox,
ArcGISDynamicMapServiceLayer,
Query, QueryTask,
lang
) {
// ***************
// private methods
// ***************
// fetch names and call addNamesList to put the list in place
var queryNames = function (map, mapLayer) {
// new QueryTask(url, options?)
var queryTask = new QueryTask("url")
var query = new Query()
// execute(parameters, callback?, errback?)
// this callback passes an argument called featureSet
queryTask.execute(query, lang.hitch(map, "addNamesList", mapLayer), function(error) {console.log(error)})
}
// callback function of queryNames
var addNamesList = function (mapLayer, featureSet) {
console.log('addOplist')
var namesCount = featureSet.features.length
for (var i = 0; i <namesCount; i++) {
// work
var cbox = new CheckBox({
id: "cbox_" + i,
value: featureSet.features[i].attributes["someID"],
checked: false,
onChange: function (evt) {
querySegments(this.value, mapLayer)
}
})
cbox.placeAt("someDiv" + i, "first")
}
}
// triggered by the checkbox event
var querySegments = function (name, mapLayer) {
// build the query
var queryStatement = "someID = " + name
var layerDefinitions = [queryStatement]
// call a method of mapLayer
mapLayer.setLayerDefinitions(layerDefinitions)
}
// **************
// public methods
// **************
var publicMethod = function (map) {
var mapLayer = new ArcGISDynamicMapServiceLayer('restURL')
map.addLayer(mapServiceLayer)
queryNames(map, mapLayer)
return mapLayer
}
return {
publicMethod: publicMethod
}
}
)
You can see a more detailed explanation and a working example on this other (and more broad) question that I have put on Code Review.
I am new to JavaScript and I guess I still have a lot of issues with scoping, closures and callbacks.
I will deeply appreciate any input, including how to improve this question.
Edit
With this current implementation (with dojo hitch), no error is thrown. The method addNamesList is not called (nor errback, which I also don't understand why). I think this is because addNamesList is not on map's (hitch first argument) namespace. I tried to put this instead, but it makes no difference.
Before I decided to use hitch, the code looked like this:
var queryNames = function (map, mapLayer) {
...
queryTask.execute(query, addNamesList)
}
var addNamesList = function (featureSet) {
...
...
...
querySegments(this.value, mapLayer)
}
but then I couldn't reach mapLayer inside the method triggered by the check box event. It would throw Uncaught ReferenceError: mapLayer is not defined. That is why I tried to use hitch.
Javascript is asynchronous, so pretty much data coming from db, http requests or whatever is returned via callbacks. Here's what happens in your code:
public method calls queryNames
queryNames call addNamesList of map asynchronously and returns nothing
public method takes back control, meanwhile some stuff is going on with the addNamesList
mapLayer is returned untouched while some stuff is still going on in the background
So, to avoid this, you should return data from public method via callback, so you pass callback as the second parameter to the public method, then to the querySegments. Then, in the success callback of query, when you finally get the result ready, you do:
callback(mapLayer);
So, everything you should do is to pass this callback as deep as needed to the place where you have your mapLayer ready (so you've done with it everything you wanted), and then do a callback(mapLayer);.
This and this would probably explain better.
Best regards, Alexander
I've built a simple html input so that users can input a zip code and then I have a variable in javascript set to that input. I can console.log this to prove the variable is set and that it is a string. I then try to run an ajax call and sub in the zip but it doesn't work. I can console.log the variable at any stage and see the variable has been updated, but somehow it's a hoisting issue or something where the ajax call value 'userInputZip' always reads to what I initially set. The ajax call works when 'userInputZip' is initially set to a valid zipoAny help is appreciated.
$(document).ready(function(){
});//end of document.ready
var inputDate = '2015-12-04T20:00:00';
var inputZipCode = '60618';
var userInputZip;
function runAjax(){
console.log(userInputZip);
$.ajax(getJambaseData);
}
// var dataArr = [];
var getJambaseData = {
type: 'get',
url:
'http://api.jambase.com/events?zipCode='+userInputZip+'&api_key=[inserted my key here]',
// 'http://api.jambase.com/events?zipCode='+userInputZip+'&api_key=[inserted my key here]',
success: function (data){
for (i=0; i< 10; i++){
if(data.Events[i].Date == inputDate){
var shortDate = data.Events[i].Date.substring(0,10);
var shortTime = data.Events[i].Date.substring(11,19);
// dataArr.push(data.Events[i].Date, data.Events[i].Artists[0].Name);
$("#divID").append('</p>' + 'date::: '+ shortDate + ' time:::' + shortTime + ' show::: ' + data.Events[i].Artists[0].Name + ' time::: ' + data.Events[i].Date + ' address::: ' + data.Events[i].Venue.Address + ' city::: ' + data.Events[i].Venue.City + '</p>');
}
}
},
error: function(){
console.log('failed');
},
}
function findShows(){
var userZip = document.getElementById("userInput");
userInputZip = userZip.value;
document.getElementById("divID").innerHTML = userInputZip;
runAjax();
}
////////////////////
You've mentioned
but somehow it's a hoisting issue or something where the ajax call value 'userInputZip' always reads to what I initially set
You define getJambaseData as a variable when the script is initially executed. You set the url value to url:
'http://api.jambase.com/events?zipCode='+userInputZip+'&api_key=[inserted my key here]'. What else did you expect to happen?
That's like saying var x = 10; and expecting it to magically change when you call a function.
What you have to do is move the whole var getJambaseData = {...} initialization into runAjax function and it should fix it. Or you could skip the variable initialization part and just pass the part inside {...} (including the curly braces obviously) inside the $.ajax call instead of variable. If you look at jQuery docs you'll see that in most examples and it's the usual syntax.
Not related to your question, but here are some friendly words of advice:
Don't use variable before you define it (reading top to bottom), it will save you a lot of headaches.
Another recommendation is don't use so much global variables, you could get the userInputZip inside findShows function and pass it to runAjax as function argument. If you'll develop applications in a way where you rely on global state a lot, you'll have a bad time very soon.
I have a function nested inside a javascript plugin which quite simply checks if something exists inside of an IndexedDB and then ideally I want it to return true or false.
I have assigned the call to the function to a variable (eg var res = $(document).check('name');)
However all I get in the console output if I do a console debug on it is undefined. If I add more console logging into the function it is getting the record correctly and in the right part of the if statement but there is no value being returned.
(function($){
var db, tx, callback, options,
names = {
checkFor: function(name){
//Lightweight copy of above without Thumbs
console.log('In checkfor');
var tx = db.transaction('myDB').objectStore('people').get(name).onsuccess = function(event){
console.debug('res',event);
var res = event.target.result;
if(res.length > 0)
return true;
else
return false;
};
}
}
$.extend($.fn,{
check: function(name){
names.checkFor(name);
});
}(jQuery));
The above is an example, there are many more functions and the plugin is quite lengthily. If anyone could help that would be greatly appreciated!
Thanks!
I am relatively new to javascript and I am facing some difficulty.I have two java script files as I have shown below. I am having trouble getting the value of the variable entry_title inside the getRss function and storing it inside the variables Rss1_title and Rss2_title . Creating a global variable and assigning it to entry_title will make things worse as I will not be able to know from which Rss url the title came from. Is there a easy way to get the value of the callback functions ?
<script type="text/javascript" src="jsRss.js"></script>
<script type="text/javascript" src="notification.js"></script>
My notification.js file
function get_rss1_feeds(){
var Rss1_title = getRss("http://yofreesamples.com/category/free-coupons/feed/?type=rss");
}
function get_rss2_feeds(){
var Rss2_title = getRss("http://yofreesamples.com/category/real-freebies/feed/?type=rss");
}
setTimeout('get_rss1_feeds()',8000);
setTimeout('get_rss2_feeds()',7000);
My jsRss.js file:
function getRss(url){
if(url == null) return false;
google.load("feeds", "1");
// Our callback function, for when a feed is loaded.
function feedLoaded(result) {
if (!result.error) {
var entry = result.feed.entries[0];
var entry_title = entry.title; // need to get this value
}
}
function Load() {
// Create a feed instance that will grab feed.
var feed = new google.feeds.Feed(url);
// Calling load sends the request off. It requires a callback function.
feed.load(feedLoaded);
}
google.setOnLoadCallback(Load);
}
Errors :
When the setTimeout(get_rss1_feeds, 8000); method is called I get a blank screen.
I get a error in my console saying octal literals and octal escape sequences are deprecated and it is pointing to the 6th line in this script.
Is it because I am using google-api for parsing my Rss?
if (window['google'] != undefined && window['google']['loader'] != undefined) {
if (!window['google']['feeds']) {
window['google']['feeds'] = {};
google.feeds.Version = '1.0';
google.feeds.JSHash = '8992c0a2cdf258e5bd0f517c78243cd6';
google.feeds.LoadArgs = 'file\75feeds\46v\0751';
}
google.loader.writeLoadTag("css", google.loader.ServiceBase + "/api/feeds/1.0/8992c0a2cdf258e5bd0f517c78243cd6/default+en.css", false);
google.loader.writeLoadTag("script", google.loader.ServiceBase + "/api/feeds/1.0/8992c0a2cdf258e5bd0f517c78243cd6/default+en.I.js", false);
}
Seeing as it's a different scope, you can either return it in a callback, or provide it in another way such as exporting it to a higher scope that is visible to your desired location. In this case, it's the global scope, so I'd advise against that.
function getRss(url, callback) {
//...
function feedLoaded(result) {
if (!result.error) {
var entry = result.feed.entries[0];
var entry_title = entry.title; // need to get this value
callback && callback(entry_title);
}
}
and call it like so,
function get_rss1_feeds() {
var Rss1_title = getRss("http://yofreesamples.com/category/free-coupons/feed/?type=rss", function(entry_title) {
// This scope has access to entry_title
});
}
As an aside, use your setTimeout like so:
setTimeout(get_rss1_feeds, 8000);
rather than
setTimeout("get_rss1_feeds()", 8000);
as the latter uses eval, whereas the former passes a reference to the function.
Eventhough it will make your code a mess, you can append the variables to the window object.
For example:
function a()
{
window.testStr = "test";
}
function b()
{
alert(window.testStr);
}
Or even create your own object, instead of using window, as such:
var MyRSSReader = {
TitleOne : '',
TitleTwo : ''
}
MyRSSReader.TitleOne = "My title";
Wikipedia has a nice article about global variables, and why they are bad.