Asynchronous Call in Google Map - google-maps

Ok, Here is my JS Function :
function GetLatLong() {
var geocoder = new google.maps.Geocoder();
var address = $('#txtAddress').val() + ', ' + $('#txtCity').val() + ', ' + $find('drpState').get_text() + ', ' + $find('drpCountry').get_text();
var result = false;
geocoder.geocode({ 'address': address }, function (results, status) {
if (status == google.maps.GeocoderStatus.OK) {
var location = results[0].geometry.location;
$('#hiddenLatLong').val(location);
result = true;
}
else {
alert("Geocode was not successful for the following reason: " + status);
result = true;
}
});
return result;
}
Now, what i want is, i want to return the result once i store the value in hidden field. Here what happens is, i am calling this function on button click, but as the call is asynchronous, it returns false on the click and i cannot get the resultant value in hidden field. Is there any way where i can wait or some work around with which it can be obtained in the hidden field?

The second parameter in the geocode function is a callback function that will be invoked if the geocode results return. So from your given code the hidden value should be set if Google returns a result.
However your GetLatLong() function is completed earlier and therefore returns false. So you should not make your processing dependent on this method return value.

Related

Address validation using Google Maps - Check Address

I'm letting users add an address to their posts using Google Maps.
If a user enters nothing for the map or enters odd special characters (#$!##) it crashes the website and gives me this error:
var lat = data.results[0].geometry.location.lat;
^
TypeError: Cannot read property 'results' of undefined
I'm trying to figure out a way to check for this error when the form is submitted. So far I have had zero luck. Is it possible to check for this error?
I've seen code like this but everything in undefined and I'm not sure how to define it in this case and frankly I'm not sure how the code below is operating.
if (status == google.maps.GeocoderStatus.OK) {
var lat = results[0].geometry.location.lat();
var lon = results[0].geometry.location.lng();
searchStores(lat, lon);
} else {
$addressErrorP.removeClass('hide');
}
Thanks for any help!
Figured it out: Simple and works perfectly.
Step 1: Make sure this is on your page.
<script type="text/javascript" src="https://maps.google.com/maps/api/js?sensor=false"></script>
Then
$('#checkingAddress').click(checkGeocode)
function checkGeocode() {
var addr = document.getElementById('location')
// Get geocoder instance
var geocoder = new google.maps.Geocoder()
// Geocode the address
geocoder.geocode({ address: addr.value }, function (results, status) {
if (status === google.maps.GeocoderStatus.OK && results.length > 0) {
alert('looks good')
// set it to the correct, formatted address if it's valid
addr.value = results[0].formatted_address
// show an error if it's not
} else alert('Invalid address')
})
}

Why Google place AutocompleteService callback not getting executed?

I have following code for place AutocompleteService() as:
var strAddr = '';
if (address.value != "") {
var service = new google.maps.places.AutocompleteService();
service.getQueryPredictions({ input: address.value }, function (predictions, status) {
console.log('1');
if (status == google.maps.places.PlacesServiceStatus.OK) {
console.log('Original Response: ' + predictions[0].structured_formatting.secondary_text);
strAddr = predictions[0].structured_formatting.secondary_text;
}
});
console.log('strAddr: ' + strAddr);
console.log('2');
}
console.log('3');
I am getting response in console as:
strAddr:
2
3
1
Original Response: //gettting matched address here
But it should be:
1
Original Response: //matched address here>
strAddr: //matched address here
2
3
Why the callback not executing in sequence as expected?
The getQueryPredictions function is asynchronous (it's probably happening via AJAX). So you can't guarantee that it will complete before the code immediately after it.
e.g. the call to getQueryPredictions might take perhaps 0.5 seconds. So while that's happening, these two lines will be able to execute immediately:
console.log('strAddr: ' + strAddr);
console.log('2');
You can't rely on strAddr being available in any code that happens outside of your callback function that's waiting for the getQueryPredictions response.
It is not guaranteed that you get this
1
Original Response: //matched address here>
strAddr: //matched address here
2
3
Because this ( new google.maps.places.AutocompleteService(); ) is asynchronous call and your callback can be called at any sequence

Geolocation data saved in a variable/localstorage

i have a problem with the Geolocation API -.-'
I'm using the FirefoxOS Boilerplate App (https://github.com/robnyman/Firefox-OS-Boilerplate-App) for create a simple html5 app.
The problem is simple: I would like the data (lat/lon) obtained from the API are returned by a function as an array.
All the examples that i have found uses the data on the fly for show the maps or insert in a div (as also this boilerplate).
navigator.geolocation.getCurrentPosition(function (position) {
geolocationDisplay.innerHTML = "<strong>Latitude:</strong> " + position.coords.latitude + ", <strong>Longitude:</strong> " + position.coords.longitude;
geolocationDisplay.style.display = "block";
},
function (position) {
geolocationDisplay.innerHTML = "Failed to get your current location";
geolocationDisplay.style.display = "block";
});
This is the code of the boilerplate for the Geolocation...
I would want a function like get_location that return me the data, but after days of testing/google search I gave up and I ask you who are more experienced with callback/scope in Javascript of me.
The opntions that i have evaluated it's save the data in a hidden div or save with localstorage/cookies.
Thanks for the help!
EDIT 20/11:
function load_location() {
navigator.geolocation.getCurrentPosition(save_location, handleLocationError, {maximumAge: 0, timeout: 1000, enableHighAccuracy: true});
}
function handleLocationError(error) {
alert(error.code + ' - ' + error.message);
}
function save_location(position) {
localStorage.clear();
ls_save('latitude',position.coords.latitude);
ls_save('longitude',position.coords.longitude);
ls_save('accuracy',position.coords.accuracy);
ls_save('altitude',position.coords.altitude);
ls_save('altitudeAccuracy',position.coords.altitudeAccuracy);
ls_save('heading',position.coords.heading);
ls_save('speed',position.coords.speed);
}
function ls_save(key,value) {
localStorage.setItem(key, value);
}
function get_location() {
while(typeof localStorage['latitude'] === 'string') {
return localStorage.getItem("latitude");
}
}
load_location();
//Code
console.log(get_location());
The new code after the comments. I do not know how performance this solution...
I have replaced console.log with alert and i get undefined then in some cases is not asynchronous.
Edit: 22/11:
Fixed the while
You can return the geolocation data as an array, by doing something like this:
function doSomethingWithGeo(geo) {
console.log(geo);
}
function get_location() {
navigator.geolocation.getCurrentPosition(function (position) {
doSomethingWithGeo([[position.coords.latitude, position.coords.longitude]]);
});
}
When you call get_location, it will retrieve the geolocation coordinates, and will call the function doSomethingWithGeo with the array you wanted. If you want to store your data, you can do it in the doSomethingWithGeo function.
Let me know if it's not what you were looking for.

Recommended practice for application exception handling in AngularJS

I am currently exploring possible methods to handle application-wide exceptions in AngularJS.
One of the things we really wanted to avoid was wrapping multiple parts of the application in nested try/catch blocks, but handle things cleanly - i.e throw an exception in response to a promise.
Has anyone covered this issue before and have any recommendations?
Any suggestions on how to pick up exceptions in services as well as controllers/directives. (See below - broadcast works ok, but only if you can attach a listener to a scope).
Progress so far
A few short design goals:
Allow exceptions from one part of the application to be handled elsewhere - or possibly multiple places (i.e. 'display error notification to user', 'disable widget').
Provide central management of common error conditions - i.e. log to server, display notification to user, redirect to login.
Allow exceptions to be thrown from controllers, directives, services etc.
Eventually allow localized messages.
The current leaning of my team is to write a service to handle exceptions, which would expose a range of simple calls:
exceptionService.warn('exception_token');
exceptionService.crit('another_exception_token');
This service would then format an 'exception' object and broadcast this from the rootscope. This would allow a default handler to watch for any broadcasts and apply default actions, as well as allow custom listeners to be set in others scopes, which could handle more specific conditions - i.e. disable a part of the UI.
var exception = {
token: 'exception_token',
severity': 'crit'
};
// broadcast exception
$rootScope.$broadcast(
'application_exception',
exception
);
I was thinking about the same recently, and it occurred to me that when it comes to a good error handling in javascript, it is irrelevant which framework you are using, Angular on something else. I wrote one such error handler recently for an AngularJS project, but I did it in a way it can be used in any framework.
Here's the complete code. You can either use it directly, or modify to your needs...
/*
Factory errorFact is to simplify error handling and reporting in other objects.
It supports detailed error output as a text string and into the browser's console.
Usage example:
A function that supports return of an error object would have the following declaration
as its very first line:
var e = errorFact.create("objectName.funcName", arguments);
- in this declaration we specify the full object + method name as the first string parameter,
- and as the second parameter we pass javascript's reserved variable called arguments, which
provides reference to all of the function's parameters for logging.
When an error occurs, the function would return:
return e.error("Error description text");
- this line will create and return a complete error context.
When a function that supports return of an error object makes a call into another
function that also supports the error context, then it can return the nested error
result by passing the embedded error to the current error object instead of the error
text.
Example:
var e = errorFact.create("objectName.funcName", arguments);
var data = callAnotherFunc(...); // calling a function that support an error object;
if(data.isError){ // If an error was triggered;
return e.error(data); // return that error from the current context;
}
The top-level code that calls an error-returning function would do verification
and if an error occurred, log all its details into console (typically).
Example:
var data = getData(...);
if(data.isError){
data.log(); // Output all the error details into the browser's console;
}
*/
"use strict";
app.factory("errorFact", function(){
return {
// creates a new error context;
create: function(method, args){
var result = {
// initiates and returns the error context;
error: function(msg){
this.info.isError = true;
if(msg.isError){
this.info.details.caller = msg;
}else{
this.info.details.msg = msg;
}
return this.info;
},
info:
{
isError: false,
details: {},
log: function(){
if(this.isError){
console.error(this.format());
}
},
// formats complete error details into a text string;
format: function(){
if(this.details.caller){
var txt = this.details.caller.format();
txt += "\nCALLER: " + this.details.method + "(" + this.formatArguments() + ")";
return txt;
}
if(this.details.method){
return "Error calling " + this.details.method + "(" + this.formatArguments() + "): " + this.details.msg;
}else{
return this.details.msg;
}
return "";
},
// formats function argument details into a text string;
formatArguments: function(){
if(!this.details.args){
return "";
}
var params = "";
for(var i = 0;i < this.details.args.length;i ++){
if(params.length > 0){
params += ",";
}
var p = this.details.args[i];
if(p === undefined){
params += "undefined";
}else{
if(p === null){
params += "null";
}else{
if(typeof(p) == "object"){
params += "Object";
}else{
params += p;
}
}
}
}
return params;
}
}
};
if(method){
result.info.details.method = method;
}
if(args){
result.info.details.args = args;
}
return result;
}
}
});
Below is a factory that shows how it is used:
"use strict";
app.factory('moduleFact', ['errorFact', function(errorFact){
return {
// Locates existing module and expands its key Id references
// into corresponding object references:
// - If 'hintGroupId' is present, property 'hints' is added from
// the corresponding hint group.
// - If 'repModules' is present, properties 'question' and 'refs'
// are added.
// On success, return the expanded module object.
// On failure, returns an error object.
//
// NOTE: Currently supports only the first value in repModules.
expandModule: function(moduleData, moduleId){
var e = errorFact.create("moduleFact.expandModule", arguments);
if(!moduleData || !moduleData.modules || !moduleId){
return e.error("Invalid parameters passed");
}
var mod = this.findModule(moduleData, moduleId);
if(mod.isError){
return e.error(mod);
}
var src = mod;
if(mod.repModules){
var repId = mod.repModules[0];
if(!repId){
return e.error("Invalid repModules encountered");
}
///////////////////////////////////////
// temporary check to throw a warning:
if(mod.repModules.length > 1){
console.warn("Multiple values in property repModules: " + JSON.stringify(mod.repModules) +
", which is not supported yet (only the first value is used)");
}
///////////////////////////////////////
src = this.findModule(moduleData, repId);
if(src.isError){
return e.error(src);
}
}
if(src.question){
mod.question = src.question;
}else{
return e.error("Question not specified");
}
if(src.refs){
mod.refs = src.refs;
}
if(src.hintGroupId){
var hg = this.findHintGroup(moduleData, src.hintGroupId);
if(hg.isError){
return e.error(hg);
}
mod.hints = hg.hints;
}
return mod; // needed extra: expand attribute repModules
},
// Expands all the modules and returns the data;
expandAllModules: function(moduleData){
var e = errorFact.create("moduleFact.expandAllModules", arguments);
if(!moduleData || !moduleData.modules){
return e.error("Invalid parameters passed");
}
for(var i = 0;i < moduleData.modules.length;i ++){
var result = this.expandModule(moduleData, moduleData.modules[i].id);
if(result.isError){
return e.error(result);
}
}
return moduleData;
},
// Locates and returns module by its Id;
findModule: function(moduleData, moduleId){
var e = errorFact.create("moduleFact.findModule", arguments);
if(!moduleData || !moduleData.modules || !moduleId){
return e.error("Invalid parameters passed");
}
for(var i = 0;i < moduleData.modules.length;i ++){
if(moduleData.modules[i].id == moduleId){
return moduleData.modules[i];
}
}
return e.error("Module with Id = " + moduleId + " not found");
},
// Locates and returns Hint Group by its Id;
findHintGroup: function(moduleData, hintGroupId){
var e = errorFact.create("moduleFact.findHintGroup", arguments);
if(!moduleData || !moduleData.hintGroups || !hintGroupId){
return e.error("Invalid parameters passed");
}
for(var i = 0;i < moduleData.hintGroups.length;i ++){
if(moduleData.hintGroups[i].id == hintGroupId){
return moduleData.hintGroups[i];
}
}
return e.error("Hint Group with Id = " + hintGroupId + " not found");
}
}
}]);
So, when you have such factory in place, your high-level code, such as in a controller would just log any issues as shown in the example below:
"use strict";
app.controller('standardsCtrl', ['$scope', 'moduleFact', function($scope, moduleFact){
var data = ...//getting data;
var mod = moduleFact.expandAllModules(data);
if(mod.isError){
mod.log(); // log all error details into the console;
}else{
// use the data
}
});
}]);
You can override the $exceptionHandler in order to pass the exceptions to your own central service for exceptions, but the $exceptionHandler seems to only receive the exceptions thrown from your controllers, directives, etc... but not for the exceptions originated from ajax calls. For those exceptions you can implement an interceptor like the one described in this page:
EDITED: Link is dead permanently.
Archive.org link
whats your opinion to create a centralized error handling function for your app
so whenever an error happened with your frontend tear (angular, API calls,...) it executed, so no need to write your error handling every time
so here is my code
(function () {
'use strict';
angular
.module('app')
.factory('$exceptionHandler', ExceptionHandler);
ExceptionHandler.$inject = ['$injector']; //for minification
function ExceptionHandler($injector) {
var $log, sweetAlert, $translate;
return function exceptionHandler(exception, cause) {
// Add DI here to prevent circular dependency
$log = $log || $injector.get('$log');
sweetAlert = sweetAlert || $injector.get('sweetAlert'); //19degrees.ngSweetAlert2
$translate = $translate || $injector.get('$translate');
// $loggerService = $loggerService || $injector.get('$loggerService');
var title, message;
title = $translate.instant('General error title');
message = $translate.instant('General error message', { exceptionMessage: exception.message });
sweetAlert.error(title, message);
$log.error(exception, cause);
// loggerService.logErrorsToBackend(exception, cause);
};
}
})();
I'm not sure if this approach considered to be a best practice but hope it helps you.

