Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

garrettmassey's avatar

Vue + Google Maps API: Adding listener to autocomplete element

I'm relatively new to Vue and am following along with the Vue tutorials and InertiaJS tutorials here on Laracasts. I have a question about how to implement some JavaScript features inside a vue component.

The Google Maps JavaScript API has an example for autocompleting an address form using Google Maps and this is the exact functionality I am trying to implement on my Vue component.

As-is, the autocomplete does initialize and does let you select the address from the suggested dropdowns, but it doesn't populate the other fields. So I don't understand how to get code autocomplete.addListener("place_changed", fillInAddress); to work, that is, how to get it to run when the address is selected from the suggested list.

Here is my component. I copied the address autocomplete code directly from the Google Maps documentation link I posted above.

<template>
	<Head title="Dashboard"/>
	<Auth>
		<section class="d-flex align-items-center justify-content-center py-6">
			<div class="container">
				<div class="row">
					<div class="row form-block">
						<div class="col-lg-12 ms-auto">
							<!-- Country-->
							<div class="mb-4">
								<label class="form-label" for="state">State</label>
								<select class="form-select" name="state" id="state">
									<option v-for="state in states" :value="state.id">
										{{ state.name }}
									</option>
								</select>
							</div>
							<!-- Street-->
							<div class="mb-4">
								<label class="form-label" for="autocomplete">Street Address</label>
								<input class="form-control" name="name" id="autocomplete">
							</div>
							<div class="mb-4">
								<label class="form-label" for="unit">Building / Unit #</label>
								<input class="form-control" name="unit" id="unit">
							</div>
							<!-- City + Zip-->
							<div class="row">
								<div class="col-md-6">
									<div class="mb-4">
										<label class="form-label" for="city">City</label>
										<input class="form-control" name="name" id="city">
									</div>
								</div>
								<div class="col-md-6">
									<div class="mb-4">
										<label class="form-label" for="zip">Zip Code</label>
										<input class="form-control" name="name" id="zip">
									</div>
								</div>
							</div>
						</div>
					</div>
				</div>
			</div>
		</section>
	</Auth>
</template>
<script>
import Auth from "@/Layouts/Auth";
import {Head} from "@inertiajs/inertia-vue3";
export default {
	props: {
		states: Object,
	},
	components: {Auth, Head, GoogleAutocomplete},
	mounted() {
		let address1Field = document.querySelector("#autocomplete");
		let address2Field = document.querySelector("#unit");
		let postalField = document.querySelector("#zip");
		// Create the autocomplete object, restricting the search predictions to
		// addresses in the US and Canada.
		let autocomplete = new google.maps.places.Autocomplete(address1Field, {
			componentRestrictions: {country: ["us"]},
			fields: ["address_components", "geometry"],
			types: ["address"],
		});
		address1Field.focus();
		// When the user selects an address from the drop-down, populate the
		// address fields in the form.
		autocomplete.addListener("place_changed", fillInAddress);

		let fillInAddress = function () {
			// Get the place details from the autocomplete object.
			const place = autocomplete.getPlace();
			let address1 = "";
			let postcode = "";

			// Get each component of the address from the place details,
			// and then fill-in the corresponding field on the form.
			// place.address_components are google.maps.GeocoderAddressComponent objects
			// which are documented at http://goo.gle/3l5i5Mr
			for (const component of place.address_components) {
				// @ts-ignore remove once typings fixed
				const componentType = component.types[0];

				switch (componentType) {
					case "street_number": {
						address1 = `${component.long_name} ${address1}`;
						break;
					}

					case "route": {
						address1 += component.short_name;
						break;
					}

					case "postal_code": {
						postcode = `${component.long_name}${postcode}`;
						break;
					}

					case "postal_code_suffix": {
						postcode = `${postcode}-${component.long_name}`;
						break;
					}
					case "locality":
						document.querySelector("#city").value = component.long_name;
						break;
					case "administrative_area_level_1": {
						document.querySelector("#state").value = component.short_name;
						break;
					}
				}
			}

			address1Field.value = address1;
			postalField.value = postcode;
			// After filling the form with address components from the Autocomplete
			// prediction, set cursor focus on the second address line to encourage
			// entry of subpremise information such as apartment, unit, or floor number.
			address2Field.focus();
		}
	},
}
</script>
0 likes
8 replies
undeportedmexican's avatar
Level 15

