v-autocomplete 与 Google Places Autocomplete API 兼容吗
Is v-autocomplete compatible with Google Places Autocomplete API
我尝试使用 Google Places Autocomplete API to populate Vuetify Autocomplete Component 如下:
<template>
<v-autocomplete
ref="autocomplete"
label="Location"
>
</v-autocomplete>
</template>
<script>
export default {
mounted() {
var autocomplete = new google.maps.places.Autocomplete(
/** @type {!HTMLInputElement} */(this.$refs.autocomplete),
{types: ['geocode']})
}
}
</script>
但是在浏览器的开发者控制台中,却报错:
InvalidValueError: not an instance of HTMLInputElement
我的猜测是 v-autocomplete
不是 HTMLInputElement 类型。
(不仅是 v-autocomplete
的情况:用 v-input
替换它也会导致同样的错误。)
有没有办法让 v-autocomplete
充满 Google 地点自动完成 API,比如手动将其设为 HTMLInputElement
的实例?
我现在也在搞这个,让它部分工作,当我把它完全清除后会更新,但现在你的代码有问题。
当您在模板中应用 ref="autocomplete"
时,您是在将 ref
应用到组件而不是输入。为了让它工作,我设置了一个直接应用于输入的 id="autocomplete"
,在我安装的函数中创建了一个变量来引用输入 id,然后我将它传递给自动完成函数。我在下面更新了您的代码以反映这一点。
<template>
<v-autocomplete
id="autocomplete"
label="Location"
>
</v-autocomplete>
</template>
<script>
export default {
mounted() {
var autocompleteInput = document.querySelector('#autocomplete');
var autocomplete = new google.maps.places.Autocomplete(
/** @type {!HTMLInputElement} */(autocompleteInput),
{types: ['geocode']})
}
}
</script>
您可以将相同的原则应用于 v-text field
,但 google 自动完成结果将出现在输入下方的它们自己的容器中,而不是 select 下拉容器中,例如 v-autocomplete
.
似乎没有直接的方法可以在仅使用 google.maps.places.Autocomplete
时同时保持 material 和 v-autocomplete
的外观。为了实现这一点,我包装了 API 的 getPlacePredictions()
方法 - 而不是组件 - 称为 Autocomplete Service:
PlacesUtils.js
/* global google */
const GetSuggestions = async searchText => {
let result
try {
const rawResult = await searchLocation(searchText)
result = rawResult.map((res) => {
return {
id: res.place_id,
value: res.description
}
})
} catch (err) {
console.log('An error occurred', err)
result = null
}
return result
}
// Auxiliary functions
// wrap google api's callback to an async function
const searchLocation = async val => {
let promise = await new Promise((resolve, reject) => {
var displaySuggestions = (predictions, status) => {
if (status !== google.maps.places.PlacesServiceStatus.OK) {
reject(status)
}
resolve(predictions)
}
var service = new google.maps.places.AutocompleteService()
service.getPlacePredictions({
input: val,
types: ['geocode']
},
displaySuggestions)
}).catch(function (err) { throw err })
return promise
}
export { GetSuggestions }
然后,为 v-autocomplete
的模型添加一个 watch
我在用户所做的更改中调用此方法如下:
Place.vue
<template>
<v-layout row justify-center>
<!-- ... -->
<v-autocomplete
label="Location"
v-model="autocompleteLocationModel"
:items="locationFoundItems"
:search-input.sync="locationSearchText"
item-text="value"
item-value="id"
hide-no-data
return-object
>
</v-autocomplete>
<!-- ... -->
</v-layout>
</template>
<script>
/* eslint handle-callback-err: "warn" */
import { GetSuggestions } from '@/utils/PlaceUtils'
export default {
data () {
return {
autocompleteLocationModel: null,
locationSearchText: null,
locationEntries: []
}
},
computed: {
locationFoundItems () {
return this.locationEntries
}
},
watch: {
locationSearchText (newVal) {
var _vue = this
// If less than 3 chars typed, do not search
if (!newVal || newVal.length <= 3) return
// Call the method from the previous section here
GetSuggestions(newVal)
.then(function (res) {
_vue.locationEntries = res
})
.catch(function (err) {
// error handling goes here
})
}
}
// ...
}
</script>
@vahdet 我想感谢你的代码,我在其中用作一个完整的组件,在“地点”上发出选定地点的详细信息谢谢你的帮助!
<template>
<v-layout row justify-center>
<!-- ... -->
<v-autocomplete
label="Location"
id="decoy"
v-model="autocompleteLocationModel"
:items="locationFoundItems"
:search-input.sync="locationSearchText"
item-text="value"
item-value="id"
hide-no-data
return-object
>
</v-autocomplete>
<!-- ... -->
</v-layout>
</template>
<script>
/* eslint handle-callback-err: "warn" */
import { GetSuggestions } from "../../../PlacesUtils";
export default {
data() {
return {
autocompleteLocationModel: null,
locationSearchText: null,
locationEntries: [],
};
},
computed: {
locationFoundItems() {
return this.locationEntries;
},
},
watch: {
autocompleteLocationModel(newVal) {
console.log(newVal.id);
let resplace = new google.maps.places.PlacesService(
document.getElementById("decoy")
);
resplace.getDetails(
{
placeId: newVal.id
},
(x) => {
this.$emit("place", x);
}
);
},
locationSearchText(newVal) {
var _vue = this;
// If less than 3 chars typed, do not search
if (!newVal || newVal.length <= 3) return;
// Call the method from the previous section here
GetSuggestions(newVal)
.then(function(res) {
_vue.locationEntries = res;
})
.catch(function(err) {
// error handling goes here
console.log(err);
});
},
},
};
</script>
我尝试使用 Google Places Autocomplete API to populate Vuetify Autocomplete Component 如下:
<template>
<v-autocomplete
ref="autocomplete"
label="Location"
>
</v-autocomplete>
</template>
<script>
export default {
mounted() {
var autocomplete = new google.maps.places.Autocomplete(
/** @type {!HTMLInputElement} */(this.$refs.autocomplete),
{types: ['geocode']})
}
}
</script>
但是在浏览器的开发者控制台中,却报错:
InvalidValueError: not an instance of HTMLInputElement
我的猜测是 v-autocomplete
不是 HTMLInputElement 类型。
(不仅是 v-autocomplete
的情况:用 v-input
替换它也会导致同样的错误。)
有没有办法让 v-autocomplete
充满 Google 地点自动完成 API,比如手动将其设为 HTMLInputElement
的实例?
我现在也在搞这个,让它部分工作,当我把它完全清除后会更新,但现在你的代码有问题。
当您在模板中应用 ref="autocomplete"
时,您是在将 ref
应用到组件而不是输入。为了让它工作,我设置了一个直接应用于输入的 id="autocomplete"
,在我安装的函数中创建了一个变量来引用输入 id,然后我将它传递给自动完成函数。我在下面更新了您的代码以反映这一点。
<template>
<v-autocomplete
id="autocomplete"
label="Location"
>
</v-autocomplete>
</template>
<script>
export default {
mounted() {
var autocompleteInput = document.querySelector('#autocomplete');
var autocomplete = new google.maps.places.Autocomplete(
/** @type {!HTMLInputElement} */(autocompleteInput),
{types: ['geocode']})
}
}
</script>
您可以将相同的原则应用于 v-text field
,但 google 自动完成结果将出现在输入下方的它们自己的容器中,而不是 select 下拉容器中,例如 v-autocomplete
.
似乎没有直接的方法可以在仅使用 google.maps.places.Autocomplete
时同时保持 material 和 v-autocomplete
的外观。为了实现这一点,我包装了 API 的 getPlacePredictions()
方法 - 而不是组件 - 称为 Autocomplete Service:
PlacesUtils.js
/* global google */
const GetSuggestions = async searchText => {
let result
try {
const rawResult = await searchLocation(searchText)
result = rawResult.map((res) => {
return {
id: res.place_id,
value: res.description
}
})
} catch (err) {
console.log('An error occurred', err)
result = null
}
return result
}
// Auxiliary functions
// wrap google api's callback to an async function
const searchLocation = async val => {
let promise = await new Promise((resolve, reject) => {
var displaySuggestions = (predictions, status) => {
if (status !== google.maps.places.PlacesServiceStatus.OK) {
reject(status)
}
resolve(predictions)
}
var service = new google.maps.places.AutocompleteService()
service.getPlacePredictions({
input: val,
types: ['geocode']
},
displaySuggestions)
}).catch(function (err) { throw err })
return promise
}
export { GetSuggestions }
然后,为 v-autocomplete
的模型添加一个 watch
我在用户所做的更改中调用此方法如下:
Place.vue
<template>
<v-layout row justify-center>
<!-- ... -->
<v-autocomplete
label="Location"
v-model="autocompleteLocationModel"
:items="locationFoundItems"
:search-input.sync="locationSearchText"
item-text="value"
item-value="id"
hide-no-data
return-object
>
</v-autocomplete>
<!-- ... -->
</v-layout>
</template>
<script>
/* eslint handle-callback-err: "warn" */
import { GetSuggestions } from '@/utils/PlaceUtils'
export default {
data () {
return {
autocompleteLocationModel: null,
locationSearchText: null,
locationEntries: []
}
},
computed: {
locationFoundItems () {
return this.locationEntries
}
},
watch: {
locationSearchText (newVal) {
var _vue = this
// If less than 3 chars typed, do not search
if (!newVal || newVal.length <= 3) return
// Call the method from the previous section here
GetSuggestions(newVal)
.then(function (res) {
_vue.locationEntries = res
})
.catch(function (err) {
// error handling goes here
})
}
}
// ...
}
</script>
@vahdet 我想感谢你的代码,我在其中用作一个完整的组件,在“地点”上发出选定地点的详细信息谢谢你的帮助!
<template>
<v-layout row justify-center>
<!-- ... -->
<v-autocomplete
label="Location"
id="decoy"
v-model="autocompleteLocationModel"
:items="locationFoundItems"
:search-input.sync="locationSearchText"
item-text="value"
item-value="id"
hide-no-data
return-object
>
</v-autocomplete>
<!-- ... -->
</v-layout>
</template>
<script>
/* eslint handle-callback-err: "warn" */
import { GetSuggestions } from "../../../PlacesUtils";
export default {
data() {
return {
autocompleteLocationModel: null,
locationSearchText: null,
locationEntries: [],
};
},
computed: {
locationFoundItems() {
return this.locationEntries;
},
},
watch: {
autocompleteLocationModel(newVal) {
console.log(newVal.id);
let resplace = new google.maps.places.PlacesService(
document.getElementById("decoy")
);
resplace.getDetails(
{
placeId: newVal.id
},
(x) => {
this.$emit("place", x);
}
);
},
locationSearchText(newVal) {
var _vue = this;
// If less than 3 chars typed, do not search
if (!newVal || newVal.length <= 3) return;
// Call the method from the previous section here
GetSuggestions(newVal)
.then(function(res) {
_vue.locationEntries = res;
})
.catch(function(err) {
// error handling goes here
console.log(err);
});
},
},
};
</script>