Strange "Undefined variable" in a JavaScript piece of code - javascript

I'm a c++ programmer so the following undefined variable error in java script is strange for me. I've defined a global variable ,directionResult and the following code initializes its value :
function calcRoute() {
var iMap = {
departure:"tiran,esfahan",
destination:"esfahan"
}
var request = {
origin : iMap.departure,
destination : iMap.destination,
travelMode : google.maps.TravelMode.DRIVING
};
directionsService.route(request, function(result, status) {
if (status == google.maps.DirectionsStatus.OK) {
directionsDisplay.setDirections(result);
document.getElementById("log").innerHTML = result.routes[0];
directionResult = result;
}
});
}
in the last if directionResult will be equal to result which is a parameter to a call-back function. But in another function :
function showSteps() {
var myRoute = directionResult.routes[0].legs[0];
var point = myRoute.steps[index].start_point;
var inst = myRoute.steps[index++].instructions;
obj.setPosition(point);
document.getElementById('inst').innerHTML = inst;
map.panTo(point);
if (index >= myRoute.steps.length)
clearInterval(timer);
}
when I want to use directionResult at the first line,I encounter this error:
TypeError: directionResult is undefined
How can I solve this strange behavior?thanks.

One possible reason directionResult is undefined may be because you are trying to reference it before it has been set. Although your showSteps() function appears after the calcRoute() function, directionResult is not set until the directionsService.route() callback is fired, which happens (presumably) after an AJAX request.
Try calling showSteps() from within the directionsService.route() callback function instead.

Related

Calling a function inside an AJAX success response?

I'm basically trying to call a function which is outside of my AJAX inside the AJAX success response.
To explain this better, this is what I have.
These are two functions:
function watchCurrentPosition() {
var positionTimer = navigator.geolocation.watchPosition(function(position) {
setMarkerPosition(userLocation, position);
map.panTo(new google.maps.LatLng(position.coords.latitude, position.coords.longitude));
});
}
function clearmyWatch() {
navigator.geolocation.clearWatch(positionTimer);
}
One of them keeps watching the users location and the other one 'should' stop it.
And this is how I'm calling the clearmyWatch(); function inside the AJAX:
$.ajax({
type:"post",
url:"TEST.PHP",
datatype:"html",
success:function(data)
{
clearmyWatch();
}
});
However, this doesn't work and the watchCurrentPosition(); constantly running and I also get an error in my console.
The error that I am getting is this:
ReferenceError: positionTimer is not defined
Could someone please advise on this issue?
Thanks in advance.
You need to define positionTimer at global level outside of function watchCurrentPosition() like:
var positionTimer = null;
function watchCurrentPosition() {
positionTimer = navigator.geolocation.watchPosition(function(position) {
setMarkerPosition(userLocation, position);
map.panTo(new google.maps.LatLng(position.coords.latitude, position.coords.longitude));
});
}
function clearmyWatch() {
if(positionTimer)
navigator.geolocation.clearWatch(positionTimer);
}
positionTimer is local to watchCurrentPosition
The better way to solve it without global variables is:
function watchCurrentPosition() {
var positionTimer = navigator.geolocation.watchPosition(function(position) {
setMarkerPosition(userLocation, position);
map.panTo(new google.maps.LatLng(position.coords.latitude, position.coords.longitude));
});
clearmyWatch=()=>{
navigator.geolocation.clearWatch(positionTimer);
}
}
function clearmyWatch() {}
Explanation: clearmyWatch is defined in a way that it does nothing.However calling watchCurrentPosition sets clearmyWatch as an inner function of watchCurrentPosition that can get to positionTimer. Actually by setting clearmyWatch to an inner function of watchCurrentPosition we create a closure (read about it of you don't know what it is, it's important).

Best way to 'return true' based on conditions from nested functions

