Preprocess form data asynchronously in javascript - javascript

One of my form fields is address. Before sending it to the server I want to transform it to latitude and longitude. This requires sending asynchronous request to some external service (e.g. Google Maps API). What's the proper way to do this?
My geocoding snippet:
function geocodeAddress() {
var address = document.getElementById("address").value;
geocoder.geocode({"address": address}, function(results, status) {
if (status === google.maps.GeocoderStatus.OK) {
var lat = results[0].geometry.location.lat()
document.getElementById("latitude").value = lat
} else {
alert("Geocode was not successful for the following reason: " + status);
}
});
}
I see several options:
Use onsubmit event handler - first return false (to wait for geocoding to complete), then in a separate callback set latitude field and submit form.
Use onchange event handler for address field - this requires blocking submit button until we geocode the address; it's also tricky if we prepopulate form values after submit it (e.g. filter rows)
Obviously I can also parse it on the server side, but this may result in one user using all my "quota" in the external service and I'd have to implement some kind of fairness, which I'd like to avoid.
So what's the proper way to solve this kind of pre-processing of form values in javascript?

You could consider the following approach:
prevent default behavior of button via Event.preventDefault()
resolve address via google.maps.Geocoder.geocoder function
once the address is resolved resume form submit: form.submit()
Example
function saveData(e) {
e.preventDefault(); //1.stop default behaviour of button
var address = document.getElementById("txtAddress").value;
resolveAddress(address,
function(results) {
document.getElementById("txtLat").value = results[0].geometry.location.lat();
document.getElementById("txtLng").value = results[0].geometry.location.lng();
document.getElementsByTagName('form')[0].submit(); //2.resume form submit
},
function(status) {
console.log('An error occured while resolving an address');
});
}
function resolveAddress(address,success,error) {
var geocoder = new google.maps.Geocoder();
geocoder.geocode({ "address": address }, function (results, status) {
if (status === google.maps.GeocoderStatus.OK) {
success(results);
} else {
error(status);
}
});
}
Plunker

Related

Google Places Autocomplete API Over Limit Solutions

I have this error: "You have exceeded your daily request quota for this API."
Currently, the form is set up to force select the first match from the API.
I would like to not force select the first match when exceeding this daily limit. I thought the status= "OVER_QUERY_LIMIT" would work, but turns out this is the status I get = "ZERO_RESULTS".
Anyone have any idea why "ZERO_RESULTS" come back instead of "OVER_QUERY_LIMIT"?
Here is a piece of my code:
geocoder.geocode({"address": firstResult}, function (results, status) {
if (status == google.maps.GeocoderStatus.OK) {
var formattedResult = results[0].formatted_address;
$("#input-location").val(formattedResult);
}
}
if (status == "OVER_QUERY_LIMIT") {
$(".btn-search-submit").removeAttr('disabled');
return;
}
if (status == 'ZERO_RESULTS') {
$(".search-no-results").remove();
$(".btn-search-submit").attr('disabled', true);
$(".pac-container").show();
$(".pac-container").append(
"<div class='search-no-results'>" +
"<p><strong> Sorry, no results</strong></p>" +
"<p>Please check your spelling or try a zipcode</p>" +
"</div>");
setTimeout(function(){
$(".pac-container").hide();
}, 3000);
}
});
}
This sounds like more of a business/software engineering question than a programming question.
You can see the cost of Google Maps API requests here: https://developers.google.com/maps/premium/usage-limits#limitexceeded
And you can see the pricing plans available here: https://developers.google.com/maps/pricing-and-plans/#details
If you're going over the limit, it might be time to just upgrade the pricing plan.

Changing input value via Javascript before form submission? PHP

