Google Places Autocomplete Force Selection
Asked Answered
S

14

33

I am using the JavaScript Google places autocomplete API v3. It works fine but I was wondering if there was a way to force the selection from the autocomplete, that is input that will not accept any free form text. I looked at the docs and didn't see such an option but figured I would ask just to be safe. I'm sure I could work out a way to do it with some JavaScript but would prefer to use an already built method if it is available. Thanks!

Sentient answered 1/2, 2013 at 18:13 Comment(0)
V
10

The Google Places API does not currently support this feature. If you believe this would be a useful feature please submit a Places API - Feature Request.

Vaca answered 5/2, 2013 at 5:26 Comment(3)
I'm using a workaround ... instead, I use the Autocomplete Prediction API to select by default the first result.Lurlene
code.google.com/p/gmaps-api-issues/issues/detail?id=8456 Vote, if you also affected.Hardwood
Don’t do this. It’s a terrible idea since Google is often wrong. For new roads you’ll have a year-long delay until correct addresses are recognized, for instance. See answer below.Thiourea
C
18

Actually, what we did was the following:
- Every time a location is picked from the Auto complete list, we populate some hidden fields with some fields coming from the JSON response (city name, country name, longitude and latitude)
- When the form is submitted, we check if these fields have values, if they don't, it means that the user instead of selecting a location from the list, he just entered a value himself.

It is not solving the problem in a JS way, but still, it does the trick just fine!

Convince answered 25/9, 2013 at 12:17 Comment(2)
It's worth noting that you should also remove the values from these hidden inputs if the user removes a selected location and then manually enters a location.Trinity
It will not work. Suppose first I select the proper location from autocomplete, so based on that your hidden field values would fill and then if I remove some text from autocomplete box and then submit, in that case the wrong address will insert in your DB as the hidden field values are already having some values.Sauger
Q
13
 <script src="http://maps.googleapis.com/maps/api/js?sensor=false&amp;libraries=places"
    type="text/javascript"></script>
<script>
    var IsplaceChange = false;
    $(document).ready(function () {            
        var input = document.getElementById('txtlocation');
        var autocomplete = new google.maps.places.Autocomplete(input, { types: ['(cities)'] });

        google.maps.event.addListener(autocomplete, 'place_changed', function () {
            var place = autocomplete.getPlace();

            IsplaceChange = true;
        });

        $("#txtlocation").keydown(function () {
            IsplaceChange = false;
        });

        $("#btnsubmit").click(function () {

            if (IsplaceChange == false) {
                $("#txtlocation").val('');
                alert("please Enter valid location");
            }
            else {
                alert($("#txtlocation").val());
            }

        });
    });
</script>




Quyenr answered 8/7, 2014 at 13:13 Comment(2)
Try to add a description or explanation to help people understand how this solves the problemMerline
If you type something and press Enter the place_changed event will be thrown and the get place object will be returned with a single property name with the text that you entered.Paulenepauletta
V
10

The Google Places API does not currently support this feature. If you believe this would be a useful feature please submit a Places API - Feature Request.

Vaca answered 5/2, 2013 at 5:26 Comment(3)
I'm using a workaround ... instead, I use the Autocomplete Prediction API to select by default the first result.Lurlene
code.google.com/p/gmaps-api-issues/issues/detail?id=8456 Vote, if you also affected.Hardwood
Don’t do this. It’s a terrible idea since Google is often wrong. For new roads you’ll have a year-long delay until correct addresses are recognized, for instance. See answer below.Thiourea
C
5

The following solution did the trick for me (with jQuery). The idea is to use the blur() event to force the last selected address once the input loses focus.

We add a timeout to prevent a conflict between the blur and the place_changed events.

Consider the following html:

<input type="text" placeholder="Your address" id="autocomplete">

And the following javascript :

var autocomplete = new google.maps.places.Autocomplete(
    (document.getElementById('autocomplete')),
    {types: ['geocode']});
autocomplete.addListener('place_changed', fillInAddress);

var currentAddress = {
    line_1: "",
    line_2: "",
    zipcode: "",
    city: "",
    country: "",
    lat: "",
    lng: "",
    one_liner: ""
};