I actually implemented Google Maps autocomplete on my Inertia + VueJS app.

In my case, the user can add multiple autocomplete fields (as they can add multiple stops on a trip).

This is what the code that sets up the autocomplete looks like:

let ac_input = document.getElementById(`google_ac_${this.stopIndex}`);

            const ac = new this.google.places.Autocomplete(ac_input, {
                componentRestricitons: { country: 'us' },
                fields: ['address_component', 'name', 'formatted_address'],
            });

            ac.addListener('place_changed', () => {
                let place = ac.getPlace();

                this.stop.location_name = place.name;

                for (var i = 0; i < place.address_components.length; i++) {
                    let addressType = place.address_components[i].types[0];
                    if (this.componentForm[addressType]) {
                        let val = place.address_components[i][this.componentForm[addressType]];
                        this.stop[this.addressTypeConversions[addressType]] = val;
                    }
                }
            });

Let it note that in my template, I'm defining every input that will hold the autocomplete as "google_ac_${index}", so that I'm able to catch it within the code.

Also, for the code above to work, I created this two properties in my Vue Component:

componentForm: {
                name: 'name',
                street_number: 'short_name',
                route: 'short_name',
                locality: 'long_name',
                administrative_area_level_1: 'short_name',
                country: 'short_name',
                postal_code: 'short_name',
            },

            addressTypeConversions: {
                name: 'name',
                street_number: 'exterior_number',
                route: 'street_name',
                locality: 'city_label',
                administrative_area_level_1: 'state_label',
                postal_code: 'zip_code',
            },
1 like
garrettmassey's avatar

@undeportedmexican Your code combined with an article on Medium solved the issue for me. Instead of creating a callback to a function fillInAddress I ended up saying:

autocomplete.addListener("place_changed", () => {
	//fillInAddress function code
});

and that worked like a charm!

garrettmassey's avatar

@undeportedmexican Now here's an issue that has me stumped:

The google maps autocomplete works, it fills out the form as expected.... but using Vue + Inertia, the tutorial here on Laracasts says to set up a form object like so:

    <script setup>
    import { reactive } from "vue";

    let form = reactive({
        state: '',
        street: '',
        unit: '',
        city: '',
        zip: '',
        latitude: '',
        longitude: '',
    });
    </script>

So if I include the above code in my vue component, what happens is that the autocomplete still fills out the form, but using the vue inspector, the values of the form object are not set properly.

For instance, if I start typing 1234 Main and I select "1234 Main St. Los Angeles, California" from the google autocomplete, the form is filled out on the client end, but Vue only shows this:

    setup
				form:Reactive
						city:""
						latitude:""
						longitude:""
						state:""
						street:"1234 Main S"
						unit:""
						zip:""

So... since my google maps autocomplete code is all in mounted(), and the form object is in the setup, how do I make it so that the autocompleted form values show up in the form object??

I'm completely lost with this haha

garrettmassey's avatar

@undeportedmexican Wait nevermind I figured it out! In the mounted() function, instead of referencing the dom element by ID, I can just reference this.form.state or this.form.street and it works!

1 like
undeportedmexican's avatar

@davexpression I found the load-google-maps-api package. It's basically a wrapper around the call to instantiate a Google Maps object.

Then I just use it like so:

async beforeMount() {

        this.google = await GoogleMapsApiLoader({
            key: API_KEY_HERE,
            libraries: ['places']
        });

    },

And now I have a property (this.google) to do whatever I need with google api.

Please or to participate in this conversation.