Been through a number of questions and nothing seems to work for my case.
Okay so, I have a form with a single input (search) field and a submit button. When the user enters a value (an address in this case) and hits submit, that address value needs to be sent to a JavaScript function which converts it into Longitude and Latitude values, and then these coordinates need to be sent to the destination page instead of the address they input.
Here is my code so far:
HTML
<form name="searchform" action="required/results.php" method="get" id="searchbar" onsubmit="convertToCoords()">
<input type="text" name="input" id="address" placeholder="Search an address or suburb...">
<input type="submit" class="searchbtn" value="Search">
</form>
JS
function convertToCoords() {
var address = document.getElementById("address").value; // Get the input address from the textbox
/* Address to Coordinates conversion (Disregard) */
geocoder = new google.maps.Geocoder();
geocoder.geocode( { 'address': address}, function(results, status) {
if (status == google.maps.GeocoderStatus.OK) {
// Get the Lat. and Long.
var searchedLat = results[0].geometry.location.lat();
var searchedLong = results[0].geometry.location.lng();
// Set address input value to coordinates, then submit form
document.getElementById('address').value = searchedLat + ',' + searchedLong;
document.getElementById('searchbar').submit();
} else {
alert("Geocode was not successful for the following reason: " + status);
}
});
}
PHP on destination page (for testing)
<?php
if (isset($_GET['input'])) {
echo $_GET['input']; // get value of search input
}
?>
So at the moment what is happening is it is just echoing the address they input, no conversion happens.
I don't know (or care) when the conversion should happen, e.g. whether it should happen before submission or after.
Thanks for any feedback!
I made a basic example of how it could work, please see the JSFiddle here: http://jsfiddle.net/5mMtm/1/
The principle is:
capture the form's submit
do an ajax call to an API (in your case geocoder) with the data entered
change form input value and send/submit to own endpoint
I'm using jQuery in the example:
$(function(){
var $form = $('#my-form'), // the form
$adrInput = $form.find('[name=address]'), // the address input field
isSearching = false; // a flag to prevent multiple submits
// on form submit event
$form.on('submit', function(e){
// Prevent submit event
e.preventDefault();
// Don't start another search while
// other is still going
if( isSearching ){ return; }
isSearching = true;
// Start query to google
getAddressFromGoogle();
});
function getAddressFromGoogle(){
var address = $adrInput.val();
// Get lat/lng value and then do something with it
getLatLng( address, function( lat, lng ){
$adrInput.val( lat + ',' + lng );
});
}
// this function takes two parameters
// the address as a string from the input field
// and a callback it will invoke with the results
// when it's done
function getLatLng( address, callback ){
var geocoder = new google.maps.Geocoder();
geocoder.geocode({ 'address': address }, function( results, status ){
if( status == google.maps.GeocoderStatus.OK ){
// Get the Lat. and Long.
var lat = results[0].geometry.location.lat(),
lng = results[0].geometry.location.lng();
// Pass latLng to callback function
if( typeof callback === 'function' ){
callback.apply( null, [ lat, lng ]);
}
}
else {
alert("Geocode was not successful for the following reason: " + status);
}
});
}
});
You have to do an event.preventDefault() at the begining. It will prevent the submit made by the "browser" ... so you will be able to do your own submit.
Here is a simplified version of your code:
Javascript:
var convertToCoords = function() {
event.preventDefault();
document.getElementById('address').value = "test";
document.getElementById('searchbar').submit();
};
Html:
<form name="searchform" action="required/results.php" method="get" id="searchbar" onsubmit="convertToCoords()">
<input type="text" name="input" id="address" placeholder="Search an address or suburb...">
<input type="submit" class="searchbtn" value="Search">
</form>

How can I validate my google map search text box for invalid input?

this is my first question on stackoverflow hope I get tons of answers ;)
I have integrated the google map api to autocomplete my search place textbox and display the selected place on a map on the same page on my travel planner app.
What I want to achieve - If the user enter's invalid value in the search textbox the form should not be submitted.
What I'm doing - I'm using jquery validation plugin for validating other elements on the form.
In the submit handler I'm calling a function to validate the search textbox. Here is the function :
function validateMapSearchInput(searchBoxID)
{
var addressField = document.getElementById(searchBoxID);
var geocoder = new google.maps.Geocoder();
var search = geocoder.geocode(
{'address': addressField.value},
function(results, status) {
if (status == google.maps.GeocoderStatus.OK)
{
var loc = results[0].geometry.location;
console.log(addressField.value+" found on Google");
console.log("loc : "+loc);
return true;
}
else {
console.log(addressField.value+" not found on Google");
return false;
}
});
console.log("search: "+search);
return search;
}
And here's where I'm calling it:
$('#r_frmAddTrip').validate({
errorClass: "addTripError",
rules:{
r_tripName:{
required : true
},
r_tripDesc:{
required : false
},
r_tripDestination:{
required : true
},
r_tripType:{
required: true
},
r_startDate:{
required: true
},
r_endDate:{
required: true,
compareDates: "#r_startDate"
}
},
messages:{
r_tripName:{
required: "Please Enter Trip Name."
},
r_tripDestination:{
required : "Please Enter Destination."
},
r_tripType:{
required: "Please select trip type."
},
r_startDate:{
required: "Please select start date."
},
r_endDate:{
required: "Please select return date.",
compareDates: "Return date should be greater than start date."
}
},
submitHandler: function() {
console.log("No add trip validation errors!");
var searchStatus = validateMapSearchInput('r_tripDestination');
console.log(searchStatus);
if(searchStatus){
addTrip();
console.log("Trip added");
}else{
console.log("TRip not added");
}
return false;
}
});
Problem : searchStatus is always undefined.. I have no idea why.. If I could atleast return something I can make this work. I even tried returning strings instead of true/false.
Appreciate your help!
geocoder.geocode(...) is an AJAX call, so returning the result of that method call will give you nothing, instead you need to make use of the callback to perform further actions based on the result.
Something like
$('#r_frmAddTrip').validate({
submitHandler: function(form) {
validateMapSearchInput('r_tripDestination', form);
}
});
Then
function validateMapSearchInput(searchBoxID, form)
{
var addressField = document.getElementById(searchBoxID);
var geocoder = new google.maps.Geocoder();
geocoder.geocode(
{'address': addressField.value},
function(results, status) {
if (status == google.maps.GeocoderStatus.OK)
{
var loc = results[0].geometry.location;
console.log(addressField.value+" found on Google");
console.log("loc : "+loc);
form.submit(); // continue with form submission
} else {
console.log(addressField.value+" not found on Google");
}
}
);
}
Please note that this is untested, your mileage may vary...
Here's a jsfiddle: http://jsfiddle.net/5mxrz/

