Find when google places autocomplete returns zero

2019-06-20 08:26发布

问题:

I use the google places autocomplete for places suggestion. Now this is what I want: when a user types in a location that is not in google's database, that is google returns zero results, I need to be able to capture the event of zero results, and fire my custome autocomplete.

How do I find when google is returning zero results? autocomplete.getPlace() will return a status if there are zero results, but this is inside place_changed event, which is fired only when user selects a suggestion from the dropdown list. How do I make it work outside the place_changed event?

This is the code I use, please give suggestions based on this:

autocomplete = new google.maps.places.Autocomplete(input,options);
google.maps.event.addListener(autocomplete, 'place_changed', function() {
   var place = autocomplete.getPlace();
   document.getElementById('place_name').value = place.name;
   document.getElementById('location_lat').value = place.geometry.location.lat();
   document.getElementById('location_lon').value = place.geometry.location.lng();
   document.getElementById('location_add').value = place.formatted_address;
   $('#locationInput').attr('title', place.formatted_address);
});

回答1:

Found a trick,

AutoComplete triggered by keyup event of input node, then:

Hide result dropdown list by display: none; if no result

or

Show result dropdown list by clear display style if has result

You can get dropdown list by document.getElementsByClassName('pac-container')[0] (native) or $('.pac-container') (jQuery)

So just set the display of dropdown list to 'block' then tracking it to detect the result status.

A simple sample modified from the google official sample:

<!DOCTYPE html>
<html>
    <head>
        <title>Google Maps JavaScript API v3 Example: Places Autocomplete</title>
        <script src="https://maps.googleapis.com/maps/api/js?sensor=false&libraries=places"
            type="text/javascript"></script>

        <style type="text/css">
            body {
                font-family: sans-serif;
                font-size: 14px;
            }
            #map_canvas {
                height: 400px;
                width: 600px;
                margin-top: 0.6em;
            }
        </style>

        <script type="text/javascript">
            var ns = {}; // a name space
            ns.checktimes = 0; // a couter of check times
            // the check function
            // @param dropdown: the drop-down list of Places.AutoComplete
            // @param msg: the div to show message
            ns._doCheck = function (dropdown, msg) {
                if (dropdown.style.display == '') {
                    msg.innerHTML = 'has results? true';
                    ns.checkTimer = null;
                }
                else if (dropdown.style.display == 'none') {
                    msg.innerHTML = 'has results? false';
                    ns.checkTimer = null;
                } else if (ns.checktimes < 20) { // check at most 10 seconds
                    ns.checktimes++;
                    ns.checkTimer = setTimeout(function () {
                        ns._doCheck(dropdown, msg);
                    }, 500);
                }
            }
            function initialize() {
                var mapOptions = {
                    center: new google.maps.LatLng(-33.8688, 151.2195),
                    zoom: 13,
                    mapTypeId: google.maps.MapTypeId.ROADMAP
                };
            var map = new google.maps.Map(document.getElementById('map_canvas'),
                mapOptions);

            var input = document.getElementById('searchTextField');
            var autocomplete = new google.maps.places.Autocomplete(input);

            autocomplete.bindTo('bounds', map);

            var infowindow = new google.maps.InfoWindow();
            var marker = new google.maps.Marker({
                map: map
            });

            google.maps.event.addListener(autocomplete, 'place_changed', function() {
                infowindow.close();
                var place = autocomplete.getPlace();
            if (place.geometry.viewport) {
                map.fitBounds(place.geometry.viewport);
            } else {
                map.setCenter(place.geometry.location);
                map.setZoom(17);  // Why 17? Because it looks good.
            }

            var image = new google.maps.MarkerImage(
                place.icon,
                new google.maps.Size(71, 71),
                new google.maps.Point(0, 0),
                new google.maps.Point(17, 34),
                new google.maps.Size(35, 35));
            marker.setIcon(image);
            marker.setPosition(place.geometry.location);

            var address = '';
            if (place.address_components) {
                address = [(place.address_components[0] &&
                place.address_components[0].short_name || ''),
                (place.address_components[1] &&
                place.address_components[1].short_name || ''),
                (place.address_components[2] &&
                place.address_components[2].short_name || '')
                ].join(' ');
            }

            infowindow.setContent('<div><strong>' + place.name + '</strong><br>' + address);
            infowindow.open(map, marker);
            // update stored value
            ns.oldValue = document.getElementById('searchTextField').value;
        });

        // Sets a listener on a radio button to change the filter type on Places
        // Autocomplete.
        function setupClickListener(id, types) {
            var radioButton = document.getElementById(id);
            google.maps.event.addDomListener(radioButton, 'click', function() {
            autocomplete.setTypes(types);
            });
        }

        setupClickListener('changetype-all', []);
        setupClickListener('changetype-establishment', ['establishment']);
        setupClickListener('changetype-geocode', ['geocode']);
        }

        // to check whether responsee and has results
        function startCheck () {
            // the input node
            var inp = document.getElementById('searchTextField'),
                value = inp.value; // value of input node
            if (value && ns.oldValue != value) { // has value and changed, start check
                // drop-down list and message div
                var dropdown = document.getElementsByClassName('pac-container')[0],
                    msg = document.getElementById('msg');
                // trick! change style to display='block'
                dropdown.style.display = 'block';
                // update stored value
                ns.oldValue = value;
                // initiate checktimes
                ns.checktimes = 0;
                // clear previous timer if exists
                if (ns.checkTimer)
                    clearTimeout (ns.checkTimer);
                ns.checkTimer = setTimeout(function () {
                    ns._doCheck(dropdown, msg);
                }, 500);
            }
        }
        google.maps.event.addDomListener(window, 'load', initialize);
    </script>
    </head>
    <body>
        <div>
            <div id="msg">has results? </div>
            <input id="searchTextField" type="text" size="50" onkeyup="startCheck();">
            <input type="radio" name="type" id="changetype-all" checked="checked">
            <label for="changetype-all">All</label>

            <input type="radio" name="type" id="changetype-establishment">
            <label for="changetype-establishment">Establishments</label>

            <input type="radio" name="type" id="changetype-geocode">
            <label for="changetype-geocode">Geocodes</lable>
        </div>
        <div id="map_canvas"></div>
    </body>
