I wanted to play with geolocation API on my Android. I know that there is a "navigator" object that is defined and that should be used to aquire user position. So, I created this sample code:
function GeolocationTester()
{
// here I want to store all acquired locations
this.locations = new Array();
alert("this.locations defined: " + this.locations);
this.onSuccess = function(position)
{
alert("Entered onSuccess");
alert("this.locations defined: " + this.locations);
}
this.onError = function(error)
{
alert("error acquiring location");
}
navigator.geolocation.watchPosition(this.onSuccess, this.onError, { enableHighAccuracy: true });
}
And it doesn't work for me. Each time watchPosition call onSuccess the this.locations field isn't defined (and it is defined just after new Array). I known that I'm doing somethind wrong, but as it is one of my JavaScript attempts, not sure what. So, anybody could find a problem here?
The problem is with the scoping of this. When the onSuccess or onError is called, this isn't bound to the object containing the locations array. You need to create an explicit variable outside of the functions to which the array should be assigned and then use this variable in the callbacks, like this:
var allLocations = this.locations = [a, b, c];
this.onSuccess = function(position) {
alert("allLocations: " + allLocations);
alert("this.locations: " + this.locations);
}
Its cause you using this. This will change cause its depends on the context your function is calling on. Just use the scope of the function to declare location:
function GeolocationTester()
{
// here I want to store all acquired locations
var locations = [];
alert("locations defined: " + locations);
function onSuccess(position) {
alert("Entered onSuccess");
alert("locations defined: " + locations);
}
function onError(error){
alert("error acquiring location");
}
navigator.geolocation.watchPosition(onSuccess, onError, { enableHighAccuracy: true });
}
To really understand what this read this blog post http://dmitrysoshnikov.com/ecmascript/chapter-3-this/
Try to define onSuccess like this:
this.onSuccess = (function(locations) {
return function(position)
{
alert("Entered onSuccess");
alert("this.locations defined: " + locations);
}
})(this.locations);
Related
I am trying to invoke a javascript method. I am building the html at run time by string concatenation.
$scope.getRoute = function (isRouteFormValid) {
routingDemoPageService.executeService(serviceURL, 'admin', 'admin').then(function (response) {
function tryingOnceAgain() {
alert('called.....');
}
var markers = L.markerClusterGroup({
showCoverageOnHover:false,
chunkedLoading: true
});
var geojsonLayer = L.geoJson(response, {
onEachFeature: function(feature, layer){
var UIDValue = (feature.properties['uid'] !== null ? Autolinker.link(String(feature.properties['uid'])) : '');
var popupContent = '<table>' +
'<tr><th scope="row">Edit</th><td></td></tr>' +
'<tr><th scope="row">uid</th><td>' + UIDValue + '</td></tr></table>';
layer.bindPopup(popupContent);
}
});
markers.addLayer(geojsonLayer);
$scope.map.addLayer(markers);
$scope.map.fitBounds(markers.getBounds());
})['catch'](function (error) {
});
}
When i click on the link, which invokes tryingOnceAgain method, i am getting following error
ReferenceError: tryingOnceAgain is not defined
I am not sure why i am getting following error.
Can someone please provide any pointers what am i doing wrong.
javascript:tryingOnceAgain() is referenced to a function in the global scope but you defined tryingOnceAgain function inside function (response) { scope.
To fix that you have to move your tryingOnceAgain function to global scope.
Or just assign it to window object without changing physical place:
window.tryingOnceAgain = function() {...}
Your function-definition for tryingOnceAgain() exists only inside the function where it is defined, in this case $scope.getRoute().
This makes that only code inside $scope.getRoute() can call tryingOnceAgain() and it'll run.
You'll have to define tryingOnceAgain() outside of $scope.getRoute() or make it a public property and call it like that inside the HTML.
This code is not working as expected. I am trying to use the Google Geolocation API to figure out my current location. However, when I try to log the result for the google.maps.LatLng object, I got (0,0) as the latitude and longitude coordinates.
$(document).ready(function(){
var func = {
lat:0,
long:0,
success:function(pos) {
var crd = pos.coords;
this.lat = crd.latitude;
this.long = crd.longitude;
},
error:function(err) {
console.log('ERROR(' + err.code + '): ' + err.message);
},
init:function(){
navigator.geolocation.getCurrentPosition(this.success, this.error);
}
};
func.init();
$('button').on('click',function(){
var loc = new google.maps.LatLng(func.lat, func.long);
alert(loc);
});
});
However, the code underneath works. All I did was changing "this" keyword to the object's name. It shouldn't make a difference.
$(document).ready(function(){
var func = {
lat:0,
long:0,
success:function(pos) {
var crd = pos.coords;
func.lat = crd.latitude;
func.long = crd.longitude;
},
error:function(err) {
console.log('ERROR(' + err.code + '): ' + err.message);
},
init:function(){
navigator.geolocation.getCurrentPosition(func.success, func.error);
}
};
func.init();
$('button').on('click',function(){
var loc = new google.maps.LatLng(func.lat, func.long);
alert(loc);
});
});
I am not sure why the code snippet on the top produces incorrect output? I am not too familiar with Objected Oriented JavaScript. I would appreciate it if anyone could help me understand what is going on.
In your first example, when you call:
getCurrentPosition(this.success, this.error);
You are merely passing the success and error functions into getCurrentPosition. Even though you reference them here via this, that is not carried through to the point where the functions are actually called. It only passes the function references themselves, not the this value that you were using here.
Another way to understand the problem: the value of this inside a function is determined at the time the function is called. When you write foo.bar() you are calling the bar() function with foo as the this value inside the function. But when you write foo.bar without the (), you are only getting a reference to bar itself. foo is out of the picture after that. If you pass foo.bar into another function which expects a callback, when it finally calls bar() there is no longer any association with foo.
That's why your second example works. It does not depend on this but uses func which is valid throughout the outer function.
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));
Eric Miraglia of Yahoo/Google presents a very clean looking way to implement information hiding in JavaScript:
http://www.yuiblog.com/blog/2007/06/12/module-pattern/
Please note some experiments here:
http://jsfiddle.net/TvsW6/5/
My question is, why can I access the seemingly "public" variable "this.setting2" (and of course not _setting1) YET I cannot access the function "this.logSetting_priv" although it is in the same scope as this.setting2 (isn't it!?!?!?) Does any one know why?
Also, with the use of the return object for the public methods, I can't seem to add a function as I might normally with "LogSystem.prototype.publicFunc1." Why is that?
Mystery of the ages . . .
Pls checkout my JSFiddle but the JS is also below:
function LogSystem() {
//default
var _divId = "log";
var _setting1 = "default stuff";
this.setting2 = "default stuff as well";; //This is accessible!
function _printLog(msg) {
msg = msg || "";
$("#" + _divId).append(msg + "<br/>");
};
//this is **not** accessible - bc of return object below?
this.logSetting_priv = function () {
_printLog("PRIV: Setting1 is: " + _setting1);
_printLog("PRIV: Setting2 is: " + this.setting2);
};
/*
* Key Distinguishing feature of this pattern
*/
return {
printLog: function (msg) {
console.log("PRINTING:" + msg);
_printLog(msg);
},
logSetting_pub: function () {
this.printLog("PUB: Setting1 is: " + _setting1);
this.printLog("PUB: Setting2 is: " + this.setting2);
},
publicFunc2: function () {
_setting1 = "Fixed Deal returnFunction";
this.setting2 = "floating hamster";
}
};
};
//THIS DOESNT WORK!! . . . . bc of the return object??
LogSystem.prototype.publicFunc1 = function () {
_setting1 = "Fixed Deal";
this.setting2 = "floating midget";
};
/*******************************/
/*********Testing Code**********/
/*******************************/
$(document).ready(function () {
var logInst = new LogSystem();
//TESTING METHODS!
try {
logInst.publicFunc1(); //THIS DOESNT WORK!!
} catch (e) {
logInst.printLog("The call to the prototype function does not work - WHY?");
logInst.publicFunc2();
}
try {
logInst.logSetting_pub();
logInst.logSetting_priv();
} catch (e) {
logInst.printLog("ERR!!: " + e.message);
}
//TESTING MEMBERS!
logInst.printLog("We know this does not work? " + logInst._setting1); //undef
logInst.printLog("Why Does THIS WORK? " + logInst.setting2); //def
});
Thank you!
EDIT: Holy crap - and when I manipulate the prototype of the INSTANCE variable, i seem to break the whole object that was returned: http://jsfiddle.net/TvsW6/7/
If any one understands JS at this level, PLEASE explain that! :)
Thank you all so much. Obviously any one in this conversation is at a level way beyond "I do some jQuery" :)
Using private instance variables prevents you from using prototype (functions that need to access them need to be in the constructor body where the privates are declared with var) at the end of this answer is link to a pattern that implements protected. It may take some time to understand how prototpe works and would advice trying to understand the basic workings first before trying to mix it with closures to simulate private/public modifier.
Pointy answered you question correctly that when invoking a function with new but then returning an object would not return the Object referred to as this in the function:
function Test(){
this.name="some test";
return {name:"something else"};
}
console.log((new Test()).name);//name:something else
Not returning an object or returning a primitive (string, boolean, number) would cause the this object to be returned:
function Test(){
this.name="some test";
return "hello";
}
console.log((new Test()).name);//name:some test
Your constructor is returning a different object than the one build implicitly with new. Thus, inside the constructor this refers to a different object than the one you actually end up with outside, and that object doesn't have a property called "logSetting_priv".
I am new to backbonejs. I am trying to pass the correct this object to a callback function where that function is a method of the view.
My current solution:
APP.LocationFormView = APP.View.extend({
initialize: function () {
if (navigator.geolocation) {
var that = this;
var success = function(position) {
_.bind(that.onSuccessUpdatePos, that, position)();
};
var error = function(error) {
_.bind(that.onFailUpdatePos, that, error)();
}
navigator.geolocation.getCurrentPosition(success,
error);
} else {
}
},
onSuccessUpdatePos: function(position) {
// We can access "this" now
},
onFailUpdatePos : function(error) {
// We can access "this" now
}
});
Is this a correct way to achieve what I want?
Is there any less verbose solution for this?
This is how I would do it. One nice aspect of bindAll is that if you add additional functions to LocationFormView they will automatically have this bound.
APP.LocationFormView = APP.View.extend({
initialize: function () {
_.bindAll(this);
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(this.onSuccessUpdatePos,
this.onFailUpdatePos);
} else {
}
},
onSuccessUpdatePos: function(position) {
// We can access "this" now
},
onFailUpdatePos : function(error) {
// We can access "this" now
}
});
_.bind is for later binding. What you would normally do is this:
that.onSuccessUpdatePos(position); // that is already the context
But instead, you can just pass it directly:
var success = _.bind(that.onSuccessUpdatePos, that, position);
var error = _.bind(that.onFailUpdatePos, that, error);
navigator.geolocation.getCurrentPosition(success, error);
That is, if that feels more clear to you than the "manual" solution.