Submit form after alert() is done

I have some jQuery code that uses Google Maps Geocoding API to convert an address to coordinates, then use alert() to show the result in popup window. Here is the code which works fine:
$("#searchbox_form #search_button").click(function(e){
e.preventDefault();
var address = $("#location").val();
var geocoder = new google.maps.Geocoder();
geocoder.geocode({'address': address}, function(results, status) {
if (status == google.maps.GeocoderStatus.OK) {
$("input#user_lat").val(results[0].geometry.location.lat());
$("input#user_lng").val(results[0].geometry.location.lng());
alert("lat: " + $("input[name='user_lat']").val());
alert("lng: " + $("input[name='user_lng']").val());
}
});
});
However now I want jQuery to submit the form $searchbox_form after the user closes the alert box. However adding $("#searchbox_form").submit(); at the end of the code chunk submits the form before the alert box shows up. This also causes the form to be submitted before the Google Maps geocoder returns the result.
How can I allow the geocoder to return the result before the form gets submitted?
Same as the code above, but with 1 additional line to submit form at the end:
$("#searchbox_form #search_button").click(function(e){
e.preventDefault();
var address = $("#location").val();
var geocoder = new google.maps.Geocoder();
geocoder.geocode({'address': address}, function(results, status) {
if (status == google.maps.GeocoderStatus.OK) {
$("input#user_lat").val(results[0].geometry.location.lat());
$("input#user_lng").val(results[0].geometry.location.lng());
alert("lat: " + $("input[name='user_lat']").val());
alert("lng: " + $("input[name='user_lng']").val());
}
});
$("#searchbox_form").submit(); //THIS IS THE ADDED LINE OF CODE!!
});
You need to move the submit within the callback to the geocode function. The reasoning is, it's asynchronous and not running in direct order, so it's calling the geocode and then immediately firing the submit. If you put it like below, the form will submit on callback and after the alerts (as alerts will block the thread).
$("#searchbox_form #search_button").click(function(e){
e.preventDefault();
var address = $("#location").val();
var geocoder = new google.maps.Geocoder();
geocoder.geocode({'address': address}, function(results, status) {
// This function is async
if (status == google.maps.GeocoderStatus.OK) {
$("input#user_lat").val(results[0].geometry.location.lat());
$("input#user_lng").val(results[0].geometry.location.lng());
alert("lat: " + $("input[name='user_lat']").val());
alert("lng: " + $("input[name='user_lng']").val());
$("#searchbox_form").submit();
}
});
});
Why don't you just submit it after you're done with your callback?
$("#searchbox_form #search_button").click(function(e){
e.preventDefault();
var address = $("#location").val();
var geocoder = new google.maps.Geocoder();
geocoder.geocode({'address': address}, function(results, status) {
if (status == google.maps.GeocoderStatus.OK) {
$("input#user_lat").val(results[0].geometry.location.lat());
$("input#user_lng").val(results[0].geometry.location.lng());
alert("lat: " + $("input[name='user_lat']").val());
alert("lng: " + $("input[name='user_lng']").val());
$("#searchbox_form").submit();
}
});
});
I think geocoder.geocode is an asynchronous function. Therefore you need the submit after the alert boxes.
$("#searchbox_form #search_button").click(function (e) {
e.preventDefault();
var address = $("#location").val();
var geocoder = new google.maps.Geocoder();
geocoder.geocode({ 'address': address }, function (results, status) {
if (status == google.maps.GeocoderStatus.OK) {
$("input#user_lat").val(results[0].geometry.location.lat());
$("input#user_lng").val(results[0].geometry.location.lng());
alert("lat: " + $("input[name='user_lat']").val());
alert("lng: " + $("input[name='user_lng']").val());
$("#searchbox_form").submit();
}
});
});

