Implementing Google Places Autocomplete with ES6 - Part Two

In part one we took a look at how to implement a Google Places Autocomplete widget on an address form and discovered a bug that degrades the user experience for certain addresses.

Other posts in this series:

  1. Implementing Google Places Autocomplete with ES6 - Part One
  2. Implementing Google Places Autocomplete with ES6 - Part Two
  3. Implementing Google Places Autocomplete with ES6 - Part Three

As we investigate this issue, let’s first define our user story for this feature as the following:

When a user enters an address into the street address form field, generate a list of predictions from which the user can select to populate the address form.

This operates on the following assumption of the Autocomplete widget in its current state:

The addresses in the list of predictions are the same as the address data that’s used to populate the fields when a user selects an address.

Let’s take a look at what the bug currently looks like on our form. Google Autocomplete Bug

In the image above, the address is found in the list of predictions returned from Google, but when that address is selected we only get the street, city, state and zip sans the actual address number. This is a critical issue if you’re using the form data for shipping. Let’s begin by parsing through the data that’s returned from Google to see if we can diagnose and resolve this bug.

Deconstructing the Widget

The following line of code is the key to the implementation found in part one:

google.maps.event.addListener(autocomplete, ‘place_changed’, () => {
const place = autocomplete.getPlace();

This adds a custom event listener to our the autocomplete object and assigns it to the address input on our form. This does three things:

  1. Builds a list of predictions from the autocomplete object, based on the user’s input in the address field.
  2. Adds a place_changed event listener which alters the autocomplete object when a place is selected.
  3. Gets place from the autocomplete object when an address is selected, seen above as the place_changed event.

Let’s explore the place variable in the browser console to see if there are parameters that can be used to filter the results to the specificity we need.

Street Level Match

In the image above, we can see that there is a types array which has a street_address value. When we input the address that exhibits the bug we can see that the types array has a route value as seen below.

Route Level Match

From these results we can deduce that the values returned in the types array are equivalent to the degree of specificity for an address. In short, a street_address will return the full street address such as 875 N Michigan Ave, whereas a route will only return the street N Michigan Ave.

With this knowledge in hand, we can apply a filter to only return an autocomplete place if it matches a street_address verification:

google.maps.event.addListener(autocomplete, ‘place_changed’, () => {
const returnedPlace = autocomplete.getPlace();
if (returnedPlace.types[0] === `street_address`) {
const place = returnedPlace;

While this may appear to resolve the bug, we’ll see that it actually leads to more problems as seen below.

Autocomplete Bug Unfixed

Instead of populating some of the form fields after a user selects an address from the list, we populate nothing. This is a frustrating user experience and a huge regression. If we were to define the perfect user interaction it would be this:

When a user enters an address, a list of address predictions is presented to the user. Any option that is presented in the list of addresses has viable address data returned from Google, which is used to populate the address form when selected.

In order to achieve this we’ll have to find a way to decouple the widget so that the list of predictions and the autocomplete object that’s returned share the same data.

Dissecting the Autocomplete Widget

To do this, let’s examine the relevant API methods and classes available to us. We’ll address this outside of our current widget by reconstructing a customized version of the widget by calling the methods directly in the following order.

  1. getPlacePredictions -- builds the prediction list.
  2. New google.maps.places.PlacesService -- initializes the PlacesService class.
  3. google.maps.places.PlacesService.getDetails -- gets the details based off of the from the prediction list.

The autocomplete widget packages these methods into a single call, which is convenient and works well for most addresses. Google’s suggestion for this bug is to fill out a form for the problematic addresses so that they’re eventually included into the Google Places database. But we cannot expect our users to check or verify that their address exists in Google’s database while they are filling out our form.

To achieve the degree of accuracy we want for our autocomplete implementation, and to present our users with a pleasant experience, we will need to rebuild the widget from the ground up. To do this we’ll start by including our own event listeners which build the prediction list based on events tied to the input of the address field. Then we’ll initialize the PlacesServices class and include the autocomplete method for each prediction that is generated. Finally we’ll add in the interactions that make the widget work well and account for keyboard interactions when traversing and selecting the prediction list. Stay tuned as we’ll be going through these steps in detail in part three.