running a function after a google map call has finished - javascript

I'm trying to collect location data from my users as they're entering other details in a form. So I have a google map which users can move a marker about on but I also have an input which they can type a location into.
When they click to submit the form, I want to first geocode the location they have typed and update the hidden lat lng inputs in my form before the submit actually takes place.
I know there's lots of info online but I've read loads of tutorials but I'm struggling to understand or at least to apply the tutorials to my situation.
So when a user clicks submit I want to run geocode and only submit the form when geocode is definitely finished.
$('.submitButton').click(function () {
geocode();
$("#searchForm").submit();//Then submit when geocode finishes
});
And the geocode function
function geocode(){
geocoder.geocode({
address: $('#address').val()
}, function(results, status){
if(status == google.maps.GeocoderStatus.OK){
var geoPoint = new google.maps.LatLng(results[0].geometry.location.lat(), results[0].geometry.location.lng());
map.setCenter(geoPoint);
circle.setCenter(geoPoint);
marker.setPosition(geoPoint);
$('#lat').val(geoPoint.lat());
$('#lng').val(geoPoint.lng());
} else{
alert("Can not geolocate this address.");
}
});
}
Can someone explain to me what I need to do in simple javascript dummy language? THanks

Because Geocoding service is asynchronous, geocode() will return almost immediately and $("#searchForm").submit() will submit what is available and that will be wrong (old or undefined data).
You have to move form submit call to geocode() function just after settings of lat/lng values:
...
marker.setPosition(geoPoint);
$('#lat').val(geoPoint.lat());
$('#lng').val(geoPoint.lng());
$("#searchForm").submit();
} else{
...
If, as you said in comment, geocode() function is also used somewhere else then you will have to write another function with similar functionality or prepare two different callback function.

You may pass an optional argument to geocode, e.g. a function that will be executed on success:
$('.submitButton').click(function (e) {
e.preventDefault();
//call geocode and pass the desired function as argument
geocode(function(){$("#searchForm").submit();});
return false;
});
function geocode(fnc){
geocoder.geocode({
address: $('#address').val()
}, function(results, status){
if(status == google.maps.GeocoderStatus.OK){
var geoPoint = new google.maps.LatLng(results[0].geometry.location.lat(),
results[0].geometry.location.lng());
map.setCenter(geoPoint);
circle.setCenter(geoPoint);
marker.setPosition(geoPoint);
$('#lat').val(geoPoint.lat());
$('#lng').val(geoPoint.lng());
//check if the fnc-argument is a function,
//when it does, execute the function
if($.type(fnc)==='function'){
fnc();
}
} else{
alert("Can not geolocate this address.");
}
});
}

Related

How can I keep showing a Js var in HTML after route changes?

I'm bad with Javascript and would like your help!
Hi, I'm building a Rails application and would like to add some features related to geoloction. For that I get the current location of the user with JS and then print it with HTML.
As you guys will see, my code runs every time the page loads, but when my routes change, for example: /about, /settings, /events, it simply disappear and I have to load the page again to print the HTML element.
/* CURRENT LOCATION */
function geolocationSuccess(position) {
var latitude = position.coords.latitude;
var longitude = position.coords.longitude;
var geocoder = new google.maps.Geocoder();
var latlng = {lat: latitude, lng: longitude};
geocoder.geocode({'location': latlng}, function(results, status) {
if (status === google.maps.GeocoderStatus.OK) {
if (results[0]){
var user_address = results[0].formatted_address;
document.getElementById("current_location").innerHTML = user_address;
}else {
console.log('No results found for these coords.');
}
}else {
console.log('Geocoder failed due to: ' + status);
}
});
}
function geolocationError() {
console.log("please enable location for this feature to work!");
}
$(document).ready(function() {
if ("geolocation" in navigator) {
navigator.geolocation.getCurrentPosition(geolocationSuccess, geolocationError);
} else {
alert("Geolocation not supported!");
}
});
How can I have the current location printed on this element in all my application routes?
I wouldn't like to request this information every time.
Maybe a cookie? I don't know...
or request just once every some time
What do you guys recommend? Please help me :)
You can use localStorage for this purpose: https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage
localStorage allows you to save data between browser sessions and windows.
An example usage might be:
...
// Somewhere in geocode request callback
localStorage.setItem('user_address', results[0].formatted_address)
...
// Somewhere in your render code
document.getElementById("current_location").innerHTML =
localStorage.getItem('user_address')
...

Google Maps reverse geocoding address appears after second click?

Currently if I click on a marker, error in console shows "address undefined" but if i click it again the address shows up, why is this happening?
What my listener looks like:
map.data.addListener('click', function(event) {
var lat = event.latLng.lat();
var lng = event.latLng.lng();
function getReverseGeocodingData() {
var latlng = new google.maps.LatLng(lat, lng);
// This is making the Geocode request
var geocoder = new google.maps.Geocoder();
geocoder.geocode({ 'latLng': latlng }, function (results, status) {
if (status !== google.maps.GeocoderStatus.OK) {
alert(status);
}
// This is checking to see if the Geoeode Status is OK before proceeding
if (status == google.maps.GeocoderStatus.OK) {
console.log(results[0].formatted_address);
address = (results[0].formatted_address);
}
});
}
getReverseGeocodingData(lat, lng);
infoWindow.setContent("Address: " + address + "<br>Vehicle: " + event.feature.getProperty('deviceID')+"<br> Speed: "+event.feature.getProperty('speedKPH'));
infoWindow.setPosition(event.latLng);
infoWindow.setOptions({pixelOffset: new google.maps.Size(0,-34)});
infoWindow.open(map);
});
thank you for your time and help in advance.
geocoder.geocode works asynchronously, which means the callback will be invoked later™. You are calling this method from the synchronous method getReverseGeocodingData, and then proceed to use the address data immediately afterwards.
This can't work.
Asynchronous communication can be visualized with traditional paper mail. Imagine you send a letter to Google to get the address at x,y. After you put the letter in the postbox, you don't have the result just yet, so you can't print that sign with the address on it yet. But you can do other stuff, like repainting your house (yeah, the metaphor is stretched). You will have to be patient to wait for the answer via mail.
A few days later the mailman rings, and delivers you the answer from Mountain View. It says: "x,y is at Hauptstraße 22". Now you can start printing that sign (and this is where the metaphor ends) to the status bar of your browser.
On the other hand, you can visualize synchronous communication with phone calls. You get the answer immediately, and you can't do anything else during the call. After you hung up, you got the answer.
In JavaScript, we are pretty much stuck with the asynchronous model. If this is good or bad is not for today to decide ;-)
So thanks thriqon i understood the problem,
and have come up with this solution which i'm not sure how correct it is, but it does what i need it to do. It calls for the address once they hover over the point in the background without popping up the infowindow and when they click, tada, the address is shown in the infowindow! hope this helps some people! messsssy code
map.data.addListener('mouseover', function(event) {
var lat = event.latLng.lat();
var lng = event.latLng.lng();
function getReverseGeocodingData(lat, lng) {
var latlng = new google.maps.LatLng(lat, lng);
// This is making the Geocode request
var geocoder = new google.maps.Geocoder();
geocoder.geocode({ 'latLng': latlng }, function (results, status) {
if (status !== google.maps.GeocoderStatus.OK) {
alert(status);
}
// This is checking to see if the Geoeode Status is OK before proceeding
if (status == google.maps.GeocoderStatus.OK) {
console.log(results[0].formatted_address);
address = (results[0].formatted_address);
return address;
}
});
}
getReverseGeocodingData(lat, lng);
map.data.addListener('click', function(event) {
infoWindow.setContent("Address: " + address + "<br>Vehicle: " + event.feature.getProperty('deviceID') +"<br> Speed: "+event.feature.getProperty('speedKPH')+"<br> Heading:"+event.feature.getProperty('heading'));
infoWindow.setPosition(event.latLng);
infoWindow.setOptions({pixelOffset: new google.maps.Size(0,-34)});
infoWindow.open(map);
});
});

Reusing same javascript with slightly different application