This may be a novice question but I am trying to create a function that returns true. However, this is based on what happens within several other functions inside.
function checkGeo(){
// CHECK FOR GEOLOCATION
if( "geolocation" in navigator ) {
navigator.geolocation.getCurrentPosition( function(position){
sessionStorage.pinlat = position.coords.latitude;
sessionStorage.pinlon = position.coords.longitude;
// position object is set!
});
// position is not defined
if ( position.coords.latitude && position.coords.longitude ){
return true;
}
}
}
This is the order I want things to happen with my geolocation check but I'm a bit surprised that the nested if is tested before the getCurrentPosition method finishes.
Putting this condition within the getCurrentPosition success function and returning true from there does not make checkGeo return true. How do I check if this asyncronous function has ended and therefore check its results in order to return true?
Have your function have a finished variable
function checkGeo(){
var self = this;
this.ready = function () {}
this.result = false;
if("geolocation" in navigator) {
navigator.geolocation.getCurrentPosition(function(position) {
sessionStorage.pinlat = position.coords.latitude;
sessionStorage.pinlon = position.coords.longitude;
self.result = (position.coords.latitude && position.coords.longitude);
self.ready.call(self);
});
}
}
Now you can use the function:
var run = new checkGeo();
run.ready = function () {
alert(this.result); //Both work
alert(run.result); //Both work
};
A bit more complicated, but better programming in my opinion.
position in the anonymous function is not the same as position in the if statement after it. Scope in JavaScript (ignoring ES6 let keyword for simplicity) is by function.
Additionally, if getCurrentPosition() is asynchronous, then you can't rely on the anonymous callback function to run before anything else.
If all you want the return true to signify is that you are trying to get geolocation info without any guarantee that you will be successful, use something more like this:
function checkGeo(){
var hasGeolocation = false;
// CHECK FOR GEOLOCATION
if( "geolocation" in navigator ) {
hasGeolocation = true;
navigator.geolocation.getCurrentPosition( function(position){
sessionStorage.pinlat = position.coords.latitude;
sessionStorage.pinlon = position.coords.longitude;
// position object is set! but only inside this function.
});
return hasGeolocation;
}
}
On the other hand, if you are trying to have return true indicate that the geolocation was successfully set, then you need to indicate it some other way than the return value of the synchronous function, because you won't know that it will be set (an error might occur, a user might disallow geolocation for your site etc.) until the asynchronous function invokes the callback.
The geolocation call is asynchronous, so you can't return the result from the function. When the function ends, you don't yet know the result from the asynchronous call. Returning anything from the callback for the asynchronous call won't make that the return value of the function, because the function has already returned.
You can use callback to report the result back. You have to use the code that checks the position in the callback for the asynchronous call:
function checkGeo(callback){
if( "geolocation" in navigator ) {
navigator.geolocation.getCurrentPosition(function(position){
sessionStorage.pinlat = position.coords.latitude;
sessionStorage.pinlon = position.coords.longitude;
callback(position.coords.latitude && position.coords.longitude);
});
} else {
callback(false);
}
}
Usage:
checkGeo(function(exists){
// here you can use the result
if (exists) {
// ...
}
});

Why does my javascript geocode function return blank results?

I hope you can help. Ive made a function that receives a lnglat point object and returns just the town. I can get it to print the correct town in the console.log but it doesnt return the data back from the function.
I know its going to be a basic error but can someone have a look at the code and let me know please.
Thanks in advance.
function getTownFromPoint(point){
var geocoder ;
var geocoder = new google.maps.Geocoder();
var townset = false;
mylocation = "";
geocoder.geocode({latLng: point}, function(results, status) {
if (status == google.maps.GeocoderStatus.OK) {
if (results[0]) {
var components=results[0].address_components;
for (var component=0;component<(components.length);component++){
if(components[component].types[0]=="country" & !townset){
mylocation = components[component].long_name;
}
if(components[component].types[0]=="postal_code" & !townset){
mylocation = components[component].long_name;
}
if(components[component].types[0]=="locality"){
mylocation = components[component].long_name;
townset = true;
console.log(mylocation);
}
}
}
}
});
return(mylocation);
}
Geocoder is asynchronous - You are returning the value before you value is set.
It has been answered here:
Waiting for google maps geocoder?
That's because geocode is an ajax call and they are asynchronous. You need to provide a callback function or use a a promise to get the data. Since you're not using jQuery by the looks of your question a callback might be easier:
Here's a simplified version of your code with an example of how the callback function can be used:
// we pass in the callback as a function parameter
function getTownFromPoint(point, callback) {
geocoder.geocode({
latLng: point
}, function (results, status) {
var myLocation = results.myLocation;
// then we call the callback with the myLocation variable
callback(mylocation);
}
);
}
// call the function with the point variable and the function
// we will use for our callback
getTownFromPoint(1.223, function (myLocation) {
console.log(myLocation)
});
The problem you're facing is that you're treating the geocoder.geocode function as immediately completing before you do the return result. What's really happening is that the geocoder.geocode is triggered, then you get an immediate return of result. Because the asynchronous result has most likely not returned, your result is empty. Think of the geocoding result as a push, not a pull. The storeResult function, not shown, is whatever code you need to do to save the information. Because you're combining a result with an error string, you have to handle that in your storeResult function. As an alternative, you can have a status in the result that indicates succcess or failure.
store the result:
storeResult(result);
use this function inside your function. this will solve your problem

