I am trying to load google autocomplete on two places in my page. One in the nav bar for searching the site and the second for the user to create an event by entering the address. I can get either one or the other working. And once I got both working but then it wouldn't fill in the info. I'd love to know what I am doing wrong, I have been banging my head against this for a hours now.
My inputs are
<input class="form-control" id="autocomplete" type="text" placeholder="Search..." name="term">
and
<input id="autocomplete2" placeholder="Enter address to store" type="text" autocomplete="off"></input>
and my js file
var placeSearch, autocomplete;
var componentForm = {
street_number: 'short_name',
route: 'long_name',
locality: 'long_name',
administrative_area_level_1: 'short_name',
country: 'long_name',
postal_code: 'short_name'
};
function initAutocomplete() {
autocomplete = new google.maps.places.Autocomplete(
(document.getElementById('autocomplete')), {
types: ['geocode']
});
autocomplete.addListener('place_changed', addlatlong);
autocomplete2 = new google.maps.places.Autocomplete(
document.getElementById('autocomplete2'), {
types: [ 'geocode' ]
});
autocomplete.addListener('place_changed', fillInAddress);
}
function addlatlong() {
var place = autocomplete.getPlace();
var latitude = place.geometry.location.lat();
var longitude = place.geometry.location.lng();
document.getElementById("latitude").value = latitude;
document.getElementById("longitude").value = longitude;
}
function fillInAddress() {
// Get the place details from the autocomplete object.
var place = autocomplete.getPlace();
for (var component in componentForm) {
document.getElementById(component).value = '';
document.getElementById(component).disabled = false;
}
var latitude = place.geometry.location.lat();
var longitude = place.geometry.location.lng();
document.getElementById("latbox").value = latitude;
document.getElementById("lngbox").value = longitude;
console.log(`${latitude}`)
console.log(`${longitude}`)
// Get each component of the address from the place details
// and fill the corresponding field on the form.
for (var i = 0; i < place.address_components.length; i++) {
var addressType = place.address_components[i].types[0];
if (componentForm[addressType]) {
var val = place.address_components[i][componentForm[addressType]];
document.getElementById(addressType).value = val;
}
}
}
Well first off, let's make sure those inputs are identical other than their ID's.
Here's a .slim example from an old project of mine:
form style="width:300px;margin: 0 auto;" action="/instructions" method="POST"
strong
p.input-label I'm currently at...
input style="width:300px;" id="autocomplete" placeholder="Enter your current address" onFocus="geolocate()" type="text" name="current_address" required="required"
strong
p.input-label And I need to get to...
input style="width:300px;" id="autocomplete2" placeholder="Enter your destination address" onFocus="geolocate()" type="text" name="destination_address" required="required"
input.button style="width:300px;" type="submit" value="What's the plan?"
Your inputs that you shared have different fields, some missing from the other, etc. Your first step should be making them identical in every way possible, and then slowly shifting one of them to your desired state.
I see you're using an older version of javascript, too. If possible you may want to consider refactoring to es5+
At the top you declare placeSearch and autocomplete, but not autocomplete2. This is another example of what I was suggesting earlier, about stepping through your code and ensuring that both inputs are being treated/created the exact same.
I see initAutocomplete is declared but never run. Is this intentional? Does the google module automatically call that function?
It seems to me like addlatlong() and fillInAddress() are the two functions you intend to use, one for autocomplete and the other for autocomplete2, but you reference autocomplete in both of them.
All in all, I think you've been messing with the code so much to try and make it work that you've lost sight of how it should be written in the end.
Perhaps starting a new file, fresh, and taking it one small step at a time might be the best way to figure out how to solve this.
Get one input working fully, as expected, and then add the next one in, step-by-step, the same way you implemented the first.
Good luck!
Related
I need to remove the partial postcode (in the red box) from the suggestions dropdown on autocomplete, so that users can only select a full postcode.
How do I achieve this or does anyone have a better suggestion?
I think everything works as it should but my client thinks to be able to select a partial postcode is too confusing.
This is my code:
let autocomplete;
let address1Field;
let postalField;
function initAutocomplete() {
address1Field = document.querySelector("#ship-address");
postalField = document.querySelector("#postcode");
autocomplete = new google.maps.places.Autocomplete(address1Field, {
componentRestrictions: {
country: ["gb"]
},
fields: ["address_components", "geometry"],
});
address1Field.focus();
autocomplete.addListener("place_changed", fillInAddress);
}
function fillInAddress() {
// Get the place details from the autocomplete object.
const place = autocomplete.getPlace();
let address1 = "";
let postcode = "";
for (const component of place.address_components) {
const componentType = component.types[0];
switch (componentType) {
case "street_number":
{
address1 = `${component.long_name} ${address1}`;
break;
}
case "route":
{
address1 += component.short_name;
break;
}
case "sublocality_level_1":
{
address1 += component.long_name;
break;
}
case "postal_code":
{
postcode = `${component.long_name}${postcode}`;
break;
}
case "postal_code_suffix":
{
postcode = `${postcode}-${component.long_name}`;
break;
}
case "postal_town":
document.querySelector("#postal_town").value = component.long_name;
break;
case "locality":
// document.querySelector("#locality").value = component.long_name;
break;
case "administrative_area_level_1":
{
// document.querySelector("#state").value = component.short_name;
break;
}
case "administrative_area_level_2":
document.querySelector("#county").value = component.long_name;
break;
}
}
address1Field.value = address1;
postalField.value = postcode;
document.getElementById('details').innerHTML = '<input name="latitude" type="hidden" id="latitude" value="' + place.geometry.location.lat() + '"><input name="longitude" type="hidden" id="longitude" value="' + place.geometry.location.lng() + '">';
}
window.initAutocomplete = initAutocomplete;
There is no easy way to do this, because those partial postal codes kind of are postal codes; they are postal code prefixes which is kind of a postal code in the Places API.
You can file a feature request for Place Autocomplete to support excluding certain types. Such a feature would allow you to request predictions including types=postal_code and excluding types=postal_code_prefix.
Currently, requesting predictions with types=postal_code will include predictions with types: ["postal_code_prefix", "postal_code"].
Without such a feature, the closest to the desired effect could be building your own widget, retrieve predictions using the Place Autocomplete Service and discarding those predictions withtypes: ["postal_code_prefix", "postal_code"]. There are 2 significant problems with this approach:
Probably not compliant with clause 3.2.3.(j) of the Google Maps Platform Terms of Service: (j) No Modifying Search Results Integrity. Customer will not modify any of the Google Maps Core Services’ search results.. You would need to seek legal advice about this.
The resulting user experience would be that typing rg1 produces only one prediction (RG1 8EQ), or possible no predictions at all.
All the above might be too much hassle considering that as soon as a blank space is added to the input (e.g. "rg1 ") most of the predictions will be for full precision postal codes, and as soon as an additional character is added (e.g. "rg1 1") all the predictions will be for full precision postal codes.
This is the intended behavior, users are expected to disambiguate short inputs like rg1 by adding a character, a blank space can often make a big difference.
I am working with googles api places for address autocomplete suggestions.
This is my simple working code:
<!DOCTYPE html>
<html>
<body>
<form>
<input id="customerAddress" name="customerAddress" required autocomplete="off" />
<input id="zip" name="zip" required readonly />
<input id="city" name="city" required readonly />
</form>
<script
src="https://maps.googleapis.com/maps/api/js?key=MY_API_KEY&callback=initAutocomplete&libraries=places"
async
></script>
<script>
let autocomplete;
function initAutocomplete() {
autocomplete = new google.maps.places.Autocomplete(document.querySelector("#customerAddress"), {
componentRestrictions: { country: ["de", "DE"] },
fields: ["address_components", "geometry"],
types: ["address"],
});
autocomplete.addListener("place_changed", fillInAddress);
}
function fillInAddress() {
const place = autocomplete.getPlace();
var address = ""
var streetNumber = ""
for (const component of place.address_components) {
const componentType = component.types[0];
switch (componentType) {
case "route": {
address = component.long_name;
break;
}
case "street_number": {
streetNumber = component.long_name
break;
}
case "postal_code": {
document.querySelector("#zip").value = component.long_name;
break;
}
case "locality":
document.querySelector("#city").value = component.long_name;
break;
}
}
document.querySelector("#customerAddress").value = address +" "+streetNumber
}
</script>
</body>
</html>
Now I would realize the following situation:
the customer should write his address into the field "customerAddress" to get the suggestions, but the address have to include a street number. how can I restrict this?
The form should only be able to be submitted, if the customer selected a suggestions from the autocomplete list. If he / she write manually a address, which doesn't is from the autocomplete list, it has to be an error
Well, I think the best solution would be to have separate hidden input where you would put selected address which you would run validation against, if it is not defined you simply return error.
As for having street number you can also run the validation of selected Place. If it has selected type street_number it is valid, if not it's not.
I am not sure if you can disable Google to restricts search results to only addresses with street number.
I'm trying to embed the google maps autocompletion API into my project.
I did everything like it was written in the documentation, but it returns me this error : Uncaught InvalidValueError: initAutocomplete is not a function. Sometimes, when I reload 3 times the page, it finally works, which is quite strange...
Does somebody know where the problem could come from ?
Here is the link where is the test that I did : http://www.dubair.ie/en/maps
Here is the code of the page (the link to the key script is in the head which is in an other file) :
{% extends "PlatformBundle::body.html.twig" %}
{% block content %}
<div id="locationField" class="form-group col-xs-12">
<textarea id="autocomplete" placeholder="Enter your address" onFocus="geolocate()" type="text" autofocus class="form-control"></textarea>
</div>
<div id="details" class="hidden">
<input disabled id="street_number"/>
<input disabled id="route"/>
<input disabled id="locality"/>
<input disabled id="postal_code"/>
<input disabled id="country"/>
</div>
<script>
// This example displays an address form, using the autocomplete feature
// of the Google Places API to help users fill in the information.
// This example requires the Places library. Include the libraries=places
// parameter when you first load the API. For example:
// <script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&libraries=places">
var placeSearch, autocomplete;
var componentForm = {
street_number: 'short_name',
route: 'long_name',
locality: 'long_name',
// administrative_area_level_1: 'short_name',
country: 'long_name',
postal_code: 'short_name'
};
function initAutocomplete() {
// Create the autocomplete object, restricting the search to geographical
// location types.
autocomplete = new google.maps.places.Autocomplete(
/** #type {!HTMLInputElement} */(document.getElementById('autocomplete')),
{types: ['geocode'], componentRestrictions: {country: 'ie'}});
// When the user selects an address from the dropdown, populate the address
// fields in the form.
autocomplete.addListener('place_changed', fillInAddress);
}
function fillInAddress() {
// Get the place details from the autocomplete object.
var place = autocomplete.getPlace();
$('#details').removeClass('hidden').hide().slideDown('slow');
for (var component in componentForm) {
document.getElementById(component).value = '';
// document.getElementById(component).disabled = false;
}
// Get each component of the address from the place details
// and fill the corresponding field on the form.
for (var i = 0; i < place.address_components.length; i++) {
var addressType = place.address_components[i].types[0];
if (componentForm[addressType]) {
var val = place.address_components[i][componentForm[addressType]];
document.getElementById(addressType).value = val;
}
}
}
// Bias the autocomplete object to the user's geographical location,
// as supplied by the browser's 'navigator.geolocation' object.
function geolocate() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(function(position) {
var geolocation = {
lat: position.coords.latitude,
lng: position.coords.longitude
};
var circle = new google.maps.Circle({
center: geolocation,
radius: position.coords.accuracy
});
autocomplete.setBounds(circle.getBounds());
});
}
}
</script>
{% endblock %}
Thank you for your help
I think it's because you call API in the header, but you have the initAutocomplete function in the body of the HTML document. Sometimes the body is not yet loaded, sometimes it is.
Just put this code:
<script src="https://maps.googleapis.com/maps/api/js?key=....&libraries=places&callback=initAutocomplete" async defer></script>
right BEFORE </body> tag (and remove it from header). So it would be the last element in the body of the HTML document. This way initAutocomplete should always be available when the API is loaded.
I wonder how you get this error, I also wonder how it may work sometimes.
I get this error: InvalidValueError: not an instance of HTMLInputElement
The error should be clear: You use a <textarea> , but the API requires an <input>
Replace:
<textarea id="autocomplete" placeholder="Enter your address" onFocus="geolocate()" type="text" autofocus class="form-control"></textarea>
with
<input id="autocomplete" placeholder="Enter your address" onFocus="geolocate()" type="text" autofocus class="form-control"/>
I've come across a little problem with autocomplete. I use a base.jsp page which has the following in a init function:
var input = document.getElementById('addressField');
var autocomplete = new google.maps.places.Autocomplete(input, {
types: ["geocode"]
});
autocomplete.bindTo('bounds', map);
I got two pages, one.jsp and two.jsp.
ons.jsp contains the following input type, which is tied to the autocomplete. This one works.
<input type="text" class="form-control" name="address" aria-label="..." id="addressField" value="">
two.jsp as the exacty same. But this one does not work?
<input type="text" class="form-control" name="address" aria-label="..." id="addressField" value="">
I do not understand how two identical input fields can have one working and one not. Both share the same base, and the autocomplete initialization.
What can possible cause this? I've got no idea where to look-
Here's where I'd start:
var input = document.getElementById('addressField');
console.log(input);
var autocomplete = new google.maps.places.Autocomplete(input, {
types: ["geocode"]
});
console.log(autocomplete);
console.log(map);
autocomplete.bindTo('bounds', map);
console.log('--------------- Done -----------');
console.log(input);
Then look at the output of the browser log and see what's different. Computers only do what we tell them :-)
I'm following this tutorial to implement Google Places Autocomplete. However, this only works well with existing statically defined text field. In my test app, I have dynamically added text field whereby user clicks on a button to add more text field.
<input maxlength="40" name="Point[location][]" id="Point_location" type="text" placeholder="Enter a location" autocomplete="off">
<input maxlength="40" name="Point[location][]" id="Point_location2" type="text" placeholder="Enter a location" autocomplete="off">
Here is my javascript
<script>
var location = $("input[id^=Point_location]")[0];
var autocompleteLocation = new google.maps.places.Autocomplete(location, options);
google.maps.event.addListener(autocompleteLocation, 'place_changed', function () {
var placeLocation = autocompleteLocation.getPlace();
});
</script>
I can't seem to get the autocomplete to work any existing input with the id Point_location or input that has just been added.
Appreciate any help on this.
Would be easier to answer when you had posted the code that creates the clone too.
I guess you are simply cloning the input, that will no work. You must create a new Autocomplete-instance for each input:
//create the clone
var clone=$(location).clone(false).val('').appendTo('body');
//apply Autocomplete
var ac = new google.maps.places.Autocomplete(clone[0]);
//apply listener
google.maps.event.addListener(ac, 'place_changed', function () {
var placeLocation = this.getPlace();
});