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>
@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