Google Maps API - transparent pass through address components - javascript

I have a web-form with a text area where a user can type in an address. I need the form not only to store the formatted address (which is currently working) but also transparently pass through the address components for storing in a backend database.
The current code below predicts the possible addresses as the user types:
<script src="https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false&libraries=places&key=*******************"></script>
*** snip ***
<script>
function init() {
var options = {
componentRestrictions: {
country: 'NZ'
}
};
var input = document.getElementById('streetAddress');
var autocomplete = new google.maps.places.Autocomplete(input, options); <-- autocomplete is never used?
}
google.maps.event.addDomListener(window, 'load', init);
</script>
*** snip ***
<div class="form-group">
<label for="streetAddress" class="col-sm-2 control-label">Location</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="streetAddress" placeholder="Street Address" name="location"/>
</div>
</div>
I am assuming I need to call a second google function - geocoder which after onClick which returns a geocoded address array from the address string in the function above.
I could then store its components in hidden html form input elements unless there is a better way.
Thymeleaf and spring-boot are being used to get the data into the backend.

The way I would do this is to add some elements to your page that are hidden to store and pass through the address.
The example below passes through the street number - you can expand it from there. The HTML & Thymeleaf:
<div class="form-group">
<label for="streetAddress" class="col-sm-2 control-label">Location</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="streetAddress" placeholder="Street Address" name="location" th:field="*{formattedAddress}" />
<input type="text" class="form-control hidden" id="streetnumber" th:field="*{streetNumber}" /> <!--hidden-->
</div>
Now the javascript:
var options = { componentRestrictions: { country: 'NZ' } };
var input = document.getElementById('streetAddress');
var autocomplete = new google.maps.places.Autocomplete(input, options);
autocomplete.addListener('place_changed', function() {
console.log(autocomplete.getPlace());
var place = autocomplete.getPlace();
console.log(place.address_components[0].short_name);
var streetNumber = document.getElementById("streetnumber");
streetNumber.value = place.address_components[0].short_name;
});
Error handling needed.

Related

Form Validation Not Resetting After Failing Validation

I'm using a small script to validate a postcode, which works and stops the user entering an invalid password, but when an invalid post code is entered you then can't submit a correct entry. For example, if I enter 'ST' I get the message telling me the postcode is invalid, so without refreshing the page manually I enter 'ST6 1SA' (which is a valid Stoke postcode) and I can't submit the form, I just keep getting the invalid tool tip advising me the post code is not in the correct format.
JS:
<script>
// Validate the postcode before it's sent
(function () {
var postcode = document.getElementById('postcode-entry');
var wrapper = document.getElementById('validation');
var notify = document.createElement('div');
var mnisLookup = document.getElementById('mnis-results');
var matchingClients = document.getElementById('matching-clients');
var postcodeWrapper = document.getElementById('postcode-wrapper');
notify.id = 'notify';
notify.style.display = 'none';
wrapper.appendChild(notify);
postcode.addEventListener('invalid', function (event) {
if (!event.target.validity.valid) {
notify.textContent = 'Please enter a valid postcode e.g. ST1, ST1 4BJ';
notify.className = 'error';
notify.style.display = 'block';
postcode.className = 'form-control invalid';
}
});
})();
</script>
HTML:
<form id="postcode-wrapper" class="form-horizontal">
<div id="postcode-lookup" class="form-group">
<label for="postcode-entry" class="col-sm-1">Postcode:</label>
<div id="postcode-entry-wrapper" class="col-sm-3">
<input type="text" pattern="^(([gG][iI][rR] {0,}0[aA]{2})|((([a-pr-uwyzA-PR-UWYZ][a-hk-yA-HK-Y]?[0-9][0-9]?)|(([a-pr-uwyzA-PR-UWYZ][0-9][a-hjkstuwA-HJKSTUW])|([a-pr-uwyzA-PR-UWYZ][a-hk-yA-HK-Y][0-9][abehmnprv-yABEHMNPRV-Y])))( {0,}[0-9][abd-hjlnp-uw-zABD-HJLNP-UW-Z]{2})?))$" oninvalid="setCustomValidity('Invalid Post Code Format ')" class="form-control" id="postcode-entry" placeholder="Enter your postcode" name="Postcode" />
</div>
<div class="col-sm-1">
<input id="search" type="submit" value="Search" class="btn btn-default" />
</div>
<div id="validation" class="col-sm-7"></div>
</div>
</form>
Just a quick note that may affect how the page is refreshing, this is inside an MVC Razor page and wrapped with Html.BeginForm - not sure if that makes a difference?
While debugging your code, i found that the event.target.validity.valid was returning false even if the input was valid e.g. 'ST6 1SA'. This was occuring because it does not update the custom validation for the new input and the previous state persists even after entering the valid input.
So to update and reset the previous validation, you have to reset setCustomValidity('') on input change, i.e. oninput="setCustomValidity('')"
Please replace this code:
<input type="text" pattern="^(([gG][iI][rR] {0,}0[aA]{2})|((([a-pr-uwyzA-PR-UWYZ][a-hk-yA-HK-Y]?[0-9][0-9]?)|(([a-pr-uwyzA-PR-UWYZ][0-9][a-hjkstuwA-HJKSTUW])|([a-pr-uwyzA-PR-UWYZ][a-hk-yA-HK-Y][0-9][abehmnprv-yABEHMNPRV-Y])))( {0,}[0-9][abd-hjlnp-uw-zABD-HJLNP-UW-Z]{2})?))$" oninvalid="setCustomValidity('Invalid Post Code Format ')" class="form-control" id="postcode-entry" placeholder="Enter your postcode" name="Postcode" oninput="setCustomValidity('')"/>

