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"/>
Related
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 am trying to make a page that gives a 3 day weather forecast with a map.
I am using the Openweathermap API and some PHP to get the forecast.
I am using Google's Maps JavaScript API to get my map.
The page has a single input field. I can make it so that submitting input
will give me the weather forecast. I can make it so that input will give
me the map. But I cannot get both to happen at the same time.
Here is the HTML form
<form class="example" method="GET" action="index.php" style="margin:auto;max-width:300px">
<h3>Type In a Zipcode</h3>
<br><p>For Example 160-0005</p>
<!--test-->
<input id="address" type="textbox" value="" name="q" required>
<button id= "OK!" type="submit"><i class="fa fa-search"></i></button>
Here is the PHP
date_default_timezone_set($get['timezone']);
$city = $_GET['q'];
$string = "http://api.openweathermap.org/data/2.5/forecast?zip=".$city.",jp&units=metric&cnt=3&appid=[API Key]";
here is the JavaScript
<script>
"use strict";
function initMap() {
const map = new google.maps.Map(document.getElementById("map"), {
zoom: 15,
center: {
lat: -34.397,
lng: 150.644
}
});
const geocoder = new google.maps.Geocoder();
document.getElementById("OK!").addEventListener("click", () => {
geocodeAddress(geocoder, map);
});
}
function geocodeAddress(geocoder, resultsMap) {
const address = document.getElementById("address").value;
geocoder.geocode(
{
address: address
},
(results, status) => {
if (status === "OK") {
resultsMap.setCenter(results[0].geometry.location);
new google.maps.Marker({
map: resultsMap,
position: results[0].geometry.location
});
} else {
alert(
"Geocode was not successful for the following reason: " + status
);
}
}
);
}
</script>
In the HTML form code, if I change the button type to "button", the JavaScript works and displays the map. If I change it to type "submit", the PHP works and gives me the forecast. Not really sure what is going on there. What can I do to enter location information and get the map and the forecast at the same time?
in you javascript:
var address = "<?php isset($_GET['q']) ? $_GET['q'] : ''?>";
function initMap() {
const map = new google.maps.Map(document.getElementById("map"), {
zoom: 15,
center: {
lat: -34.397,
lng: 150.644
}
});
const geocoder = new google.maps.Geocoder();
address !== '' && geocodeAddress(geocoder, map, address);
}
// have geocodeAddress accept third param of address
function geocodeAddress(geocoder, resultsMap, address) {
... rest of your code
this is all untested. let me know if its works for your (or doesn't)
Your form isn't submitted when using the button element because you're not specifying the form attribute : HTML form Attribute
Also, you will likely have to use a javascript http client to send the form instead of just submitting it. The most known and documented one would be Ajax from jquery.
jQuery - AJAX get() and post() Methods
This will allow you to send data to your back-end(php) transparently without refreshing the page on each form (post or get request) you make.
Good luck with your project :)
EDIT : CODE edited with form attribute.
<form id="form1" class="example" method="GET" action="index.php" style="margin:auto;max-width:300px" >
<h3>Type In a Zipcode</h3>
<br><p>For Example 160-0005</p>
<!--test-->
<input id="address" type="textbox" value="" name="q" required>
<!--When using a button, potentially out of the form block-->
<!--You should fill the "form" attribute based on the form's ID-->
<button id= "OK!" type="submit" form=form1"><i class="fa fa-search"></i></button></form>
Also I don't know if it's only from your sample code but there is a missing </form> tag.
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!
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();
});