Vue, Google Maps, and Address Autocomplete
About 6 months ago I needed Google Maps in my Vue app and couldn’t find an appropriate package; so I made my own (x5-gmaps). A frequent question from users is how to include an address search, so here is a simple tutorial on that.
This is the result: Repository (code)
UPDATED: This is an implementation of this with the latest version: https://codesandbox.io/s/search-implementation-b53zt
Setup
Very basic setup using the Vue CLI and default preset (Babel and ESLint).
We’re just using the App.vue file with no components (you can delete everything other than App.vue and main.js in the src folder).
x5-gmaps (Google Maps plugin for Vue)
- Install via
npm install x5-gmaps
- Import and install the plugin in your main.js file:
// ...
import x5GMaps from 'x5-gmaps'
Vue.use(x5GMaps, { key: GOOGLE_KEY, libraries: ['places'] }
// ...
The Map
Setting up the map is straight forward in App.vue, just make sure the container of the <gmaps-map/>
component has a height:
<template>
<div id="app">
<gmaps-map />
</div>
</template><script>
import { gmapsMap } from 'x5-gmaps'
export default {
components: { gmapsMap }
}
</script><style>
body {
margin: 0;
padding: 0;
height: 100vh;
}
#app {
height: 100%;
}
</style>
The Autocomplete
I started with Google’s own tutorial Place Autocomplete Hotel Search, but stripped out the hotel bit to keep it simple.
How Google’s code works is you feed an existing <input/>
element to the Google Map API and it turns it into an autocomplete box. The problem is that to do so, we need a reference to the map, and when the page is opened there isn’t a map (x5-gmaps runs code to make it). This is the tricky thing with Google Maps and Vue, so we need to listen to the mounted
event of the <gmaps-map/>
which gives us the map for our ready()
method (which stores and transforms our <input/>
).
At this point it’s worth adding some CSS to the <input/>
so it sits on top of the map too:
<template>
<div id="app">
<gmaps-map @mounted="ready" />
<input ref="autocomplete" id="autocomplete" placeholder="Search" />
</div>
</template><script>
import { gmapsMap } from 'x5-gmaps'
export default {
components: { gmapsMap },
data: () => ({
autocomplete: null,
places: null,
map: null
}),
methods: {
ready(map) {
this.map = map
this.$GMaps().then((maps) => {
this.places = new maps.places.PlacesService(map)
this.autocomplete = new maps.places.Autocomplete(this.$refs.autocomplete)
})
}
}
}
</script><style>
/* ... */
#autocomplete {
border: 10px solid rgba(0, 60, 255, 0.329);
font-size: 18px;
height: 40px;
left: 40%;
padding: 0 10px;
position: absolute;
top: 0;
width: 20%;
z-index: 1;
}
</style>
Using it for something
So you should have a map and an autocomplete visible; but it’s still useless. For fun, let’s add a marker which moves (along with the map) to wherever you select.
First, let’s add the marker. We just use the <gmaps-marker/>
component inside our <gmaps-map/>
component, and give it the position data (markerPos
) it needs in the shape of a reactive property:
<template>
<div id="app">
<gmaps-map @mounted="ready">
<gmaps-marker :position="markerPos" />
</gmaps-map>
<input ref="autocomplete" id="autocomplete" placeholder="Search" />
</div>
</template><script>
import { gmapsMap, gmapsMarker } from 'x5-gmaps'
export default {
components: { gmapsMap, gmapsMarker },
data: () => ({
autocomplete: null,
places: null,
map: null,
markerPos: { lat: -27, lng: 153 }
}),
...
Now we need to add a listener for the Google Maps autocomplete
instance which we fed our <input/>
to. And referring to the method we will add very shortly (this.update
) we now have:
methods: {
ready(map) {
this.map = map
this.$GMaps().then((maps) => {
this.places = new maps.places.PlacesService(map)
this.autocomplete = new maps.places.Autocomplete(this.$refs.autocomplete)
this.autocomplete.addListener('place_changed', this.update)
})
}
}
Last but not least, the update()
method gets the place of the autocomplete instance, and if it’s an actual place (and not just text), pans the map to that location and sets the marker there too:
update() {
const place = this.autocomplete.getPlace()
if (place.geometry) {
this.map.panTo(place.geometry.location)
this.markerPos = place.geometry.location
}
}
Conclusion
This is a pretty basic example and I hope to get to a more complicated one using Google’s AutocompleteService which lets you design your own combobox. Even better, I’d like to also include out-of-the-box components in the x5-gmaps package… Make sure you give me some 👏claps👏 on here and ⭐stars⭐ on Github so I know it’s worth my time 🧔