Javascript assign value to HTML field

I am trying to save an HTML field (for later use in a form) from a JS script.
This is the code:
Form
<form class="new_client" id="new_client" action="/clients" accept-charset="UTF-8" method="post">
<div class="form-group">
<input class="form-input" type="hidden" name="client[city]" id="client_city">
</div>
<div class="form-group">
<input class="form-input" type="hidden" name="client[address]" id="client_address">
</div>
<div id="locationField">
<input autocomplete="off" class="autocomplete" placeholder="Enter your address" type="text">
</div>
<div class="text-center">
<button class="btn button-general ">Save</button>
</div>
</form>
And the javascript:
function configureGeoAutocomplete(context) {
if( context === undefined ) {
context = $('body');
}
var element = context.find('.autocomplete')
//It doesn't really matter what this line does, it's from Google Places API
var autocomplete = new google.maps.places.Autocomplete(
element[0], {types: ['geocode']});
autocomplete.addListener('place_changed', fillInAddress)
}
function fillInAddress() {
var client_address = autocomplete.getPlace().formatted_address;
document.getElementById("client_address").value = client_address;
}
The javascript is queried when loading the modal in which the form is
jQuery(function() {
$('div.modal').on('loaded.bs.modal', function(e) {
configureGeoAutocomplete(context);
}
}
I wanna save that client_address to the text field so when the form is submitted I can have that information.
Sounds like an excellent candidate for cookies if you are allowed to use them: http://www.w3schools.com/js/js_cookies.asp. Another idea is to pass it along in a Query String. It isn't PII or anything like that. Try an event code on that input. I do not like to hit "enter"!
onkeypress="if (event.keycode==13) { // do something }"
Handle the form submit event:
$(function() {
$("#new_client").submit(function(e) {
e.preventDefault();
// This is your value stored in the field.
$("#client_address").val();
});
})
Apparently, for some reason, if I searched the element by ID it was not saving the information on the field. If I istead search by class:
function fillInAddress() {
var place = autocomplete.getPlace();
$(".client-address").val(place.formatted_address);
}
It works as intended.

How to change form input value dynamically in AngularJS [duplicate]

I'm trying to show some editable results to the users, so I show them through an input field. This is a basic example of what I'm doing:
<div class="form-group">
<label>First number</label>
<input type="text" ng-model="first" ng-required="true" class="form-control">
</div>
<div class="form-group">
<label>Second number</label>
<input type="text" ng-model="second" ng-required="true" class="form-control">
</div>
<div class="form-group">
<label>The sum is: {{first + second }}</label>
<input type="text" ng-model="result" ng-required="true" class="form-control">
</div>
In the result's div, I used a label to test if the result is being obtained correctly, and it is. But if I edit the first or second values, the input's result doesn't update.
This is the controller used (yeah, the form is in a modal):
var ModalInstanceCtrl = function ($scope, $modalInstance) {
$scope.result = $scope.first + $scope.second;
$scope.confirm = function () {
$modalInstance.close(result);
};
$scope.cancelNewBet = function () {
$modalInstance.dismiss('cancel');
};
};
I thought the value was supposed to get automatically updated once I define how it is obtained. But clearly it misses something to change the result through script...
Thanks in advance.
What do you want to happen when the user edits the results input? Do you want the data binding to break (I assume not and ignore this possibility)? Do you want one of the inputs to adjust to the proper value?
If you only want to display the output, do this, in your controller:
$scope.result = function() { return $scope.first + $scope.second; }
And in your view:
{{ result() }}
But if you want the user to be able to edit the result and (let's say) have second be assigned (result - first), then you'd want something like this in your view (by the way, note the type="number"):
<input type="number" ng-change="adjustResult()" ng-model="first">
<input type="number" ng-change="adjustResult()" ng-model="second">
<input type="number" ng-change="adjustInput()" ng-model="result">
And in your controller:
$scope.adjustResult = function() {
$scope.result = $scope.first + $scope.second;
};
$scope.adjustResult(); // initialize result
$scope.adjustInput = function() {
$scope.second = $scope.result - $scope.first;
}

Meteor Google Maps Autocomplete only works once on multiple templates

I am using a Google Places autocomplete package with Meteor and if I have the user select a location in one template, the autocomplete won't work again in a different template.
For instance, if the user picks an autocomplete location for an event they are hosting, and then they try to set their profile location in the profile settings, no autocomplete locations pop up.
In fact, if they even activate the autocomplete dropdown on one page (without even selecting one of the options), it won't work on the other page.
Here's my HTML:
<template name="userUpdate">
<script>
window.onload = function() {
var autocomplete = new google.maps.places.Autocomplete(
(document.getElementById('autocomplete')),{types: ['geocode'] }
);
};
</script>
<form class="main form" autocomplete="off">
<label class="control-label" for="location">Location</label>
<div class="controls">
<div id="locationField">
<input id="autocomplete" name="userLocation" class="form-control" autocomplete="off" placeholder="Where you live." type="text">
</div>
</div>
<p>
<h4 id="setLocation">{{currentUser.profile.location}}</h4>
<input id="updateUser" type="submit" class="btn btn-primary" value="Update Profile" />
</p>
</form>
</template>
Here is the second template:
<template name="postSubmit">
<form class="main form" autocomplete="off">
<div class="form-group">
<label class="control-label" for="title">Event Name</label>
<div class="controls">
<input name="title" id="title" type="text" value="" placeholder="Event name" class="form-control"/>
</div>
</div>
<div class="form-group">
<!--begin google test-->
<script>
window.onload = function() {
var autocomplete = new google.maps.places.Autocomplete(
(document.getElementById('autocompleteEventPost')),{types: ['geocode'] }
);
};
</script>
<label class="control-label" for="location">Event Location</label>
<div class="controls">
<!--<input name="location" id="url" type="text" value="" placeholder="The location of the vent" class="form-control"/>-->
<div id="locationField">
<input id="autocompleteEventPost" name="location" autocomplete="off" placeholder="Event Location" type="text">
</div>
</div>
</div>
<input type="submit" value="Submit" class="btn btn-primary"/>
</form>
</template>
I have the mrt:googlemaps package added, and I have set a googlePlaces.js like so:
GoogleMaps.init({
'sensor': true, //optional
'key': '***my api key***', //optional
'language': 'en', //optional
'libraries': 'places'
});
It is notable to state that although the autocomplete does not function again after a file update (with server restart), it will work again after a page refresh from the client.
Basically, I would like to see a perfect example of how to get this working in multiple templates in meteor.js.
The thing is with Google Maps is once you initialize, it only attaches itself on that current DOM instance. When you switch to another page/template, Gmaps seems to lose touch of those bindings you just created and you will have to reinitialize properly.
And you're using window.onload.. that only runs once..
Take a look at moving the <script> code found inside your templates to the rendered template event(s):
var initAutoComplete = function() {
var autocomplete = new google.maps.places.Autocomplete(
(document.getElementById('autocompleteEventPost')),{types: ['geocode'] }
);
};
Template.userUpdate.rendered = initAutoComplete;
Template.postSubmit.rendered = initAutoComplete;
Make sure you get the timings right though.. GoogleMaps API is async after all and may even mess up with this kind of initialization. One thing you could do to avoid this is to wrap the above code in the GoogleMaps.init callback.
Electric Jesus' answer is the answer that worked, HOWEVER: A variable must be declared for each element that is going to use the Google Places Autocomplete API.
His solution:
var initAutoComplete = function() {
var autocomplete = new google.maps.places.Autocomplete(
(document.getElementById('autocompleteEventPost')),{types: ['geocode'] }
);
};
Template.userUpdate.rendered = initAutoComplete;
Template.postSubmit.rendered = initAutoComplete;
There are two separate input fields, one on each template. So, there must be a variable for each input field you want the Places Autocomplete API to work on. I changed the input element ID's back to "autocomplete". Here's my solution. Note the one-to-one ratio of variables to input fields.
var initAutoComplete = function() {
var autocomplete = new google.maps.places.Autocomplete(
(document.getElementById('autocomplete')),{types: ['geocode'] }
);
};
var initAutoCompletePost = function() {
var autocomplete = new google.maps.places.Autocomplete(
(document.getElementById('autocomplete')),{types: ['geocode'] }
);
};
Template.userUpdate.rendered = initAutoComplete;
Template.postSubmit.rendered = initAutoCompletePost;
For others who use this solution, in my working implementation, I have removed the <script> tags from my HTML (there is no more javascript in my templates).
My guess is you should probably change it to class="autocomplete", id shouldn't have duplicate, so document.getElementById('autocomplete') will always return first element it finds. Never worked with google-maps tho, but I think this can be the reason
None of the other answers worked consistently for me so I'm calling initAutoComplete() when the text input is clicked and it works better for me.
Template.myTemplate.events({
'click #autocomplete': function(e,template) {
initAutoComplete();
}
});
var initAutoComplete = function() {
var autocomplete = new google.maps.places.Autocomplete(
(document.getElementById('autocomplete')),{types: ['geocode'] }
);
};
Edit: Not working so great it turns out though, getting this from time to time:
Uncaught TypeError: Cannot read property 'Autocomplete' of undefined

How to make an autocomplete address field with google maps api?

Using Google Maps API and JQuery I would like to have an Address field that when typing it will autocomplete the address entered there. How this could be achieved?
Well, better late than never. Google maps API v3 now provides address autocompletion.
API docs are here: http://code.google.com/apis/maps/documentation/javascript/reference.html#Autocomplete
A good example is here:
http://code.google.com/apis/maps/documentation/javascript/examples/places-autocomplete.html
It is easy, but the Google API examples give you detailed explanation with how you can get the map to display the entered location. For only autocomplete feature, you can do something like this.
First, enable Google Places API Web Service. Get the API key. You will have to use it in the script tag in html file.
<input type="text" id="location">
<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?key=[YOUR_KEY_HERE]&libraries=places"></script>
<script src="javascripts/scripts.js"></scripts>
Use script file to load the autocomplete class. Your scripts.js file will look something like this.
// scripts.js custom js file
$(document).ready(function () {
google.maps.event.addDomListener(window, 'load', initialize);
});
function initialize() {
var input = document.getElementById('location');
var autocomplete = new google.maps.places.Autocomplete(input);
}
Below I split all the details of formatted address like City, State, Country and Zip code.
So when you start typing your street name and select any option then street name write over street field, city name write over city field and all other fields like state, country and zip code will fill automatically.
Using Google APIs.
------------------------------------------------
<script type="text/javascript"
src="http://maps.googleapis.com/maps/api/js?sensor=false&libraries=places"></script>
<script type="text/javascript">
google.maps.event.addDomListener(window, 'load', function() {
var places = new google.maps.places.Autocomplete(document
.getElementById('txtPlaces'));
google.maps.event.addListener(places, 'place_changed', function() {
var place = places.getPlace();
var address = place.formatted_address;
var value = address.split(",");
count=value.length;
country=value[count-1];
state=value[count-2];
city=value[count-3];
var z=state.split(" ");
document.getElementById("selCountry").text = country;
var i =z.length;
document.getElementById("pstate").value = z[1];
if(i>2)
document.getElementById("pzcode").value = z[2];
document.getElementById("pCity").value = city;
var latitude = place.geometry.location.lat();
var longitude = place.geometry.location.lng();
var mesg = address;
document.getElementById("txtPlaces").value = mesg;
var lati = latitude;
document.getElementById("plati").value = lati;
var longi = longitude;
document.getElementById("plongi").value = longi;
});
});
Like others have mentioned, the Google Places Autocomplete API is missing some important functions. Case in point, Google will not validate that the street number is real, and they also will not put it into a standardized format. So, it is the user's responsibility to enter that portion of the address correctly.
Google also won't predict PO Boxes or apartment numbers. So, if you are using their API for shipping, address cleansing or data governance, you may want one that will validate the building number, autocomplete the unit number and standardize the information.
Full Disclosure, I work for SmartyStreets
Drifting a bit, but it would be relatively easy to autofill the US City/State or CA City/Provence when the user enters her postal code using a lookup table.
Here's how you could do it if you could force people to bend to your will:
User enters: postal (zip) code
You fill: state, city (province, for Canada)
User starts to enter: streetname
You: autofill
You display: a range of allowed address numbers
User: enters the number
Done.
Here's how it is natural for people to do it:
User enters: address number
You: do nothing
User starts to enter: street name
You: autofill, drawing from a massive list of every street in the country
User enters: city
You: autofill
User enters: state/provence
You: is it worth autofilling a few chars?
You: autofill postal (zip) code, if you can (because some codes straddle cities).
Now you know why people charge $$$ to do this. :)
For the street address, consider there are two parts: numeric and streetname. If you have the zip code, then you can narrow down the available streets, but most people enter the numeric part first, which is backwa
I really doubt it--google maps API is great for geocoding known addresses, but it generally return data that is suitable for autocomplete-style operations. Nevermind the challenge of not hitting the API in such a way as to eat up your geocoding query limit very quickly.
Youtube video reference: https://youtu.be/WxH0J4wOnZA
HTML
<input type="text" name="myAddress" placeholder="Enter your address" value="333 Alberta Place, Prince Rupert, BC, Canada" id="myAddress"/>
Google Autofill address
<script src="https://maps.googleapis.com/maps/api/js?v=3.exp&libraries=places&key=[YOUR-KEY]"></script>
<script type="text/javascript">
var searchInput = 'myAddress';
$(document).ready(function () {
var autocomplete;
autocomplete = new google.maps.places.Autocomplete((document.getElementById(searchInput)), {
types: ['geocode']
});
google.maps.event.addListener(autocomplete, 'place_changed', function () {
var near_place = autocomplete.getPlace();
});
});
</script>
There are some awesome libraries such as select2, but it doesn't match my need.
I've made a sample from scratch in order to use a simple input text.
I only use bootstrap and JQuery, Hope it'll be useful: Example
HTML:
<div class="form-group col-md-12">
<label for="address">Address</label>
<input type="text" class="form-control" id="address">
</div>
<div class="form-group">
<div class="col-md-4">
<label for="number">number</label>
<input type="text" class="form-control" id="number">
</div>
<div class="col-md-8">
<label for="street">street</label>
<input type="text" class="form-control" id="street">
</div>
</div>
<div class="form-group">
<div class="col-md-4">
<label for="zip">zip</label>
<input type="text" class="form-control" id="zip">
</div>
<div class="col-md-8">
<label for="town">town</label>
<input type="text" class="form-control" id="town">
</div>
</div>
<div class="form-group">
<div class="col-md-4">
<label for="department">Department</label>
<input type="text" class="form-control" id="department">
</div>
<div class="col-md-4">
<label for="region">Region</label>
<input type="text" class="form-control" id="region">
</div>
<div class="col-md-4">
<label for="country">Country</label>
<input type="text" class="form-control" id="country">
</div>
</div>
JS:
$("input#address").suggest({
label : "Adresse complete",
street_number_input : {
id : "number",
label : "Numero de la rue"
},
street_name_input : {
id : "street",
label : "Nom de la rue"
},
zip_input : {
id : "zip",
label : "Code postal"
},
town_input : {
id : "town",
label : "Ville"
},
department_input : {
id : "department",
label : "Departement"
},
region_input : {
id : "region",
label : "Region"
},
country_input : {
id : "country",
label : "Pays"
}
});

Categories