accessing variable value outside of function - javascript

I have 2 functions in my script. The purpose of function #1 is to produce a url called "mapsURL"
The purpose of the second function is to run an .ajax request with "mapsURL"
However I am having issues with accessing "mapsURL" in the second function. I declared "mapsURL" in the first function without a "var" in fornt of it. From my understanding, this should make the this the value global and I should be able to access it form inside other functions. Is my understanding incorrect or what am I missing here?
here's my js:
note: I removed my API key for this post, so that's not the issue
$(document).ready(function (){
navigator.geolocation.getCurrentPosition(function (position) {
var positionLat = position.coords.latitude
var positionLon = position.coords.longitude
mapsURL = "https://maps.googleapis.com/maps/api/geocode/json?latlng=" + positionLat + "," + positionLon + "&key=***mykeygoeshere***";
});
function getData (){
$.ajax({
url: mapsURL,
dataType: 'json',
success: function(response){
console.log(response);
}
});
};
getData();
});

getCurrentPosition is asynchronous. It doesn't assign to mapsURL immediately, so when you call getData synchronously, mapsURL hasn't been populated yet.
You should call getData inside the getCurrentPosition callback - which will also allow you to avoid using a global variable:
$(document).ready(function() {
navigator.geolocation.getCurrentPosition(function(position) {
var positionLat = position.coords.latitude
var positionLon = position.coords.longitude
var mapsURL = "https://maps.googleapis.com/maps/api/geocode/json?latlng=" + positionLat + "," + positionLon + "&key=***mykeygoeshere***";
getData(mapsURL);
});
function getData(mapsURL) {
$.ajax({
url: mapsURL,
dataType: 'json',
success: function(response) {
console.log(response);
}
});
}
});

Related

Javascript functions order