function fillInAddress() {
    var place = this.autocomplete.getPlace();
    // reset the address
    currentAddress = {
        line_1: "",
        line_2: "",
        zipcode: "",
        city: "",
        country: "",
        lat: "",
        lng: "",
        one_liner: place.formatted_address
    };
    // store relevant info in currentAddress
    var results = place.address_components.reduce(function(prev, current) {
        prev[current.types[0]] = current['long_name'];
        return prev;
    }, {})
    if (results.hasOwnProperty('route')) {
        currentAddress.line_1 = results.route;
    }
    if (results.hasOwnProperty('street_number')) {
        currentAddress.line_1 = results.street_number + " " + currentAddress.line_1;
    }
    if (results.hasOwnProperty('postal_code')) {
        currentAddress.zipcode = results.postal_code;
    }
    if (results.hasOwnProperty('locality')) {
        currentAddress.city = results.locality;
    }
    if (results.hasOwnProperty('country')) {
        currentAddress.country = results.country;
    }
    currentAddress.lat = Number(place.geometry.location.lat()).toFixed(6);
    currentAddress.lng = Number(place.geometry.location.lng()).toFixed(6);
}

$('#autocomplete').blur(function() {
    var address = $('#autocomplete').val();
    // we set a timeout to prevent conflicts between blur and place_changed events
    var timeout = setTimeout(function() {
        // release timeout
        clearTimeout(timeout);
        if (address !== currentAddress.one_liner) {
            $('#autocomplete').val(currentAddress.one_liner);
        }
    }, 500);
});

I hope this helps.

Ceratoid answered 18/7, 2018 at 16:58 Comment(2)
This is a nice solution. I didn't have access to place.formatted_address, so I had to just set one_liner to the current value of the autocomplete input field at the end of the fillInAddress function or use a more complex way to get the formatted_address : autocompleteObj.gm_bindings_.bounds[7].re.formattedPredictionCalumniate
Although I wouldn't be inclined to use the second option I suggested as it may not work in all scenarios and is rather uglyCalumniate
D
3
    var options = {
        language: 'en',
        types: ['(cities)'],
    };

    var input = document.getElementById('city');
    var autocomplete = new google.maps.places.Autocomplete(input, options);
    input.addEventListener("change", function(){
        input.value = "";
    });

Quiting the value when the input changes works for me. It forces to use Google Places.

Dilettante answered 28/3, 2019 at 10:28 Comment(1)
I tried bunch of other solutions before I tried this, and must say, it works perfectly. Nice one!Clein
A
1

I was having the same problem, below is a function that I came up with as a work-around. This could be used in combination with an event such as onchange or onkeyup. Hope it helps!

//Arguments:

address - string containing the place that you want to validate address_success_div - div object specifically used for displaying validation

function is_location_valid(address, address_success_div)
{
    var geocoder = new google.maps.Geocoder();

    geocoder.geocode( {"address": address}, function(results, status) {
        if (status == google.maps.GeocoderStatus.OK)
        {
            address_success_div.innerHTML = "SUCCESS";
        }
        else
        {
            address_success_div.innerHTML = "FAILURE";
        }
    });
}
Armalda answered 12/3, 2013 at 15:5 Comment(0)
S
1

You can use the input pattern tag, forcing the user to choose a predeterminate based on Google Autocomplete API.

The input:

    <form>
         <input type="text" onkeyup="changePatterns()" onchange="changePatterns()" id="autocomplete" required pattern="">
         <input type="submit">
    </form>

The JS file:

function changePatterns() {
    let input = document.getElementById('autocomplete').value;
    let regExp = new RegExp(' ', 'g');
    input = input.replace(regExp, '%20');

    let mapsURL = 'https://maps.googleapis.com/maps/api/place/autocomplete/json'
    mapsURL += '?types=geocode';
    mapsURL += '&key=YOUR-KEY-HERE';
    mapsURL += `&input=${input}`;
    const endpoint = 'middle.php';

    let formData = new FormData();
    formData.append('url', mapsURL);

    fetch(endpoint, {
        method: 'post',
        body: formData
        })
        .then(blob => blob.json())
        .then(response => {
            const predictions = response.predictions;
            let {length} = predictions;
            let pattern = '';
            for(let i = 0; i < length; i++){
                pattern += i == 0 ? '' : '|';
                pattern += predictions[i].description
            }
            const input = document.getElementById('autocomplete');
            input.setAttribute('pattern', pattern);
        });
}

I had to use a php intermediate because of problems with CORS policy:

<?php
header('Content-Type: application/json');
$url = $_POST['url'];
$ch = curl_init();
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); //remove on upload
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_VERBOSE, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_AUTOREFERER, false);
curl_setopt($ch, CURLOPT_REFERER, "https://www.notmydomain.com");
curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
curl_setopt($ch, CURLOPT_HEADER, 0);
$result = curl_exec($ch);
echo curl_error($ch);
curl_close($ch);
echo $result;
Snoop answered 13/12, 2019 at 19:23 Comment(0)
A
1