Javascript Function Variables

What do the variables result and status refer to in this? Where do they come from? How does origin, destination and travel mode get passed into the function? Where does the result of the function go?
$('#directions-form').submit(function(e) {
$('#error').hide();
ds.route({
origin: $('#from').val(),
destination: $('#to').val(),
travelMode: $('#mode').val()
}, function(result, status) {
if (status == google.maps.DirectionsStatus.OK) {
fitBounds = true;
dr.setDirections(result);
}
else {
$('#error').text(status).show();
}
recalcHeight();
});
e.preventDefault();
return false;
});
They are the parameters of the success callback function.
route method when invoked with proper variables fires the callback asynchronously when the response is a success. The parameters are part of the callback as part of the method invocation.
So you can go ahead and replace them with any variable name you want which will can be used inside the function closure.
}, function(a, b) { // Will work to
This function is being called with two parameters:
ds.route()
The first parameter is an object with some values in it:
{
origin: $('#from').val(),
destination: $('#to').val(),
travelMode: $('#mode').val()
}
The second parameter is a function:
function(result, status) {
if (status == google.maps.DirectionsStatus.OK) {
fitBounds = true;
dr.setDirections(result);
}
else {
$('#error').text(status).show();
}
recalcHeight();
}
Note that in JavaScript a function is an object like any other, and can be passed around like a variable. Since it's being passed into ds.route() as a variable, the function itself isn't being executed yet.
Internally, ds.route() is using the first parameter which has the values (origin, destination, travelMode) to do, well, something. It doesn't matter what, it's just whatever that function does.
Then, when it's done, it's going to execute the second parameter, which is the function. When it executes that function, it's going to pass two values into it as parameters. Those values will end up being the result and status variables used within the function.
To illustrate, you could do something as simple as this:
function doSomething(value, callback) {
if (value == 1) {
alert("The value is 1");
callback(2)
}
}
doSomething(1, function (value) {
if (value == 2) {
alert("The value is 2");
}
});
This defines a function which takes two arguments, and expects them to be a number and a function. If the number equals 1, it executes that function. This function is then called with the value 1 and a function, which will be executed as a callback.
When ds.route is called with route object as it's sole argument, it generates a result and a status. These are passed as the arguments to its callback.

How to resolve a Javascript Scope issue. Is closure the answer? [duplicate]

This question already has an answer here:
Saving geocoder results to an array - Closure Trouble
(1 answer)
Closed 9 years ago.
I have a function
function getCustomAddress() {
alert(results[i].formatted_address)
}
alert(results[i].formatted_address) is defined in another function. It clearly means that it is undefined in getCustomAddress, so how do I resolve this issue and alert the values. I have set up a fiddle as well.
http://jsfiddle.net/KEdrq/5/
You could just pass it as a function parameter
function getCustomAddress(result) {
alert(result.formatted_address)
}
so when you call the function you need to supply one parameter:
getCustomAddress(results[i]); for example
You could create a private scope with a function and define all your global variables there:
(function(){
var results = [];
function getCustomerAdress(){
//... call result etс
}
function set result(){
//... set result etc
}
// some code for initialization, setting onload handlers etc
})();
I checked out the jsFiddle, the results are fetched as an ajax request.
You need to store the results in a variable with a global scope and then set a timeout to fetch the result. You can also execute your function before the end of geocoder request and pass it the results variable.
geocoder.geocode(geocoderRequest, function (results, status) {
// execute your function here. getCustomAddress(result)
}
Check the changes I have made.
http://jsfiddle.net/KEdrq/7/
Summary of code changes.
var _results;
function initialize() {
.
.
.
google.maps.event.addListener(marker, 'dragend', function (e) {
getAddress(e.latLng);
setTimeout('getCustomAddress(0);', 500);
})
function getAddress(latLng) {
if (!geocoder) {
geocoder = new google.maps.Geocoder();
}
var geocoderRequest = {
latLng: latLng
}
geocoder.geocode(geocoderRequest, function (results, status) {
_results = results;
.
.
.
function getCustomAddress(i) {
alert(_results[i].formatted_address)
}
You might want to create a for loop to alert all the results instead of passing the result id in the getCustomAddress function.

Categories