I'm making a jquery library to use an application with the json rpc protocol but I'm stuck with a little problem.
This is the fiddle that shows the code (obviously it can't work): https://jsfiddle.net/L9qkkxLe/3/.
;(function($) {
$.lib = function(options) {
var outputHTML = [],
plugin = this;
var APIcall = function(api_method, api_params) {
request = {};
request.id = Math.floor((Math.random() * 100) + 1);
request.jsonrpc = '2.0';
request.method = api_method;
request.params = (api_params) ? api_params : [];
$.ajax({
type: "POST",
url: "http://localhost:8898/jsonrpc",
data: JSON.stringify(request),
timeout: 3000,
beforeSend: function(xhr) {
xhr.setRequestHeader('Authorization', window.btoa(options.username + ":" + options.password));
},
success: function(data) {
handleData(data, api_method);
},
error: function(jqXHR, textStatus, errorThrown) {
log("Connection time out: can't reach it. Try changing the settings.");
isConnected = "false";
},
dataType: "json"
});
}
var handleData = function(data, method) {
if (method == "getgenres") {
outputHTML = data.result.genres; //I need data.result.genres to return in getgenres function
}
}
var log = function(msg) {
if (options.debug == true) console.log(msg);
}
plugin.getgenres = function() {
APIcall("getgenres");
return outputHTML; //This is sadly empty.
}
};
}(jQuery));
var init = new $.lib();
console.log(init.getgenres());
I need that the getgenres function returns data.result.genres but actually it returns an empty array because getgenres is called for first and only after the handleData function gives to outputHTML the value that I need.
You are performing an asynchronous AJAX request, which means you can't actually get back the data immediately. There are two ways to solve your issue: making it synchronous (easy but ill advised) or using a callback (a little bit more complex but generally accepted):
In your getgenres function, you could accept one more parameter: callback
plugin.getgenres = function(callback) {
/* Dont forget APIcall already took two parameters in, so callback has to be the third in line! */
APIcall("getgenres", false, callback);
}
Now modify your APIcall function to accept your callback:
var APIcall = function(api_method, api_params, callback) { ... }
And call the callback from the successful completion call - instead of having a handler method in between wrapped in a function, you can simply pass the anonymous function. So instead of success: function(data){ handle(data); }, just use:
success: callback
The anonymous function that we will pass to it will receive as its first parameter the data you were passing to the handler. Now you can do the following:
var myGenres = [];
var init = new $.lib();
init.getgenres(function(data){
/* Now your data is actually loaded and available here. */
myGenres = data;
console.log(myGenres);
});
I would like to point out that there are many better ways to handle this, including turning this into a Constructor (More here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance_and_the_prototype_chain) instead of the strange amalgamation of functions and variables you have now, as well as using JS Promises (here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) to make this easier. But the basic gist should be here.
Update (potential implementation)
Because I mentioned that this could be done in a way that I think is clearer to read and use. I do not know all use cases for this, but from the provided example I would change the code to something looking like the following. Please also note I am not an expert on jQuery plugins, so I am avoiding plugging into jQuery and just using it as an easy AJAX call.
function getAjax(){
if(!window.jQuery || !window.$) throw("jQuery is required for this plugin to function.");
this.data = [];
this.request = '';
return this;
}
getAjax.prototype = {
createRequest : function(method, parameters){
this.request = {};
this.request.id = Math.floor((Math.random() * 100) + 1);
this.request.jsonrpc = '2.0';
this.request.method = method;
this.request.params = parameters || [];
return this;
},
callRequest : function(options, callback, error){
var self = this;
// We could also `throw` here as you need to set up a request before calling it.
if(!this.request) return this;
else {
$.ajax({
// We will allow passing a type and url using the options and use sensible defaults.
type: options.type || "POST",
url: options.url || "http://localhost:8898/jsonrpc",
// Here we use the request we made earlier.
data: JSON.stringify(this.request),
timeout: options.timeout || 3000,
beforeSend: function(xhr){
xhr.setRequestHeader(
'Authorization',
window.btoa( options.username + ":" + options.password)
);
},
// We will also store all the made request in this object. That could be useful later, but it's not necessary. After that, we call the callback.
success: function(data){
var store = {request:self.request, data: data};
self.data.push(store);
// Call the callback and bind `this` to it so we can use `this` to access potentially pther data. Also, pass the results as arguments.
callback(data, self.request.id).bind(self);
},
// Error function!
error: error,
dataType: options.dataType || "json"
});
}
return this;
}
}
// Example use
new getAjax().createRequest('getgenres').callRequest({
username: 'myusername',
password: 'mypassword'
}, function(data, id){
// Success! Do with your data what you want.
console.log(data);
}, function(e){
// Error!
alert('An error has occurred: ' + e.statusText);
console.log(e);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js"></script>
What I do in those occasions is this:
You are supplying a method. So put a reference to the a callback function. In this case plugin.getGenresFinalize. When handleData is called it will fire that callBack function. This way you can pass multiple methods to the api call for different types of data.
plugin.getgenres = function() {
APIcall(this.getgenresFinalize);
}
plugin.getgenresFinalize = function(data) {
console.log(data);
}
var handleData = function(data, method) {
method(data);
}

ReferenceError: function parseXml undefined in Firefox

I am getting this strange error even when parseXml is defined. The piece of code works fine in Chrome but NOT in Firefox.
$(document).on("pageinit", "#map-page", function () {
var defaultLatLng = new google.maps.LatLng(56.8517843, 14.828458); // Default somewhere to Växjö when no geolocation support
if (navigator.geolocation) {
var stations = [];
$.ajax({
type: "GET",
url: "busstations.xml",
dataType: "xml",
success: parseXml
});
function parseXml(xml) {
$(xml).find('station').each(function () {
var name = $(this).find("name").text();
var localurl = $(this).find("localurl").text();
var latitude = $(this).find("latitude").text();
var longitude = $(this).find("longitude").text();
navigator.geolocation.getCurrentPosition(success, fail, {
maximumAge: 500000,
enableHighAccuracy: true,
timeout: 6000
});
function success(pos) {
currentLatitude = pos.coords.latitude;
currentLongitude = pos.coords.longitude;
console.log(pos.coords.latitude + " " + pos.coords.longitude);
}
function fail(error) {
alert("No GL support!");
}
stations.push({
"name": name,
"localurl": localurl
});
console.log(JSON.stringify(stations));
});
}
}
});
However if I remove the if(navigator.geolocation) check condition on the 3rd line, then it also works fine in Firefox and there is also no such undefined ReferenceError.
Also if I bring this if(navigator.geolocation) check condition inside the parseXml function, the code works fine. Wonder what is causing the problem in Firefox.
Is this acceptable and working?
$(document).on("pageinit", "#map-page", function () {
var defaultLatLng = new google.maps.LatLng(56.8517843, 14.828458); // Default somewhere to Växjö when no geolocation support
if (navigator.geolocation) {
$.ajax({
type: "GET",
url: "busstations.xml",
dataType: "xml",
success: parseXml
});
}
});
function parseXml(xml) {
var stations = [];
$(xml).find('station').each(function () {
var name = $(this).find("name").text();
var localurl = $(this).find("localurl").text();
var latitude = $(this).find("latitude").text();
var longitude = $(this).find("longitude").text();
navigator.geolocation.getCurrentPosition(
function(pos) {
currentLatitude = pos.coords.latitude;
currentLongitude = pos.coords.longitude;
console.log(pos.coords.latitude + " " + pos.coords.longitude);
},
function(error) {
alert("No GL support!");
},
{
maximumAge: 500000,
enableHighAccuracy: true,
timeout: 6000
}
);
stations.push({
"name": name,
"localurl": localurl
});
console.log(JSON.stringify(stations));
});
}
The problem might be that Firefox deals a little different with function declarations inside conditionals statments. The documentation says:
Note: Although this kind of function looks like a function
declaration, it is actually an expression (or statement), since it is
nested within another statement. See differences between function
declarations and function expressions.
So if it is an expression then when the ajax call try to use it the function is not defined yet.
To fix it change the order of the declaration or declare the function outside.
This is covered also in this question.

how to return a string from a function in jQuery as a parameter to the other jQuery function?

I have a small issue: I need to pass the value of string from function A to function B which will take it as argument and use it.
I have tried the following but its not working.
// first function (A)
$("a#SayHello").click(function (e) {
e.preventDefault();
var x = function () {
var dt = '{"ProductID": "' + $("input#ProductID").val() + '" , "AffiliationURL": "' + $("input#AffiliationURL").val() + '" , "Quantitiy": "' + $("input#Quantitiy").val() + '" , "PricePerUnit": "' + $("input#PricePerUnit").val() + '" , "commissionAmount": "' + $("input#commissionAmount").val() + '"}';
return dt.toString();
};
alert(x);
$.B(x);
});
// second function
function B(dt) {
$.ajax({
type: 'POST',
data: dt,
url: 'http://localhost:4528/WebSite1/WebService.asmx/InsertCommissionRecord',
contentType: 'application/json; charset=utf-8',
dataType: 'json',
success: function (data, textStatus, XMLHttpRequest) {
alert(data.d);
alert(XMLHttpRequest);
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
alert(textStatus);
}
});
};
I'm not sure but your not executing the function, try this:
alert(x());
$.B(x());
Try call the B function without " $. ", just B() , like any simple function.
Also, you can add an alert(dt) inside B() function, to check the data from parameter.
Instead of
$.B(x);
call x() and B() straight away:
B(x());
To be able to use B() like this: $.B() you need to add your function to jQuery's $:
$.B = function(){...
or
jQuery.B = function(){...
But it's generally not recommended to pollute $ - it's much better to use your own namespace.
See here: is-it-possible-to-create-a-namespace-in-jquery

Javascript Class - Cannot set a property

I have the below code to create a class in javascript.... Now i will only pass the ID of the 'website' and then via an ajax call i will get the rest of the information from the database (JSON ENCODED).
Now my only problem is that when i see whats in the object at the end its only showing the id.
The ajax calls works fine because if i alert this.address after success (ajax) its displaying the result.
What I presume is that I cannot set a property with an ajax request... Can you help?
function website(id) {
this.id = id; //id
$.ajax({ //website_information
type: "GET",
url: '/proc.php?proc=website_name&id=' + this.id + '',
success: function(data){
var tmp = $.parseJSON(data);
this.address = tmp.website_address;
this.name = tmp.website_name;
}
});
}
var obj = new website('20');
obj.alertwebsite();
console.log(obj);
There are two problems here. The first is that $.ajax is asynchronous. That means it returns before the request is complete. The success function is run when the request is complete, but obj.alertwebsite() will be run before.
The second problem is the value of this within an AJAX callback. Within the callback, this is set to an object containing all the settings for the AJAX call. This means you are setting address and name properties on this object. There are two ways around this. The first is that = this as in other answers. The nicer way is to use the context setting in the AJAX call:
function website(id) {
this.id = id; //id
$.ajax({ //website_information
type: "GET",
url: '/proc.php?proc=website_name&id=' + this.id + '',
context: this,
success: function (data) {
var tmp = $.parseJSON(data);
this.address = tmp.website_address;
this.name = tmp.website_name;
}
});
}
This allows you to customise what this means inside the callback. This is documented in the jQuery AJAX documentation.
the this inside the AJAX callback may not be referring to the "object-to-be" that your constructor is making. Instead, we preserve the this from the outside by putting it in another variable, like say that. Then we use that to refer to our object-to-be in the AJAX callback.
function website(id) {
var that = this; //preserve "this"
this.id = id;
$.ajax({
type: "GET",
url: '/proc.php?proc=website_name&id=' + this.id + '',
success: function(data){
var tmp = $.parseJSON(data);
that.address = tmp.website_address; //using "that"
that.name = tmp.website_name; //using "that"
}
});
}
also, you are calling blindly in your obj.alertwebsite() because you don't know if your website info (that.address and that.name) has been loaded or not.
function website(id) {
// you need to save "this"
var self = this;
this.id = id; //id
$.ajax({ //website_information
type: "GET",
url: '/proc.php?proc=website_name&id=' + this.id + '',
//async: false, // optionally, if you want to get it back syncronously, so the console.log after the function call will already have the data
success: function(data){
var tmp = $.parseJSON(data);
// you need to use the saved "this"
self.address = tmp.website_address;
self.name = tmp.website_name;
}
});
}

Javascript callback from custom jQuery AJAX function

I have this jQuery code
(function () {
function load_page (pagename) {
$.ajax({
url: "/backend/index.php/frontend/pull_page/",
type: "POST",
data: {page: pagename},
success: function (json) {
var parsed = $.parseJSON(json);
console.log(parsed);
return parsed;
},
error: function (error) {
$('#content').html('Sorry, there was an error: <br>' + error);
return false;
}
});
}
...
var json = load_page(page);
console.log(json);
if (json == false) {
$('body').fadeIn();
} else {
document.title = json.pagename + ' | The Other Half | freddum.com';
$("#content").html(json.content);
$('#header-navigation-ul a:Contains('+page+')').addClass('nav-selected');
$('body').fadeIn();
}
})();
and, guess what, it doesn't work. The AJAX request fires fine, and the server returns valid JSON but the console.log(json); returns undefined and the js crashes when it gets to json.pagename.
The first console.log(parsed) also returns good data so it's just a problem with the return (I think).
I knew I was clutching at straws and would be extremely if this worked, but it doesn't. To be honest, I don't know how to program callback functions for this situation.
EDIT: This is my now updated code, which doesn't work either.
function load_page (pagename, callback) {
$.ajax({
url: "/backend/index.php/frontend/pull_page/",
type: "POST",
data: {page: pagename},
success: function (json) {
callback(json);
},
error: function (error) {
$('#content').html('Sorry, there was an error: <br>' + error);
var json = false;
callback(json);
}
});
}
(function () {
$('body').hide();
var page = window.location.hash.slice(1);
if (page == "") page = 'home';
load_page(page, function(json) {
var parsed = $.parseJSON(json);
console.log(parsed);
if (json.pagename == "" || json.pagename == null) {
document.title = 'Page Not Found | The Other Half | freddum.com';
$('body').fadeIn();
} else {
document.title = parsed.pagename + ' | The Other Half | freddum.com';
$("#content").html(parsed.content);
$('#header-navigation-ul a:Contains('+page+')').addClass('nav-selected');
$('body').fadeIn();
}
});
})();
I moved load_page into global namespace 'cos I needed it to be there. The console.log(parsed) returns what seems to be a valid json object, but console.log(parsed.content) yields undefined. #content isn't being set either. Any ideas? I'll be glad to do any testing.
Any help is greatly appreciated!
Because Ajax requests are asynchronous, the code following the $.ajax function invocation still executes, whether the request is finished or not, so you should accept a callback as a argument to load_page that is invoked when the request is finished:
function load_page (pagename, callback) {
$.ajax({
url: "/backend/index.php/frontend/pull_page/",
type: "POST",
data: {page: pagename},
success: function (json) {
var parsed = $.parseJSON(json);
console.log(parsed);
callback(parsed); //bingo
},
error: function (error) {
$('#content').html('Sorry, there was an error: <br>' + error);
}
});
}
load_page(page, function(json) {
console.log(json);
if (json == false) {
$('body').fadeIn();
} else {
document.title = json.pagename + ' | The Other Half | freddum.com';
$("#content").html(json.content);
$('#header-navigation-ul a:Contains('+page+')').addClass('nav-selected');
$('body').fadeIn();
}
});
Inside the definition of the load_page function there is no "return" statement, not directly at least hence by doing a var json = load_page(page); you'll end up with json = undefined. Ideally you should re-organize your code a little. There is more than one way of doing this but here is one:
(function () {
function mySuccess(json) {
var parsed = $.parseJSON(json);
console.log(json);
console.log(parsed);
document.title = parsed.pagename + " | The Other Half | freddum.com";
$("#content").html(parsed.content);
$("#header-navigation-ul a:Contains(" + page + ")").addClass("nav-selected");
$("body").fadeIn();
}
function myFailure(error) {
$('#content').html('Sorry, there was an error: <br>' + error);
$("body").fadeIn();
}
function load_page(pagename, onSuccess, onFailure) {
$.ajax({
url: "/backend/index.php/frontend/pull_page/",
type: "POST",
data: {
page: pagename
},
success: onSuccess,
error: onFailure
});
}
load_page(page, mySuccess, myFailure);
})();
The issue is because jQuery issues ajax calls asynchronously by default. Hence the next statement is executed even before the ajax call is complete after
var json = load_page(page);.
You can either make the calls synchronous by passing async:false in the config parameters and dealing with the retun value in the callback function.
try console.log before parsing to check what data is exactly coming. is it valid json
success: function (json) {
console.log(json);
var parsed = $.parseJSON(json);
It's an AJAX call, as in, the code is completed asynchronously. You need to put the console.log and any other use of the json variable in the success function.

Categories