Just in case someone gets into this post and is using React with Google Places Autocomplete, this worked for me:

const [place, setPlace] = React.useState(null);
const input = document.getElementById('google-places-address');

const handleBlur = () => {
    if (!place) {
        onChange('');
    }
};

useEffect(() => {
    if (input) {
        input.addEventListener('blur', handleBlur);
    }

    return () => {
        if (input) {
            input.removeEventListener('blur', handleBlur);
        }
    };
}, [input]);


onPlaceSelected: (place) => {
    setPlace(place);
    (Your code here)
}

onChange is the function I get from React Hook Form controller, but should work with any input control system you use.

Basically when you click outside the autocomplete the input gets blurred and if no option from Google was selected the input gets cleaned. When you select an option, the input also gets cleaned but then filled with the onPlaceSelected function.

Abigailabigale answered 29/4 at 15:19 Comment(0)
S
0

I solved the problem combining the suggestions on a couple of different answers from here. My Textfield where the user inputs the address is called txtPlaces. Here's my code:

var pac_input = document.getElementById('txtPlaces');

(function pacSelectFirst(input) {
    // store the original event binding function
    var _addEventListener = (input.addEventListener) ? input.addEventListener : input.attachEvent;

    function addEventListenerWrapper(type, listener) {
        // Simulate a 'down arrow' keypress on hitting 'return' when no pac suggestion is selected,
        // and then trigger the original listener.
        if (type == "keydown") {
            var orig_listener = listener;
            listener = function(event) {
                var suggestion_selected = $(".pac-item-selected").length > 0;
                if ((event.which == 13 || event.which == 9) && !suggestion_selected) {
                    var simulated_downarrow = $.Event("keydown", {
                        keyCode: 40,
                        which: 40
                    });
                    orig_listener.apply(input, [simulated_downarrow]);
                }

                orig_listener.apply(input, [event]);
            };
        }



        _addEventListener.apply(input, [type, listener]);
    }

    input.addEventListener = addEventListenerWrapper;
    input.attachEvent = addEventListenerWrapper;

    var autocomplete = new google.maps.places.Autocomplete(input);

})(pac_input);

$('#txtPlaces').blur(function(e){
    selectFirstAddress(pac_input);
});


////Ensuring that only Google Maps adresses are inputted
function selectFirstAddress (input) {
    google.maps.event.trigger(input, 'keydown', {keyCode:40});
    google.maps.event.trigger(input, 'keydown', {keyCode:13});
}

This will properly react to Enter or Tab keys, as well as when the user selects another field. Hope this helps!

Sacramentalism answered 17/1, 2018 at 14:33 Comment(0)
E
0

I solved my issue using this code found here

<asp:TextBox runat="server" ID="TextBox1" />
<input type="button" id="btnSubmit" name="name" value="Validate" />
<div id="dvMap">
</div>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?libraries=places&key=AIzaSyBE1J5Pe_GZXBR_x9TXOv6TU5vtCSmEPW4"></script>
<script type="text/javascript">
    var isPlaceChanged = false;
    google.maps.event.addDomListener(window, 'load', function () {
        var places = new google.maps.places.Autocomplete(document.getElementById('<%= TextBox1.ClientID %>'));
        google.maps.event.addListener(places, 'place_changed', function () {
            isPlaceChanged = true;
            var geocoder = new google.maps.Geocoder();
            var place = places.getPlace();
            var address = place.formatted_address;
            geocoder.geocode({ 'address': address }, function (results, status) {
                if (status == google.maps.GeocoderStatus.OK) {
                    var latitude = results[0].geometry.location.lat();
                    var longitude = results[0].geometry.location.lng();
                    var mapOptions = { center: new google.maps.LatLng(latitude, longitude), zoom: 15, mapTypeId: google.maps.MapTypeId.ROADMAP };
                    var map = new google.maps.Map(document.getElementById("dvMap"), mapOptions);
                    var marker = new google.maps.Marker({ position: new google.maps.LatLng(latitude, longitude), map: map });

                } else {
                    alert("Request failed.")
                }
            });
        });
    });
    $(function () {
        $("#TextBox1").keydown(function () {
            isPlaceChanged = false;
        });

        $("#btnSubmit").click(function () {
            if (!isPlaceChanged) {
                $("#TextBox1").val('');
                alert("Please Enter valid location");
            }
            else {
                alert($("#TextBox1").val());
            }
        });
    });
</script>
Eyre answered 11/4, 2019 at 10:49 Comment(0)
Y
0

