Skip to content

Instantly share code, notes, and snippets.

@dschreij
Last active May 7, 2022 20:48
Show Gist options
  • Save dschreij/06ac09c3ac9be4be4d25849f08eb3121 to your computer and use it in GitHub Desktop.
Save dschreij/06ac09c3ac9be4be4d25849f08eb3121 to your computer and use it in GitHub Desktop.
Location autocomplete field using the Google Places API and Vuetify's v-combobox
<template>
<v-combobox
v-bind="$attrs"
item-text="description"
item-value="description"
:search-input.sync="query"
:loading="loading"
:items="results"
v-on="$listeners"
@input="handleInput"
>
<template v-slot:item="{ item }">
<v-list-item-content v-if="item">
<v-list-item-title v-html="item.structured_formatting.main_text" />
<v-list-item-subtitle v-html="item.structured_formatting.secondary_text" />
</v-list-item-content>
</template>
</v-combobox>
</template>
<script>
import { debounce, isEmpty, isObject } from 'lodash'
export default {
props: {
latitude: {
type: Number,
default: 52.08966
},
longitude: {
type: Number,
default: 5.11436
},
debounceMsec: {
type: Number,
default: 200
},
placesFields: {
type: Array,
default: () => ([
// 'address_components',
'formatted_address'
])
},
placesTypes: {
type: Array,
default: () => ([
'address'
])
},
placesRadius: {
type: Number,
default: 100000
}
},
data () {
return {
platform: null,
searchService: null,
placesService: null,
query: '',
results: [],
loading: false,
googleSessionToken: ''
}
},
watch: {
query (val) {
this.search(val)
}
},
created () {
this.search = debounce(this.search, this.debounceMsec)
},
mounted () {
this.initService()
},
methods: {
initService () {
this.searchService = new window.google.maps.places.AutocompleteService()
this.placesService = new window.google.maps.places.PlacesService(
new window.google.maps.Map(document.createElement('div'))
)
this.generateNewSessionToken()
},
async search (val) {
if (isEmpty(val) || val.length < 3) {
this.results = []
return
}
this.loading = true
try {
this.results = await this.searchPlace(val)
} catch (e) {
// eslint-disable-next-line no-console
console.warn('Could not retrieve location suggestions', e)
}
this.loading = false
},
searchPlace (val) {
return new Promise((resolve, reject) => {
this.searchService.getPlacePredictions({
input: val,
location: new window.google.maps.LatLng(this.latitude, this.longitude),
radius: this.placesRadius,
types: this.placesTypes,
sessionToken: this.googleSessionToken
},
(predictions, status) => {
if (status === window.google.maps.places.PlacesServiceStatus.OK) {
return resolve(predictions)
}
if (status === window.google.maps.places.PlacesServiceStatus.ZERO_RESULTS) {
return resolve([])
}
return reject(status)
})
})
},
generateNewSessionToken () {
this.googleSessionToken = new window.google.maps.places.AutocompleteSessionToken()
},
async handleInput (choice) {
// If choice is a string, simply return that
if (!isObject(choice)) {
this.$emit('input', choice)
return
}
// Already set the text to the current information as provided by the choice
this.$emit('input', choice.description)
this.loading = true
try {
// Attempt to get full address including postal code
const placeDetails = await this.getPlaceDetails(choice)
this.$emit('input', placeDetails.formatted_address)
} catch (e) {
// eslint-disable-next-line no-console
console.error(e)
}
this.loading = false
this.generateNewSessionToken()
},
getPlaceDetails (choice) {
return new Promise((resolve, reject) => {
this.placesService.getDetails({
placeId: choice.place_id,
sessionToken: this.googleSessionToken,
fields: this.placesFields
},
(result, status) => {
if (status === window.google.maps.places.PlacesServiceStatus.OK) {
return resolve(result)
}
return reject(result)
})
})
}
}
}
</script>
@dschreij
Copy link
Author

dschreij commented Nov 4, 2020

This is a first attempt to create a Vuetify-native Google autocompleting places search, using Google's Places AutocompleteService. Make sure you include the google maps api javascript library in the head of your app's html (instructions). The default values for the props are the sane defaults for my current situation. They might not be for yours, so change where appropriate.

@xyanlucasx
Copy link

Tip

is necessary use prop no-filter in v-combobox. Otherwise the search will bug and not show some results, the filtering is already being done by the google api

@dschreij
Copy link
Author

dschreij commented May 7, 2022

Thanks for the tip @xyanlucasx

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment