Implementing Google Places Autocomplete with ES6 - Part One

The Google Places JavaScript API is a great client-side toolset that uses the same database as Google Maps. While Google’s own quick-start documentation is a fantastic resource, we’ll be taking a deeper dive in this series to examine the API and address both common and complex bugs that we’ve encountered and fixed while implementing the Places Autocomplete functionality.

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
Google Places Autocomplete

Let’s explore how we can implement Place Autocomplete on a form to automatically fill in an address as you type using ES6.

First we’ll add an event listener to our markup. Then we’ll explore the data returned by the Google Places Autocomplete service, and finally we’ll map that data to our form to populate the form fields automatically.

Getting Started with the Google Maps JavaScript API

To begin, you’ll include the Google Places library script on your page. This allows us to create an autocomplete object with specific parameters using the google.maps.places.Autocomplete class.

You won’t necessarily need to include an API key during development, but you will need one for any production environment.

<script src=“https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&libraries=places”>

Adding an Autocomplete Listener

Before we initialize a Google Autocomplete class, let’s put together all of the pieces we need for the constructor, the specified input text field that the Autocomplete functionality is attached to.

First we’ll specify the field by getting the element’s ID.

Since we’ll be passing this around to multiple methods, let’s define it as a const.

const autocompleteFormField = document.getElementById(`street-
address-field
`
);

Now that we have our input field defined, we can instantiate a new autocomplete object:

const autocomplete = new google.maps.places.Autocomplete((autocompleteFormField), {
types: [`address`],
componentRestrictions: [`us`],
});

Let’s dissect the options that we included above. The types value is an array that allows us to specify the type of results that are returned from the Places service. The componentRestrictions value restricts the results to a specific group, which we are using to restrict all results to the United States (us).

Now that we have our autocomplete object instantiated, our form will now return autocomplete prediction results when you start typing in the input field.

Autocomplete Step 2

We can utilize these results by adding the custom Google Places listener, place_changed, which will activate when a result from the autocomplete list is selected.

google.maps.event.addListener(autocomplete, ‘place_changed’, () => {
// Custom methods to populate form fields.
}

We’ll be writing our own custom methods to populate the form fields with the Google Maps data later in the post.

We should always clear out any listeners before a new one is instantiated to keep our prediction results consistent to the latest set of prediction results. Depending on your implementation, you might need to call this method several times. For example, if you have a country selector to restrict results to a specific country, and need to dynamically update the autocomplete object and listener, you will need to clear out any listeners each time the country value is altered by the user. Clearing the listener ensures that the results that are returned always correspond to the latest user input.

google.maps.event.clearInstanceListeners(autocompleteFormField);
google.maps.event.addListener(autocomplete, ‘place_changed’, () => {
// Custom methods to populate form fields.
}

Our event listener is using an arrow function, which is an ES6 expression that binds the this value lexically. This is essentially equivalent to:

google.maps.event.addListener(‘place_changed’, function() {
// Custom methods to populate form fields
}

Before we write our custom methods to populate the form field, let’s first take a look at what’s actually returned from Google’s Places Autocomplete service.

Exploring the Google Places Array

With our autocomplete object instantiated, we can use the getPlace() method to pull in the data returned from Google. This data is returned as an array that we can parse through to grab the relevant data for our fields.

const place = autocomplete.getPlace();

Let’s place this within our event listener:

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

Before we continue, let’s review the sequence of events so far. The getPlace() method fires in tandem with the place_changed event listener. The place that’s returned is the result that is selected from the prediction results that were attached to our original input field.

The .getPlace() method returns an object that can be used to populate our form fields.

.getPlace Method

Since we want to parse the data on a more granular level to populate our form, we have to dig into the address_components array more deeply:

Address Components

Here we can see that each object in the array has the following items:

  • long_name: The full name of a component.

  • short_name: The abbreviated name of a component.

  • types: An array of the types of address component for an object.

To populate our form fields, we’ll have to create a map of the types returned in this array with the IDs of the form elements that we will be populating.

Mapping form fields with Google’s data

We need to create an object that is a simple key/value pair to map the data keys for the data returned from Google’s Autocomplete service to the IDs of our form fields.

const googleComponents = [
{ googleComponent: `sublocality_level_1`, id: `city-address-field` },
{ googleComponent: `locality`, id: `city-address-field` },
{ googleComponent: `administrative_area_level_1`, id: `state-address-field` },
{ googleComponent: `postal_code`, id: `postal—code-address-field` },
],

In the example above we’ve mapped both the sublocality_level_1 and locality to the same field. This may not be necessary depending on your use case, but we’re mapping them this way because some addresses in cities, such as New York, will only return a sub locality as seen in the response below:

Sublocality Components

Populating the form fields

Now that we have our map object in place, we can iterate through each key/value pair and access the address_components array:

for (const component in googleComponentMap) {
const addressComponents = place.address_components;
addressComponents.forEach(addressComponent => populateFormElements(addressComponent, googleComponents[component]));
}

In our custom populateFormElements method we are passing in an addressComponent and the corresponding value from our mapped object.

function populateFormElements(addressComponent, formMap) {
const addressType = addressComponent.types[0];
if (formMap.googleComponent === addressType) {
let formValue = addressComponent.long_name;
document.getElementById(formMap.id).value = formValue;
}
}
}

Implementing Autocomplete

This implementation uses the standard widget provided by Google to quickly add Autocomplete to your form. Take a look at a working example of our compiled code here and the full ES6 module below:

function initAutocomplete() {
const googleComponents = [
{ googleComponent: `sublocality_level_1`, id: `city-address-field` },
{ googleComponent: `locality`, id: `city-address-field` },
{ googleComponent: `administrative_area_level_1`, id: `state-address-field` },
{ googleComponent: `postal_code`, id: `postal-code-address-field` },
];
const autocompleteFormField = document.getElementById(`street-address-field`);
const autocomplete = new google.maps.places.Autocomplete((autocompleteFormField), {
types: [`address`],
componentRestrictions: [`us`],
});
google.maps.event.clearInstanceListeners(autocompleteFormField);
google.maps.event.addListener(autocomplete, `place_changed`, () => {
const place = autocomplete.getPlace();
autocompleteFormField.value = place.name;
for (const component in googleComponents) {
const addressComponents = place.address_components;
addressComponents.forEach( addressComponent => populateFormElements(addressComponent, googleComponents[component]));
}
});
}

function populateFormElements(addressComponent, formMap) {
const addressType = addressComponent.types[0];
if (formMap.googleComponent === addressType) {
const formValue = addressComponent.long_name;
document.getElementById(formMap.id).value = formValue;
}
}

When we test our implementation with a variety of addresses we’ll see that some addresses, particularly those on new developments or streets, might return an address in the list but won’t have the appropriate Place data to populate our field with the address numbers.

Autocomplete Address Number Error

This is an unresolved bug that will interfere with address verification if we’re using this to ship a product to our users. We will examine the classes, data and methods available to us to resolve this bug by extending our current implementation in part two. Stay tuned.