Passing a variable from a callback function to another function? - javascript

I am working on setting up an HTML5 GeoLocation script and I would like to store the zip code in a cookie but for now I am just trying to figure out how to pass the zip code variable into another function.
Here is my script to reverse geo-code based on lat/long:
function retrieve_zip(callback)
{
try { if(!google) { google = 0; } } catch(err) { google = 0; } // Stupid Exceptions
if(navigator.geolocation) // FireFox/HTML5 GeoLocation
{
navigator.geolocation.getCurrentPosition(function(position)
{
zip_from_latlng(position.coords.latitude,position.coords.longitude,callback);
});
}
else if(google && google.gears) // Google Gears GeoLocation
{
var geloc = google.gears.factory.create('beta.geolocation');
geloc.getPermission();
geloc.getCurrentPosition(function(position)
{
zip_from_latlng(position.latitude,position.longitude,callback);
},function(err){});
}
}
function zip_from_latlng(latitude,longitude,callback)
{
// Setup the Script using Geonames.org's WebService
var script = document.createElement("script");
script.src = "http://ws.geonames.org/findNearbyPostalCodesJSON?lat=" + latitude + "&lng=" + longitude + "&callback=" + callback;
console.log(script.src);
// Run the Script
document.getElementsByTagName("head")[0].appendChild(script);
}
function callback(json)
{
zip = json.postalCodes[0].postalCode;
country = json.postalCodes[0].countryCode;
state = json.postalCodes[0].adminName1;
county = json.postalCodes[0].adminName2;
place = json.postalCodes[0].placeName;
alert(zip);
}
$('#findLocation').click(function(event) {
event.preventDefault();
console.log(zip); // This is giving me undefined currently
});
So basically, in the callback function, I want to store the zip code as a variable(rather than displaying it in an alert) and then in the on click function at the bottom, I want to be able to display the zip code that was stored in the previous callback function.
Any help greatly appreciated, still pretty new to Javscript/jQuery, thanks!

You could set zip as a 'global' variable by including it outside of the function at the top of the document like so:
var zip;
...
Alternatively, you may consider defining an object at the 'global' level and using it as a namespace to store variables like so:
window.address = {};
function callback(json){
address.zip = json.postalCodes[0].postalCode;
address.country = json.postalCodes[0].countryCode;
address.state = json.postalCodes[0].adminName1;
address.county = json.postalCodes[0].adminName2;
address.place = json.postalCodes[0].placeName;
}
$('#findLocation').click(function(event) {
event.preventDefault();
console.log(address.address);
console.log(address.zip);
...
});
I hope this helps!

Define var zip at very begining of code. You haven't defined it.
I haven't tried, but it should solve your problem.
Also, it seems that you forgot to define other variables in callback function as well.

What I would do, is to avoid the anonymous function in the event handler, that is, create a new named function -which gives you the added benefit of traceability during debugging- and then use that function as the event handler callback:
function eventHandlerFunction(event) {
var zip;
event.preventDefault();
zip = eventHandlerFunction.zip;
console.log(zip);
}
function callback(json) {
var zip;
zip = doSomethingWithJsonToGetTheZip();
eventHandlerFunction.zip = zip;
}
$("#findLocation").click(eventHandlerFunction);
Or, better yet, code this as a module and then you have member encapsulation and you can share variables amongst functions without modifying the global object. You never know when another library will modify the same global member that you are using.
var yourModule = (function($) {
var zip;
function retrieveZip(callback) {
// your code
}
function callback(json) {
// do something with json
zip = json.zip; // or whatever
}
$("#findLocation").click(function(event) {
event.preventDefault();
console.log(zip); // zip is visible in the parent scope + this scope
});
}(jQuery));

Related

Is it bad practice to instantiate variables inside of $(document).ready as opposed to globally declaring them?

