google-mapsgoogle-places-apigoogle-places-autocomplete

Geosearch via GooglePlaces Autocomplete: Start requests after X chars


I'm using the following code to do an "intelligent" autocomplete via Geosearch using Google Places API:

var input = 'field_18';
google.maps.event.addDomListener(document.getElementById(input), 'keydown', function(event) {
    if (event.keyCode === 13) {
        event.preventDefault();
    }
});

var ppx_autocomplete = new google.maps.places.Autocomplete((document.getElementById(input)), { types: ['geocode'] });
ppx_autocomplete.setFields(['geometry', 'formatted_address']);
google.maps.event.addListener(ppx_autocomplete, 'place_changed', function () {
    var place = ppx_autocomplete.getPlace();
    document.getElementById(input).value = place.formatted_address;
    var lat = place.geometry.location.lat();
    var lng = place.geometry.location.lng();
    document.getElementById('pp_18_geocode').value = latlng;
});

Pretty common and straight forward.

The downside is: The autocompletion starts right away with the first 2 or 3 letters typed, resulting in a LOT of requests to Google, hence to my API key consumption.

Is there any way to restrict the number of requests, e.g. by sending requests only after 5 typed letters AND maybe after some delay time, e.g. not sending requests when the user still types...


Solution

  • What you are looking for can be done, but you'll need to use the Autocomplete Service [1] instead of the Autocomplete widget [2]. Here is an example that waits until the fifth character is entered to make a request, and then makes one after each 2 additional characters. The number of characters can be edited at the line "edit params here". You need to insert your own API key. A similar example is at https://codepen.io/ecglover8/pen/ExPqdNd that does every 3 characters.

    Since you won't be using the Autocomplete widget, you'll need to handle session tokens [3] yourself (not shown). From a price standpoint, whether you'll need tokens or not depends on exactly how you plan to use the predictions [4]. This example actually makes a geocoding API request using the Place ID from the prediction and displays the lat/long.

    [1] https://developers.google.com/maps/documentation/javascript/places-autocomplete#place_autocomplete_service

    [2] https://developers.google.com/maps/documentation/javascript/places-autocomplete#add-autocomplete

    [3] https://developers.google.com/maps/documentation/javascript/places-autocomplete#session_tokens

    [4] https://developers.google.com/maps/documentation/places/web-service/usage-and-billing#ac-per-request

    //declare constants
    const ac = document.getElementById("ac");
    const g = document.getElementById("geocoded");
    const results = document.getElementById("results");
    
    //listen for typing into input box
    ac.addEventListener("input", ACRequest);
    
    //show resulting predictions 
    const displayPredictions = function(predictions, status) {
      results.innerHTML = "";
      g.innerHTML = "";
      if (status != google.maps.places.PlacesServiceStatus.OK) {
        alert(status);
        return;
      }
      predictions.forEach(prediction => {
        let li = document.createElement("li");
        li.appendChild(document.createTextNode(prediction.description));
        li.dataset.placeid = prediction.place_id;
        li.addEventListener("click", Geo)
        results.appendChild(li);
      });
      let img = document.createElement("img");
      img.setAttribute("src", "https://developers.google.com/maps/documentation/images/powered_by_google_on_white.png");
      results.appendChild(img);
    };
    
    //make autocomplete request if input value length divisible by 3
    function ACRequest() {
      //edit params here
      if ((ac.value.length > 4) && (ac.value.length % 2 == 1)) {
        const service = new google.maps.places.AutocompleteService();
        service.getPlacePredictions(
          {
            //here is where you can add bounds, componentrestrictions, types, etc
            input: ac.value
          }, displayPredictions
        )
      };
    };
    function Geo() {
      console.log(this.getAttribute("data-placeid"));
      const geocoder = new google.maps.Geocoder();
      geocoder.geocode(
        {
          "placeId": this.getAttribute("data-placeid")
        }, function(address, status) {
          if (status == "OK") {
            g.innerHTML = address[0].geometry.location
          } else {
            alert("Geocode was not successful for the following reason: " + status);
          }
        });
    }
    li {
      border: 2px solid blue;
      list-style: none;
      margin: 2px;
      padding: 2px;
    }
    li:hover {
      border: 2px solid red;
    }
    .pac-card {
      border-radius: 2px 0 0 2px;
      box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
      box-sizing: border-box;
      font-family: Roboto;
      margin: 10px 10px 0 0;
      outline: none;
    }
    .title {
      background-color: #4D90FE;
      color: #FFFFFF;
      font-size: 25px;
      font-weight: 500;
      margin: 10px 0px;
      padding: 6px 12px;
    }
    <div class="pac-card">
      <div class="title">Autocomplete Makes Requests Every Three Characters</div>
      <input id="ac" size="10" type="text" />
      <ul id="results"></ul>
      <p id="geocoded"></p>
    </div>
    <script src="https://maps.googleapis.com/maps/api/js?key=YOUR_KEY&libraries=places&v=weekly" async defer></script>