I'm having this weird issue where when I get the result of a HTML geolocation call, I cant bind it to Vue data, but I can console.log it successfully.
Vue method:
initGeolocation: function() {
if( navigator.geolocation )
{
// Call getCurrentPosition with success and failure callbacks
navigator.geolocation.getCurrentPosition( success, fail );
}
else
{
return;
}
function success(position)
{
console.log(position.coords.latitude); //works
this.lat = position.coords.latitude; //does not work
}
function fail()
{
console.log('fail')
}
},
mounted() {
this.lat = this.initGeolocation(); // does not work
console.log(this.initGeolocation()) // returns undefined
},
Data:
lat: '',
long: '',
Any help would be very much appreciated.
The word this refers to the scope of the function. When you nest another function inside, the word this now refers to the new/ smaller scope so this.lat is no longer defined. So we capture the out this in vm and use it inside functions.
methods: {
initGeolocation: function() {
var vm = this;
if( navigator.geolocation)
{
// Call getCurrentPosition with success and failure callbacks
navigator.geolocation.getCurrentPosition( success, fail );
}
else
{
return;
}
function success(position)
{
vm.lat = position.coords.latitude; //should work now!!
}
function fail()
{
console.log('fail')
}
}
},
mounted() {
this.initGeolocation();
},
In your mounted you assign this.lat with the return of your initGeolocation() method. However this method does not return any data if it succeeds. Instead you write your result into this.lat which then will be overridden again by the void result of your method. So make sure your method initGeolocation either returns your geolocation data or you change your mounted method to call the method without assigning the return value to this.lat.
Also it seems like you just added the initGeolocation method to your component. Look into the methods property of vue components where this would belong.
So try this instead:
mounted() {
this.initGeolocation();
console.log(this.initGeolocation());
},
methods: {
initGeolocation: function() {
if( navigator.geolocation)
{
// Call getCurrentPosition with success and failure callbacks
navigator.geolocation.getCurrentPosition( success, fail );
}
else
{
return;
}
function success(position)
{
this.lat = position.coords.latitude; //does not work
}
function fail()
{
console.log('fail')
}
}
}
Related
Making my first react app. I want to update the google maps api based on the user's location.
I am receiving the error "this is undefined". I understand using .bind(this) and wrapping in an arrow function but think this case is a bit different because I am setting state inside a nested function:
constructor(props) {
super(props);
this.state = {zip: null, lat: 40.5304 , lng: -100.6534 , zoom: 3.8 };
this.updateCurrentPosition= this.updateCurrentPosition.bind(this);
}
//...
updateCurrentPosition = () => {
navigator.geolocation.getCurrentPosition(success, error);
function success(pos) {
this.setState(`{lat: ${pos.coords.latitude}, lng: ${pos.coords.longitude}, zoom: ${3.8}`)
}
function error(err) {
console.warn(`ERROR(${err.code}): ${err.message}`);
};
}
ops = () => {
return {
center: { lat: this.state.lat, lng: this.state.lng },
zoom: this.state.zoom
}
};
Arrow functions automatically bind functions to the parent class. If a function is not binded, or not an arrow function, "this" will refer only to the function itself, even if it is nested. Your success function (and failure function too) is not bound to the parent class, as you have neither binded it or defined it as an arrow function.
The problem is that this is undefined under strict mode in Javascript. You can refer to this paragraph to read more http://2ality.com/2014/05/this.html
For your particular question, when you defined success and error, the two functions are not bound to parents.
The following modification by defining the functions as arrow functions will resolve your issue.
const success = (pos) => {
this.setState(`{lat: ${pos.coords.latitude}, lng: ${pos.coords.longitude}, zoom: ${3.8}`)
}
const error = (err) => {
console.warn(`ERROR(${err.code}): ${err.message}`);
};
So, I've instead passed the functions directly as arguments to the getCurrentPosition method and it seems to work fine.
updateCurrentPosition = () => {
navigator.geolocation.getCurrentPosition( (pos) => {
this.setState({ lat: pos.coords.latitude, lng: pos.coords.longitude, zoom: 1 })
},
(err) => {
console.warn(`ERROR(${err.code}): ${err.message}`)
}
)
}
I'm trying to get a history data from Pubnub.history(), store that data and update the views by using different controllers.
I've tried creating a service:
(function(){
'use strict';
angular.module('app')
.service('pubnubService', ['Pubnub',
pubnubService
]);
function pubnubService(Pubnub){
var history;
Pubnub.history({
channel : 'ParkFriend',
limit : 1,
callback : function(historyData) {
console.log("callback called");
history = historyData;
}
});
return {
getHistory : function() {
console.log("return from getHistory called");
return history;
}
};
}
})();
The problem is, getHistory() returns the data before Pubnub.history(). I need to make sure that history data is stored on history before returning it.
Since Pubnub.history is async, your getHistory function have to be an async function too.
Try the following:
function pubnubService(Pubnub) {
return {
getHistory: function(cb) { // cb is a callback function
Pubnub.history({
channel: 'ParkFriend',
limit: 1,
callback: function(historyData) {
console.log("callback called");
cb(historyData);
}
});
}
};
}
To use this service, you can't use it as a synchronous function (i.e., like var history = Pubnub.getHistory()), you need to pass a function as parameter to act like a callback.
Correct usage:
Pubnub.getHistory(function(history) { // here you have defined an anonym func as callback
console.log(history);
});
I'm currently building a simple application of example for Google Map API GeoCoding and stumbled upon a problem with javascript.
The geocodeRequest method should assign its result value to the variable this.tempResult.
However the varriable is null when I try to print it in Listener.
The output to console is:
Listener: null
geoCodeRequest: Object
The order how output is printed seems to imply that the code in Listener run ahead before the geoCodeRequest method manage to assign the this.tempResult variable.
Is there a solution for this?
$JSKK.Class.create
(
{
$namespace :'application',
$name :'GeoCoder'
}
)
(
{
},
{
service :null,
main :null,
geoCodeInput: null,
geoCodeButton: null,
reverseGeoCodeButton: null,
reverseGeoCodeActive: null,
callback :null,
reverseCallback:null,
tempResult: null,
init: function(main, callback, reverseCallback)
{
this.main = main;
this.callback = callback;
this.reverseCallback = reverseCallback;
this.service = new google.maps.Geocoder();
this.geoCodeInput = $('#toolPannel div[data-resource=locator] input[data-action=input]');
this.geoCodeButton = $('#toolPannel div[data-resource=locator] input[data-action=geocode]');
this.reverseGeoCodeButton = $('#toolPannel div[data-resource=locator] input[data-action=reversegeocode]');
this.reverseGeoCodeActive = false;
this.createListener();
},
geoCodeRequest: function(request)
{
this.service.geocode
(
request,
function (result,status)
{
//console.debug(arguments);
if (status== google.maps.GeocoderStatus.OK)
{
this.tempResult = result[0];
console.debug(this.tempResult);
}
else
{
alert ('GeoCoder request failed!');
}
}.bind(this)
);
},
createListener: function()
{
this.geoCodeButton.click(function()
{
this.geoCodeRequest
(
{
address: this.geoCodeInput.val()
}
);
this.callback(this.tempResult);
}.bind(this) //Bind here
);
this.reverseGeoCodeButton.click(function()
{
if (!this.reverseGeoCodeActive)
{
this.main.map.setOptions({draggableCursor:'crosshair'});
this.reverseGeoCodeActive=true;
}
else if(this.reverseGeoCodeActive)
{
this.main.map.setOptions({draggableCursor:'hand'});
this.reverseGeoCodeActive=false;
}
}.bind(this)
);
google.maps.event.addListener
(
this.main.map,
'click',
function (event)
{
if (this.reverseGeoCodeActive)
{
this.geoCodeRequest
(
{
location: event.latLng
}
);
console.debug(this.tempResult);
this.reverseCallback(this.tempResult);
}
}.bind(this)
);
}
}
);
The problem is this code:
geoCodeRequest: function( request ) {
this.service.geocode( request, function( result, status ) {
if (status == google.maps.GeocoderStatus.OK) {
this.tempResult = result[0];
console.debug( this.tempResult );
} else {
alert( 'GeoCoder request failed!' );
}
}.bind(this) );
},
this.geoCodeButton.click( function() {
this.geoCodeRequest({
address: this.geoCodeInput.val()
});
this.callback( this.tempResult );
}.bind(this) );
(Sorry, but I took the liberty of reformatting so I could follow the logic better. Feel free to convert back to your own style.)
You are trying to call this.callback() before the geocoded result comes back from the server. That won't work. You need to handle the geocoding result in the callback that the geocoder uses.
You're already providing the geocoder a callback in the geoCodeRequest() method, so what you can do is add a callback to your click handler and call that from the callback in geoCodeRequest(). You could do that like this:
geoCodeRequest: function( request ) {
this.service.geocode( request, function( result, status ) {
if (status == google.maps.GeocoderStatus.OK) {
request.success( result[0] );
} else {
request.failure( status );
}
}.bind(this) );
},
this.geoCodeButton.click( function() {
this.geoCodeRequest({
address: this.geoCodeInput.val(),
success: function( result ) {
console.debug( result );
this.callback( result );
},
failure: function( status ) {
alert( 'GeoCoder request failed!' );
}
});
}.bind(this) );
Note that I also added a failure callback to handle the error condition.
The reverseGeoCodeButton.click() handler has the same problem and can be fixed with the same solution.
This is close to your original approach, but I have to wonder if the code could be simplified. Do you need these multiple levels of code? In any case, wherever you're dealing with an asynchronous result like what the geocoder gives you, you do have to handle that inside the appropriate callback instead of after the geocoder (or any asynchronous function) returns.
I have an object
var actions = {
'photos': function()
{
var self = this; // self = actions
$.get('./data.php?get=photos', function(data)
{
self.result = data;
});
},
'videos': function()
{
var self = this;
$.get('./data.php?get=videos', function(data)
{
self.result = data;
});
}
};
Each function creates one more item in actions called result
Then, instead of switch I use this (works good):
if (actions[action])
{
actions[action](); // call a function
console.log(actions);
console.log(actions.result);
}
action is a variable with value photos or videos.
console.log(actions) gives this:
Object
message: function ()
messages: function ()
profile: function ()
profile-edit: function ()
result: "<div>...</div>"
__proto__: Object
So I think there is resultitem in actions with the value "<div>...</div>".
But, console.log(actions.result) returns undefined.
Why?
I know all this code may be rewrited, but I would like to understand the reason of undefined.
Because we are dealing with asynchronous requests, we use "callbacks".
A callback is called when an asynchronous request is ready. Your request will get a response, and you send that response with the callback. The callback handles the response.
var actions = {
'photos': function(callback)
{
$.get('./data.php?get=photos', callback);
},
'videos': function(callback)
{
$.get('./data.php?get=videos', callback);
}
};
var action = 'photos';
actions[action](function(data) {
console.log(data);
});
Since you ensist on keeping the values, I would use this structure:
var actions = {
'photos': function()
{
$.get('./data.php?get=photos', function() {
this.__callback('photos', data);
});
},
'videos': function()
{
$.get('./data.php?get=videos', function() {
this.__callback('videos', data);
});
},
'__callback': function(action, data) {
this.results[action].push(data);
},
'results': {
'photos': [],
'videos': []
}
};
var action = 'photos';
actions[action]();
// use a timeout because we are dealing with async requests
setTimeout(function() {
console.log(actions.results); // shows all results
console.log(actions.results.photos); // shows all photos results
console.log(actions.results.videos); // shows all videos results
}, 3000);
gaaah what a horrible piece of code...
I'm working on a mobile app with Phonegap which will use Geolocation. I used Geolocation before but this time I considered that creating a new object wrapper for it would be better as a large part of the functionality is based on this and don't want to end up with messy code.
The solution is probably straight forward but it beats me as I've never done anything very advanced in JS using objects. Here's the code (removed unneeded parts) at the moment:
function Geolocation(maximumAge, accurate) {
this.settings = {
'maximumAge': maximumAge,
'accurate': accurate
}
}
Geolocation.prototype = {
position: {
latitude: null,
longitude: null,
altitude: null,
accuracy: null,
altitudeAccuracy: null,
heading: null,
speed: null
},
lastCheck: null,
watchId: null,
onSuccess: function(position) {
this.position.latitude = position.coords.latitude;
this.position.longitude = position.coords.longitude;
this.position.altitude = position.coords.altitude;
this.position.accuracy = position.coords.accuracy;
this.position.altitudeAccuracy = position.coords.altitudeAccuracy;
this.position.heading = position.coords.heading;
this.position.speed = position.coords.speed;
this.lastCheck = new Date(position.timestamp);
},
onError: function(error) {
console.log(error.code+' '+error.message);
},
getCoordinates: function() {
if (this.watchId == null) {
this.watchId = navigator.geolocation.watchPosition(this.onSuccess, this.onError, { maximumAge: this.settings.maximumAge, enableHighAccuracy: this.settings.accurate});
}
return {
latitude: this.position.latitude,
longitude: this.position.longitude
};
}
};
As you probably noticed already, when I call getCoordinates(), the callback success function is (I'm guessing) out of the scope of the object, therefore not knowing what "this" is... Any idea of how to get around this or what the correct implementation would be?
Thank you for your time!
Later edit: I know I need to modify the function to return the coordinates only after the position was found. Not sure on how to do this at the moment, but if you have any tips that would be great!
OK, I figured it out, partly with help from this other thread regarding binding the scope for the callback function: JavaScript Callback Scope
To return the coordinates I'm using a callback function. Included the code below and I'm marking this as community wiki - but if anyone has any suggestions please go ahead and make them. Everything seems to be working fine, but maybe I'm not doing something right here.
function Geolocation(maximumAge, accurate) {
this.settings = {
'maximumAge': maximumAge,
'accurate': accurate
}
}
Geolocation.prototype = {
position: {
latitude: null,
longitude: null,
altitude: null,
accuracy: null,
altitudeAccuracy: null,
heading: null,
speed: null
},
lastCheck: null,
callback: null,
watchId: null,
onSuccess: function(position) {
this.position.latitude = position.coords.latitude;
this.position.longitude = position.coords.longitude;
this.position.altitude = position.coords.altitude;
this.position.accuracy = position.coords.accuracy;
this.position.altitudeAccuracy = position.coords.altitudeAccuracy;
this.position.heading = position.coords.heading;
this.position.speed = position.coords.speed;
this.lastCheck = new Date(position.timestamp);
var pos = {
latitude: this.position.latitude,
longitude: this.position.longitude
};
// call callback with position and accuracy parameters
this.callback(pos, this.position.accuracy);
},
onError: function(error) {
console.log(error.code+' '+error.message);
},
getCoordinates: function(callback) {
// Helper function to bind scope to callback function as seen at https://stackoverflow.com/questions/183214/javascript-callback-scope
function bind(scope, fn) {
return function () {
fn.apply(scope, arguments);
};
}
// Assign the callback function to the local member
this.callback = callback;
// watchPosition is a method that gets called each time the position changes. Making sure it's only getting called once (watchId can be used to stop the function when necessary
if (this.watchId == null) {
// notice usage of the bind function here
this.watchId = navigator.geolocation.watchPosition(bind(this, this.onSuccess), bind(this, this.onError), { maximumAge: this.settings.maximumAge, enableHighAccuracy: this.settings.accurate});
}
}
};
Usage example:
var geo = new Geolocation(3000, true);
geo.getCoordinates(createMap);
function createMap(position, accuracy) {
// Your code here
}