I have a problem where I have some code which I want to apply to two slightly different applications, but 75% of the code will be the same. This is where I am with things:
http://jsfiddle.net/spadez/H6SZ2/6/
$(function () {
var input = $("#loc"),
lat = $("#lat"),
lng = $("#lng"),
lastQuery = null,
lastResult = null, // new!
autocomplete;
function processLocation(callback) { // accept a callback argument
var query = $.trim(input.val()),
geocoder;
// if query is empty or the same as last time...
if( !query || query == lastQuery ) {
callback(lastResult); // send the same result as before
return; // and stop here
}
lastQuery = query; // store for next time
geocoder = new google.maps.Geocoder();
geocoder.geocode({ address: query }, function(results, status) {
if( status === google.maps.GeocoderStatus.OK ) {
lat.val(results[0].geometry.location.lat());
lng.val(results[0].geometry.location.lng());
lastResult = true; // success!
} else {
alert("Sorry - We couldn't find this location. Please try an alternative");
lastResult = false; // failure!
}
callback(lastResult); // send the result back
});
}
var ctryiso = $("#ctry").val();
var options = {
types: ["geocode"]
};
if(ctryiso != ''){
options.componentRestrictions= { 'country': ctryiso };
}
autocomplete = new google.maps.places.Autocomplete(input[0], options);
google.maps.event.addListener(autocomplete, 'place_changed', processLocation);
$('#search_form').on('submit', function (event) {
var form = this;
event.preventDefault(); // stop the submission
processLocation(function (success) {
if( success ) { // if the geocoding succeeded, submit the form
form.submit()
}
});
});
});
When the submit form button is pressed it will do the following (this works already):
Check if result is already geocoded from autosuggest click
If not, geocode
Submit the form if successful
When the Geocode button is pressed I want it to do the following (this doesn't work):
Check if result is already geocoded from autosuggest click
If not, geocode
So my question is, how can I make it so I can use the same code for both scripts without having to duplicate it twice. My idea was to do the following:
Have a function for the "submit form" button which includes submitting the form
Have a function for the geocode button which does not include submitting the form
I would rather use a function for each button rather than using some kind of flag, but I am completely stuck on how to implement this, every time I work on it I end up breaking the script completely. I am new to JavaScript.
If the only difference is that one submits and the other does not, allow a callback to be passed into the function, and the one that submits can pass a submit to the function.

Jquery Logic - Can't efficiently achieve flag check

I am new to javascript but I am having a hard time creating code which can achieve this logic:
I am trying to achieve the following logic:
If a user enters text in location box and DOES click autosuggestion Then geocode and set coded flag to "True"
If a user enters text in location DOES NOT click autosuggestion Keep coded flag to "False"
If a user changes the text in location box Make sure "coded" flag is set to "false" - Since the text doesnt match the coordinates we
fetched for it now
If a user clicks search button Check "coded" flag If true, submit form If false, process geocode, and if successful, submit form
This is how far I got before I got stuck: LINK
Even when I click on an autosuggested link, which initiates a geocode, and then do not change the content in the input box, when I hit search it says "Location not geocoded - Process location first". I feel like my logic in the programming is wrong but I am not sure how to fix it without lots of ugly repetitive code.
FULL CODE
geocode();
// SET COOKIE FOR TESTING PURPOSES
$.cookie("country", "uk");
// GEOCODE FUNCTION
function geocode() {
var coded = false;
console.log(coded);
var input = document.getElementById('loc');
var options = {
types: ['geocode']
};
var country_code = $.cookie('country');
console.log(country_code);
if (country_code) {
options.componentRestrictions = {
'country': country_code
};
}
var autocomplete = new google.maps.places.Autocomplete(input, options);
google.maps.event.addListener(autocomplete, 'place_changed', function() {
processLocation();
});
// ON SUBMIT - WORK OUT IF WE ALREADY HAVE THE RESULTS FROM AUTOCOMPLETE FUNCTION
$('#searchform').on('submit', function(e) {
console.log(coded);
e.preventDefault();
if(coded == true) {
console.log("Location already geocoded - Submitting form");
$('#searchform').submit();
}
else {
console.log("Location not geocoded - Process location first");
processLocation();
}
});
// CHECK TO SEE IF INPUT HAS CHANGED SINCE BEING GEOCODED
// IF "CODED" VAR IS FALSE THEN WE WILL GEOCODE WHEN SEARCH BUTTON HIT
$("#loc").bind("change paste keyup", function() {
var coded = false;
console.log("Content changed - Coordinates no longer valid");
});
};
// GEOCODE THE LOCATION
function processLocation(){
var geocoder = new google.maps.Geocoder();
var address = document.getElementById('loc').value;
geocoder.geocode({
'address': address
},
// RESULTS - STORE COORDINATES IN FIELDS OR ERROR IF NOT SUCCESSFUL
function(results, status) {
if (status == google.maps.GeocoderStatus.OK) {
var coded = true;
console.log("Geocode Successful");
$('#lat').val(results[0].geometry.location.lat());
$('#lng').val(results[0].geometry.location.lng());
} else {
var coded = false;
console.log("Geocode unsuccessful");
alert("Sorry - We couldn't find this location. Please try an alternative")
}
});
}
You are using too many local variables coded. YOu should define one global flag and then can use set it.
When you so
function geocode() {
....
var coded = false;
.....
}
function processLocation(){
....
var coded = true;
....
}
These are not the same coded. To access it globally. You could do somethink like
var coded = false; //initialize with a proper default value function geocode() {
function geocode() {
....
coded = false;
.....
}
function processLocation(){
....
coded = true;
....
}
Note that I'm putting coded outside of all the functions and access it without the var. You should read up on this topic .

Javascript function on submit button - Wait for it to complete

I want to trigger a function when the submit button is pressed on the form and wait for the javascript function to complete, then continue the form submission. I dont want the form to submit before the javascript function has completed.**
This is what I have at the moment:
http://jsfiddle.net/njDvn/68/
function autosuggest() {
var input = document.getElementById('location');
var options = {
types: [],
};
var autocomplete = new google.maps.places.Autocomplete(input, options);
}
<!-- Get lat / long -->
function getLatLng() {
var geocoder = new google.maps.Geocoder();
var address = document.getElementById('location').value;
geocoder.geocode({
'address': address
}, function (results, status) {
if (status == google.maps.GeocoderStatus.OK) {
var latLng = results[0].geometry.location;
$('#lat').val(results[0].geometry.location.lat());
$('#lng').val(results[0].geometry.location.lng());
} else {
alert("Geocode was not successful for the following reason: " + status);
}
});
}
<!-- Load it -->
window.onload = autosuggest;
You can intercept the form submission, abort it and send the geocoding request to Google.
When the server responds, you can re-submit the form from the callback (or display an error in case of failure).
Store the state of the request somewhere (for the sake of simplicity, I'm using a global variable in my example). In this case, it's just a flag that indicates whether or not the geocoding request was completed successfully (so that now, when the form is submitted and the listener is re-triggered, it will know not to re-send the geocoding request).
http://jsfiddle.net/njDvn/75/
Feel free to remove the console logging.
You may also want to make the lat/long fields hidden.
var GeoCoded = {done: false}; // this holds the status of the geo-coding request
$(document).ready(function(){
autosuggest(); // place your auto-suggest (now called autocomplete) box
$('#myform').on('submit',function(e){
if(GeoCoded.done)
return true;
e.preventDefault();
console.log('submit stopped');
var geocoder = new google.maps.Geocoder();
var address = document.getElementById('location').value;
// disable the submit button
$('#myform input[type="submit"]').attr('disabled',true);
// send the request
geocoder.geocode({
'address': address
},
function (results, status) {
// update the status on success
if (status == google.maps.GeocoderStatus.OK) {
var latLng = results[0].geometry.location;
$('#lat').val(results[0].geometry.location.lat());
$('#lng').val(results[0].geometry.location.lng());
// if you only want to submit in the event of successful
// geocoding, you can only trigger submission here.
GeoCoded.done = true; // this will prevent an infinite loop
$('#myform').submit();
} else { // failure
console.log("Geocode was not successful for the following reason: " + status);
//enable the submit button
$('#myform input[type="submit"]').attr('disabled',false);
}
});
});
});
First you need to prevent the form to be submitted, add a return false at the end of getLatLng() function.
Then when the geocoding is done, submit the form manually with document.getElementsByTagName('form')[0].submit()
Here's an updated jsfiddle: http://jsfiddle.net/njDvn/70/
myFunction = new function(callback) {
$.ajax({...}).done(callback());
}
myFunction(new function() {
getLatLng();
});
Here you need to call myFunction onSubmit event.
As MasterAM has stated, all you need do is the following:
/// I've included the following function to allow you to bind events in a
/// better way than using the .onevent = function(){} method.
var addEvent = (function(){
if ( window.addEventListener ) {
return function(elm, eventName, listener, useCapture){
return elm.addEventListener(eventName,listener,useCapture||false);
};
}
else if ( window.attachEvent ) {
return function(elm, eventName, listener){
return elm.attachEvent('on'+eventName,listener);
};
}
})();
/// add another window.onload listener
addEvent(window,'load',function(){
/// find the form, obviously should use whatever id you have on your form
var form = document.getElementById('my_form');
form.preventSubmit = true;
/// add our onsubmit handler
addEvent(form,'submit',function(e){
/// only stop the form if we haven't already
if ( form.preventSubmit ) {
/// place/call whatever code you need to fire before submit here.
alert('submit was blocked!');
/// after that code is complete you can use the following
form.preventSubmit = false;
form.submit();
/// return false prevents the submit from happening
return false;
}
});
});

Categories