</html>


回答2:

I think your scenario may be different than Google returning zero results. One of the assumptions in your question, that the place_changed event "is fired only when [the] user selects a suggestion from the dropdown list," is not correct. From the google.maps.places.Autocomplete class Event docs for place_changed:

This event is fired when a PlaceResult is made available for a Place the user has selected. If the user enters the name of a Place that was not suggested by the control and presses the Enter key, a place_changed event will be fired that contains the user input in the name property, with no other properties defined.

Otherwise, Google should be returning you results, because the user has selected one of the results that was in the Autocomplete's dropdown options. If direct user entry is not what is driving your scenario, shouldn't the check for zero results simply involve checking one, some, or a subset of the google.maps.places.PlaceResult properties, such as:

  • the: address_components Array of google.maps.GeocoderAddressComponents
  • id
  • name
  • reference
  • the types Array of strings (e.g., [political, locality] or [restaurant, establishment])


回答3:

You can get results on this way as well. Predictions is array of what google maps returns.

const displaySuggestions = (predictions, status) => {
      if (status !== google.maps.places.PlacesServiceStatus.OK) {
        if (status === 'ZERO_RESULTS') {
          alert('zero results found')
        }
        return;
      }
    };
    // you can create autocompleteService as variable outside the function as well
    const autocompleteService = new google.maps.places.AutocompleteService();
    // input is string of what user types 
    autocompleteService.getPlacePredictions({ input: input, types: ['address'] }, displaySuggestions);