Passing Clientside Geocoded Result using POST

I am building a page which contains a form with 2 text input boxes, search_term and search_location. This allows the site user to search for a place near a particular location. To prevent hitting the API limit, I need to geocode clientside the user-inputted address from search_location text input box to a LatLng coordinate, which will be passed in $_POST[] to a PHP function which will access the relevant records in the MySQL database.
This is how I think it should work:
User types in address/zipcode into search_location text input box & a keyword into the search_term text input box.
When the Submit button is clicked, a JS event handler will use the Google Geocoder API to geocode the result and return the LatLng coordinates.
The LatLng coordinates is passed to the PHP function which accesses it as a POST variable, and access the database to retrieve the required results.
The Question:
I do not know how I can submit the form after getting the geocoded result from Google Geocoder API so I can pass the LatLng coordinates along in POST. At the moment, the form passes the user-inputted values in search_term and search_location input boxes.
Basically you need to set 2 hidden fields (one for lat and another for lng unless you want to save them as 1 string) and save the coords to it and then submit the form:
your form:
<form id='codeForm' action='some.php'>
<input type='text' name='search_location'>
<input type='text' name='search_term'>
<input type='hidden' name='lat'>
<input type='hidden' name='lng'>
<input type="button" value="Search" onclick="codeAddress()">
</form>
In the form I used a button type if you use a submit button you need to cancel the submit action until google returns a response or use onSubmit() event for the form.
Your geocode js function:
function codeAddress() {
myForm = document.forms["codeForm"]
//get the address
var address = myForm.elements["search_location"].value;
geocoder.geocode( { 'address': address}, function(results, status) {
//geocode was successful
if (status == google.maps.GeocoderStatus.OK) {
//grab the lat and long
lat = results[0].geometry.location.lat
lng = results[0].geometry.location.lng
myForm.elements["lat"].value=lat
myForm.elements["lng"].value=lng
//submit form
myForm.submit();
} else {
//geocode failed
alert("Geocode was not successful for the following reason: " + status);
}
});
}
My slightly modified code to Michael's above, which unfortunately doesn't work out of the box. I feel it's important to highlight this, because Google takes me to this post when I search for "geocode client side hidden fields". I'm taking the following code directly from my site, which uses $_GET, but it works either way. We start with the form:
<form type="get" id='codeForm' action='index.php'>
<input type='text' name='search_location' id='search_location'>
<input type='text' name='search_term'>
<input type="submit" value="Search" id="submitform">
<input type='text' name='search_term'>
</form>
As you can see, I've taken away the hidden lat and lng input fields from the form and appended them with jquery below:
<script type="text/javascript">
function codeAddress() {
myForm = document.forms["codeForm"];
//get the address
var geocoder = new google.maps.Geocoder();
var address = document.getElementById('search_location').value;
geocoder.geocode({'address': address, componentRestrictions:{country:"uk"}}, function(results, status) {
//geocode was successful
if (status == google.maps.GeocoderStatus.OK) {
//grab the lat and long
var lat = results[0].geometry.location.lat();
$("#codeForm").append("<input type='hidden' name='lat' value='" + lat + "' />");
var lng = results[0].geometry.location.lng();
$("#codeForm").append("<input type='hidden' name='lng' value='" + lng + "' />");
//submit form
} else {
//geocode failed
alert("Geocode was not successful for the following reason: " + status);
}
});
}
$(document).ready(function() {
$('#codeForm').submit(function(e) {
var form = this;
e.preventDefault();
codeAddress();
setTimeout(function () {
form.submit();
}, 1000); // in milliseconds
return false;
});
});
</script>
The important point about my code is that I have added a setTimeout delay, thanks to this post on SO. Without the delay, I found that the values in Michal's solution above were not being appended in time.
As a footnote, I know that I could improve upon the mix of javascript and jquery in my solution, but importantly it's a working example, and it's up to each and everyone to modify should they wish.

Categories