Like they mentioned it's not supported in the Google Autocomplete API. I found a way to make it work by creating a hidden element that stores the selected places than check if it's equal to the autocomplete input.

/*----------------------------------------------------*/
/*  check if it's valid address
/*----------------------------------------------------*/
function checkAddress(location, validLocation) {
if (location.val() == validLocation.val()) {
    return true;
}
return false;
}

/*----------------------------------------------------*/
/*  initialize auto complete address
/*----------------------------------------------------*/
function initialize() {
 let input = document.getElementById('autocomplete-input');
 let inputHidden = document.getElementById('autocomplete-input-hidden');
 let options = {
    types: ['(regions)'],
    componentRestrictions: {country: "tn"}
 };
 let autocomplete =new google.maps.places.Autocomplete(input, options);
 autocomplete.setFields(['formatted_address']);
 autocomplete.addListener('place_changed', function () {
    inputHidden.value= autocomplete.getPlace().formatted_address;
 });
}

call the function in your main.

 let location = $('#autocomplete-input');
 let validLocation = $('#autocomplete-input-hidden');
 checkAddress(location,validLocation);

and in your html:

 <input id="autocomplete-input" type="text">
 <input id="autocomplete-input-hidden" type="hidden" >
Youth answered 2/6, 2020 at 18:26 Comment(0)
C
0

I found a simpler solution that I implemented with jquery validation plugin, but that's not a must. I just used a class name as a flag to show whether the field was valid (i.e. the user made a selection from Google) or invalid (i.e. they typed their own stuff). I created two functions, one to add the class "err" and one to take it away.

function invalidate(element_id){
    $('#' + element_id).addClass('err');

}

function makeValid(element_id){
    if ($('#' + element_id).hasClass('err')){
        $('#' + element_id).removeClass('err');
    }
    $('#' + element_id).valid(); //this is a method from jquery validation plugin to check validity of a field
}

Then, onkeydown I called the "invalidate" function to flag that the input element had been typed into.

document.getElementById("element_id").setAttribute('onkeydown', "invalidate('element_id')");

And at the end of the Google Autocomplete API (I did it at the end of the fillInAddress method), I called "makeValid" to remove the flag because the user has just chosen an option from the dropdown menu.

makeValid(userElementId);

Finally, I validated that the "err" class that I used as my flag for when the user typed instead of choosing an option is not present. Here I used jquery validation plugin, but you could use something else if desired.

jQuery.validator.addMethod("hasClassErr", function(value, element)  {
    return this.optional( element ) || !element.classList.contains("err");
}, jQuery.validator.format("Please choose an option from the dropdown menu."));

FORM_RULES = {
    element_name: {required: true, 'hasClassErr': true}
};


$('#form_id').validate({
    rules: FORM_RULES
});
Calumniate answered 9/8, 2020 at 10:19 Comment(0)
S
0

see https://mcmap.net/q/336755/-events-other-than-39-place_changed-39-for-google-maps-autocomplete

Below doesn't work. just kept for history

An easier approach could be:

let placeInputEl = document.getElementById("placeInput");
let autcomplete = new google.maps.places.Autocomplete(placeInputEl);


placeInputEl.addEventListener("change", () => {
  //this fires when the user changes to a non-selection / freeform text
  doStuff(true);
});
autocomplete.addListener("place_changed", () => {
  //this fires when the user selects a place from Google
  doStuff(false);
});

function doStuff(isFreeText) {
  //maybe clear the selection or do whatever you need to reset
  if (isFreeText) {
    placeInputEl.value = "";
    return;
  }
  // handle a place selection
}
<input id="placeInput" />

this lets you clear the input, determine validation, reset the input, or whatever you want to do to make the selection mandatory.

the place_changed event only fires when the user selects an input or hits enter.

the change event fires before place_changed so you may be able to apply your logic without interfering with google's api call

Snuggle answered 25/11, 2021 at 15:10 Comment(0)
D
-1

Hia I have managed to do that by creating the autocomplete object each time the user focuses the input field, and checking it's value each time it's blured:

var autocompleteField;
var input = document.getElementById('YOUR_INPUT');

$('#YOUR_INPUT').focus(function(e){

    if(autocompleteField){
        autocompleteField=null;
    }
    autocompleteField = new google.maps.places.Autocomplete(input);

});

$('#YOUR_INPUT').blur(function(e){

    var results = autocompleteField.getPlace();
    if(!results){
        $(this).attr('value','');
    }
});
Dooryard answered 26/9, 2017 at 14:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.