I'm trying to avoid the use of global variables in my code, so I'm trying to use a work around by declaring them inside of $(document).ready and passing them as parameters to functions outside of $(document).ready, updating them, and then returning the updated value from those functions to manipulate the variables inside of $(document).ready.
Another way around this is to use hidden input fields to store variables but I also heard that was bad practice.
I'm wondering if I should just use global variables, do it the way I'm currently doing it, or use hidden input fields?
Below is a brief example of what I'm trying to accomplish. The variable validations is the variable I want to be able to use and update.
$(document).ready(function(){
var validations = [];
$('#inp').keypress(function(e){
if(e.which == 13){
e.preventDefault();
scanValidation(validations, function(valid){
validations = valid;
});
}
});
}):
function scanValidation(valid, cb){
var scanval = $('#inp').val();
if(valid.includes(scanval)){
//display error
}
else{
var validarr = valid.slice();
validarr.push(scanval);
var myData=JSON.stringify({ "name":"user1", "validations":validarr});
//Makes an ajax call to see if the sent array validarr is a valid request
apiCall(myData,'scanValidation',function(decoded) {
if (decoded.Status!="ERROR") {
valid = validarr;
}
else {
//display error
}
return(cb(valid));
});
}
}
Any variables declared within the immediately executed function below will NOT be in the global scope.
(function () {
var someVar = 'someValue';
$(document).ready(function() {
});
})();
Is it bad practice to instantiate variables inside of $(document).ready as opposed to globally declaring them?
No, not at all! Variables should always be declared in the scope they're needed in, nowhere else.
Another way around this is to use hidden input fields to store variables but I also heard that was bad practice.
I've never heard of that, but yes it definitely sounds like a bad practise. That's just the same as a global variable, but a global variable stored in the DOM for some odd reason.
I'm trying to avoid the use of global variables in my code, so I'm trying to use a work around by declaring them inside of $(document).ready and passing them as parameters to functions outside of $(document).ready, updating them, and then returning the updated value from those functions to manipulate the variables inside of $(document).ready.
That, admittedly, is a bit weird.
The easiest way to improve this is to move the function declaration inside the ready handler as well, and just access the variable there directly - with the additional bonus of not having a scanValidation global variable:
$(document).ready(function() {
var validations = [];
function scanValidation() {
var scanval = $('#inp').val();
if (validations.includes(scanval)) {
//display error
} else {
var validarr = validations.slice();
validarr.push(scanval);
var myData = JSON.stringify({"name": "user1", "validations": validarr});
// Makes an ajax call to see if the sent array validarr is a valid request
apiCall(myData, 'scanValidation', function(decoded) {
if (decoded.Status!="ERROR") {
validations = validarr;
} else {
//display error
}
});
}
}
$('#inp').keypress(function(e){
if(e.which == 13){
e.preventDefault();
scanValidation();
}
});
});
If you want to make scanValidation reusable, so that it could be called from other places as well with its own array, I would suggest to create a factory function that creates validators, each of which is a closure over its own array. That way, the array is declared where it belongs, so that the user of the function does not have to store the state for them:
function makeScanValidator(display) { // any other configuration
var validations = [];
// returns closure
return function scanValidation(scanval) { // take it as an argument
if (validations.includes(scanval)) {
display(/* error */);
} else {
var validarr = validations.concat([scanval]);
var myData = JSON.stringify({"name": "user1", "validations": validarr});
apiCall(myData, 'scanValidation', function(decoded) {
if (decoded.Status!="ERROR") {
validations = validarr;
} else {
display(/* error */);
}
});
}
}
$(document).ready(function() {
var validate = makeScanValidator(function display() { … });
$('#inp').keypress(function(e){
if(e.which == 13){
e.preventDefault();
validate(this.value);
}
});
});

Using closure for storing and retrieving data

I am trying to use closure for storing and retrieving variable at the same time.
I am using JSONP and callback to the function
http://freegeoip.net/json/?callback=geoIPInfo
Closure
function geoIPInfo(newGeoData) {
var geoInfo;
if (newGeoData) {
geoInfo = newGeoData;
}
var provideGeoData = function () {
return geoInfo;
};
return provideGeoData();
}
I want firstly to store data and than retrieve last saved data from the closure using simple call like that
geoIPInfo()
If argument provided it will set new info otherwise it will return existing one.
But in my case data is set successfully, but when I try to get set data I get undefined
$("#super_button").click(function (e) {
alert(geoIPInfo());
e.preventDefault();
});
What is wrong with my closure understanding ?
Please explain.
Thank you.
This will work. The idea here is we create a function that returns a function with that accepts a parameter and we store geoInfo in a closure to keep it value. Idk if that makes sense, if you need a better explanation I can give it another try :)
var geoIPInfo = function() {
var geoInfo;
var provideGeoData = function (newGeoData) {
if (newGeoData) {
geoInfo = newGeoData;
}
return geoInfo;
};
return provideGeoData;
}();
Each time you call geoIPInfo(), you're re-declaring the local variable geoInfo. You'll want to declare geoInfo once, and have it accessible to geoIPInfo() via a closure:
//Create a closure
var geoIPInfo = (function(){
//Private variable, available via closure
var geoInfo;
function geoIPInfo(newGeoData) {
if (newGeoData) {
geoInfo = newGeoData;
}
var provideGeoData = function () {
return geoInfo;
};
return provideGeoData();
}
return geoIPInfo;
})();
alert(geoIPInfo()); //undefined
geoIPInfo('Some Data');
alert(geoIPInfo()); //'Some Data'
Here, we're creating a closure using an Immediately-Invoked Function Expression (IIFE).