Google Maps API Geocode Synchronously

I was wondering if its possible to geocode something using googlemaps api synchronously so instead of waiting for a callback function to be called, it would wait for a value to be returned. Has anyone found a way to do something like this.
P.S.: I'm using version 3 of the api
Yes, what you are trying to achieve is possible, although a synchronous request is not needed.
Look at this code
function StoreGeo()
{
var address = $('input[name=zipcode]').val() + ', ' + $('input[name=city]').val();
geocoder.geocode( { 'address': address}, function(results, status) {
if (status == google.maps.GeocoderStatus.OK) {
var ll = results[0].geometry.location.toString();
llarr = ll.replace(/[\(\) ]/g, '').split(',');
for(i = 0; i < llarr.length;i++)
{
$('#form').append($('<input type="hidden" name="'+(i == 0 ? 'lat' : 'long')+'">').val(llarr[i]));
}
$('#form').submit();
}
else
{
alert(status);
}
});
$('#form').unbind('submit');
return false;
}
$(document).ready(function () {
//init maps
geocoder = new google.maps.Geocoder();
$('#form').bind('submit',function() {
StoreGeo();
});
});
So, attach submit handler to the form, when it is submitted do the geo request based on the address info from your form. But at the same time postpone submitting by returning false in the handler.
The response handler will make 2 hidden textfields 'lat' and 'long' and store the response. finally the form is submitted by client script, including the two new fields. At the server side you can store them in the DB.
!! Note that this is possible, but is probably against the google terms, like noted above.
The Geocoder calls your callback function with the value. That's the only way to do it. If it were synchronous, your script would freeze while it waited for the Geocode to process. There really isn't any reason to do it like that.
What exactly are you trying to accomplish?
I simply use a flag during form submit to know when submit should pass or when it should wait for geocoding. When geocoding is done it will then re-submit the form again.
var hasGeocoded = false;
searchFrom.on('submit', function(){
//If not geocoded yet
if (!hasGeocoded) {
var geocoder = new google.maps.Geocoder();
var location = locationEl.val();
geocoder.geocode({'address': location}, function (results, status) {
hasGeocoded = true;
if (status == google.maps.GeocoderStatus.OK) {
$('#coords').val(
results[0].geometry.location.lat() + ',' + results[0].geometry.location.lng()
);
}
searchFrom.submit();
});
return false; //wait for geocoder to finish and re-submit the form
}
return true;
});