Using variable in a ajax function

I just want to use a variable from outside of the function, but I am not sure what I have to do for that...
Is var myRequest = a; line enough to use this variable in the function?
Because I saw such an example: var myRequest = e.which;
I am asking this because I did not get a succesful result for my request.
I am think that it is not working as I expected because ajaxFunction(3) working diffirent than writing send.php?goto=3 into address bar of my browser.
You can see the following codes:
function ajaxFunction(a)
{
var ajaxRequest;
try {
ajaxRequest = new XMLHttpRequest();
} catch (e) {
try {
ajaxRequest = new ActiveXObject("Msxml2.XMLHTTP");
} catch (e) {
try {
ajaxRequest = new ActiveXObject("Microsoft.XMLHTTP");
} catch (e) {
alert("Your browser broke!");
return false;
}
}
}
ajaxRequest.open("GET", "send.php?goto=" + a, true);
ajaxRequest.send();
}
If you want to use a variable outside a function you have to use a global scope variable, example (using jQuery ajax)
var globalA = null;
$(document).ready(function(){
var localA1 = null;
$.ajax({
"url":"http://someurl.com/",
"type":"POST",
"dataType":"json",
"success":function(incomingData){
var localA2 = incomingData //localA2 only useable inside this function
localA1 = incomingData; //localA1 being set here still can only be used here as code within the "ready" function has already been executed and will see it as null
globalA = incomingData; //Now any further code should use globalA as it now contains useable data
doSomethingWithData();
},
"error":function(xhr,msg) {
alert("Ajax Error:"+msg);
}
});
alert(localA1); //Will give alertbox with null in it as localA1 has not been set.
});
function doSometingWithData() {
alert(globalA); //You can now use the data in whatever function makes reference to globalA
}
Of course in this example you could have just passed the data straight to doSomethingWithData() and processed it there.
You could take a look at the jQuery $.globalEval for instantiating a variable globally inside of your AJAX success function.
$.ajax({
url: "send.php",
success: function (data) {
$.getScript("somescript.js", function(data) {
$.globalEval("var something = new Whatever;");
});
});
The $.getScript portion is a helpful little snippet if you find you need to load an external JS file in your ajax call, and make its assets globally available. You can then use $.globalEval to instantiate a variable inside of your AJAX function.
Documentation for $.globalEval
Documentation for jQuery AJAX
You don't a function wrapper for setting a value to a variable.
var myRequest = a;
That is good enough.
After thought revision
in a very basic way a variable can be created on its own like a place holder.
var myRequest;
when you get to the function (say you have a series of functions.
You could do something like this.
function(myRequest=a);
if the function has more than one argument it can look like this.
function(myRequest=a,myConcern=b); as you have it stated in the
var arg1 = 1;
var arg2 = 2;
var arg3 = 3;
ajaxRequest.open(arg1,arg2,arg3);
I hope this is helpful and yes, some more info would help (like the poster below stated).

JS is saying that I have an undefined object. Could REALL use some help figuring this out

Today I was tasked with working with the Google API for the first time to create a map. I had no problem setting up the map on my site, and that was easy enough. But for some reason my points I coded with json are not showing up. It is saying that my gmap object is undefined. I could really use some help here, I've been starring at this thing ALL DAY!!
function load () {
var map = document.getElementById("map");
if (GBrowserIsCompatible()) {
var gmap = new GMap2(map);
gmap.addControl( new GSmallMapControl() );
gmap.addControl( new GMapTypeControl()) ;
gmap.addControl( new GOverviewMapControl(new GSize(100,100)) );
gmap.setCenter( new GLatLng(42.3313889, -83.0458333), 7 );
} else {
alert("Sorry, your browser cannot handle the true power of Google Maps");
}
}
window.onload = load;
window.onunload = GUnload;
function createMarker(input) {
var marker = new GMarker(input.point);
GEvent.addListener(marker, "click", function() {
marker.openInfoWindowHtml( input.walkName + input.date + input.address );
});
return marker;
}
function parseJson (doc) {
var jsonData = eval("(" + doc + ")");
for (var i = 0; i < jsonData.markers.length; i++) {
var marker = createMarker(jsonData.markers[i]);
gmap.addOverlay(marker);
}
}
GDownloadUrl("points.json", function(data, responseCode) {
parseJson(data);
});
You are using the gmap variable inside your parseJson function. But it is declared inside the load() function. They don't share scope. Just move the gmap declaration outside of both functions so it is in a common scope to both of them.
It looks like gmap is defined within the scope of the load function. So it's not available in the parseJSON function where you reference it.

Open social viewer state (isOwner)

We are creating a gadget for the opensocial API 0.7.
In some functions we have to decide, if the viewer is the owner.
We couldn't use the usual function for this purpose:
return gadgets.util.getUrlParameters().viewer == gadgets.util.getUrlParameters().owner;
so we had to create a workaround and get the information via a DataRequest.
The DataRequest calls a callback function and has no useable return value.
We tried a quick hack by using global variables to set the corresponding value.
The issue at this point is, that the function does not 'wait' for the callback-function to be finished. We know this is no good code/style at all, but we tried to force a timeout for debug reasons.
Handling all the code within the callback-function (as suggested in the examples of the opensocial docs) is not possible.
We are looking for something like a real 'sleep()' in JavaScript to wait for the callback-function to complete or another alternative to get the owner information about the viewer.
globalWorkaroundIsOwner = false;
function show_teaser(){
if (current_user_is_owner()){
// ...
}
// ...
}
function current_user_is_owner() {
var req = opensocial.newDataRequest();
req.add(req.newFetchPersonRequest(opensocial.DataRequest.PersonId.VIEWER), 'viewer');
// This will set the the correct value
req.send( user_is_owner_workaround );
// This is an attempt to delay the return of the value.
// An alert() at this point delays the return as wanted.
window.setTimeout("empty()", 2000);
// This return seems to be called too early (the variable is false)
return globalWorkaroundIsOwner;
}
function user_is_owner_workaround(dataResponse) {
var viewer = dataResponse.get('viewer').getData();
globalWorkaroundIsOwner = viewer.isOwner();
// value is correct at this point
}
Can you use an additional flag in order to indicate whether the remote query has already returned the required value?
var globalWorkaroundIsOwner = false;
var workaroundStarted = false, workAroundComplete = false;
var checker;
function show_teaser(){
if (!workaroundStarted) {
workaroundStarted = true;
current_user_is_owner();
}
if (workaroundComplete) {
if (globalWorkaroundIsOwner){
// ...
}
// ...
if (checker) {
clearInterval(checker);
}
}
}
function current_user_is_owner() {
var req = opensocial.newDataRequest();
req.add(req.newFetchPersonRequest(opensocial.DataRequest.PersonId.VIEWER), 'viewer');
checker = setInterval("show_teaser()", 1000);
// This will set the the correct value
req.send( user_is_owner_workaround );
}
function user_is_owner_workaround(dataResponse) {
var viewer = dataResponse.get('viewer').getData();
globalWorkaroundIsOwner = viewer.isOwner();
workAroundComplete = true;
// value is correct at this point
}

Categories

Resources