如何在 Vuetify Autocomplete 组件中创建无限滚动? [英] How to create infinite scroll in Vuetify Autocomplete component?
问题描述
我有一个带有 Vuetify Autocomplete 组件的页面,以及带有/vendors"方法的 REST API 后端.此方法采用 limit、page 和 name 参数并返回带有 id 和 name 的 JSON> 字段.
我在用户输入事件上做了一些延迟列表加载的代码.但现在我想添加在用户滚动事件上加载此列表的功能.
例如,默认情况下有 100 个供应商的列表.用户滚动此列表直到最后,然后调用某个事件"并加载接下来的 100 个供应商.然后用户继续滚动并重复该操作.
是否可以使用 Vuetify Autocomplete 组件来实现这一点,还是应该使用其他库?
当前组件的示例代码如下所示:
<v-自动完成:items="供应商"v-model="selectedVendorId"项目文本=名称"项目值=id"label="选择供应商"@input.native="getVendorsFromApi"></v-自动完成></模板><脚本>导出默认{数据 () {返回 {页数:0,限制:100,selectedVendorId: null,供应商:[],加载:真实}},创建:函数(){this.getVendorsFromApi();},方法: {getVendorsFromApi(事件){返回新的承诺(() => {this.$axios.get(this.$backendLink+ '/vendors?limit=' + this.limit+ '&page=' + this.page+ '&name=' + (event ? event.target.value : '')).then(响应 => {this.vendors = response.data;})})}}}
我能够使用 Vuetify AutoComplete 组件进行自动加载.这有点黑客,但基本上解决方案是使用 v-slot
附加项目,v-intersect
指令来检测该附加项目是否可见,以及如果是,请调用您的 API 以获取更多项目并将其附加到您当前的列表中.
...
出口默认{方法: {endIntersect(条目,观察者,isIntersecting){如果(是相交){让 moreVendors = loadMoreFromApi()this.vendors = [ ...this.vendors, ...moreVendors]}}}}
在我的用例中,我使用 API 平台作为后端,使用 GraphQL 分页和游标.
<组件v-bind:is="canAdd ?'v-combobox' : 'v-自动完成'"v-model=用户":items="computedUsers";:search-input.sync="搜索"item-text="item.node.userProfile.username";隐藏细节四舍五入独奏:过滤器=(item, queryText, itemText) =>{返回 item.node.userProfile.username.toLocaleLowerCase().indexOf(queryText.toLocaleLowerCase()) >-1} ":loading="加载";项目值=用户名"class="text-left pl-1";颜色=蓝灰色变亮-2":label="标签"><template v-slot:selection="{ item }"><v-chip v-if="typeof item == 'object'"><v-头像左边><v-img v-if=item.node.userProfile.image":src=item.node.userProfile.image"/><v-icon v-else>mdi-account-circle</v-icon></v-头像>{{ item.node.userProfile.firstName }} {{ item.node.userProfile.lastName }}</v-chip><v-chip v-else-if="typeof item == 'string'">{{ 物品 }}</v-chip></模板><template v-slot:item={ item: { node } }"><v-list-item-avatar ><img v-if="node.userProfile.avatar";:src=node.userProfile.avatar"/><v-icon v-else>mdi-account-circle</v-icon></v-list-item-avatar><v-list-item-content class="text-left"><v-list-item-title>{{ $t('fullName', { firstName: node.userProfile.firstName, lastName: node.userProfile.lastName } )}}</v-list-item-title><v-list-item-subtitle v-html=node.userProfile.username"></v-list-item-subtitle></v-list-item-content></模板><template v-slot:append-item=""><div v-intersect="endIntersect";>
</模板></组件>从vuetify/lib"导入 { VCombobox, VAutocomplete };从@/helpers/debounce"导入去抖动;从@/graphql/UserQueries"导入 { SEARCH_USER_BY_USERNAME };const RESULTS_TO_SHOW = 5导出默认{道具: {可以添加:{类型:布尔型,默认值:假,},值:[对象,字符串],标签:字符串,},组件:{ VCombobox, VAutocomplete },阿波罗:{用户:{查询:SEARCH_USER_BY_USERNAME,变量(){返回 {用户名:this.search,numberToShow:RESULTS_TO_SHOW,游标:空,}},watchLoading(isLoading) {this.loading = isLoading},跳过() {如果(this.search){返回 !(this.search.length > 1)}返回真},},},数据() {返回 {用户:this.value,搜索:",游标:空,加载:假,};},手表: {用户(新值){让发射 = newValue如果(新值){发射 = newValue.node}this.$emit(输入",发出);},价值(新价值){如果(this.user && this.user.node != newValue){if (newValue == null) {this.user = null}别的 {this.user = { 节点:newValue };}}},搜索(新值){this.debouncedSearch(newValue)},},方法: {endIntersect(条目,观察者,isIntersecting){if (isIntersecting && this.users && this.users.pageInfo.hasNextPage) {让光标 = this.users.pageInfo.endCursorthis.$apollo.queries.users.fetchMore({变量:{游标:游标},updateQuery: (previousResult, { fetchMoreResult }) =>{让边 = [...previousResult.users.edges,...fetchMoreResult.users.edges,]让 pageInfo = fetchMoreResult.users.pageInfo;返回 {用户:{边缘:边缘,页面信息:页面信息,__typename:previousResult.users.__typename,}}}})}},去抖动搜索:去抖动(功能(搜索){如果(this.users){this.$apollo.queries.users.refetch({用户名:搜索,numberToShow:RESULTS_TO_SHOW,游标:空,});}}, 500),过滤器(项目,查询文本){返回 item.node.userProfile.username.toLocaleLowerCase().indexOf(queryText.toLocaleLowerCase()) >-1}},计算:{计算用户(){如果(this.users){返回 this.users.edges}返回 []},跳过() {如果(this.search){返回 this.search.length >1}返回假}}};
I have a page with Vuetify Autocomplete component, and REST API backend with '/vendors' method. This method takes limit, page and name parameters and returns JSON with id and name fields.
I made some code with lazy list load on user input event. But now I want to add the ability to load this list on user scroll event.
For example, by default there is a list of 100 vendors. User scrolled this list until the end, then "some event" is called and loads next 100 of vendors. Then user keeps scrolling and the action is repeated.
Is it possible to made this with Vuetify Autocomplete component, or should i use another library?
Example code of current component is shown below:
<template>
<v-autocomplete
:items="vendors"
v-model="selectedVendorId"
item-text="name"
item-value="id"
label="Select a vendor"
@input.native="getVendorsFromApi"
></v-autocomplete>
</template>
<script>
export default {
data () {
return {
page: 0,
limit: 100,
selectedVendorId: null,
vendors: [],
loading: true
}
},
created: function (){
this.getVendorsFromApi();
},
methods: {
getVendorsFromApi (event) {
return new Promise(() => {
this.$axios.get(this.$backendLink
+ '/vendors?limit=' + this.limit
+ '&page=' + this.page
+ '&name=' + (event ? event.target.value : ''))
.then(response => {
this.vendors = response.data;
})
})
}
}
}
</script>
I was able to get auto-loading going with the Vuetify AutoComplete component. It's a bit of a hack, but basically the solution is to use the v-slot
append item, the v-intersect
directive to detect if that appended item is visible, and if it is, call your API to fetch more items and append it to your current list.
<v-autocomplete
:items="vendors"
v-model="selectedVendorId"
item-text="name"
item-value="id"
label="Select a vendor"
@input.native="getVendorsFromApi"
>
<template v-slot:append-item>
<div v-intersect="endIntersect" />
</template>
</v-autocomplete>
...
export default {
methods: {
endIntersect(entries, observer, isIntersecting) {
if (isIntersecting) {
let moreVendors = loadMoreFromApi()
this.vendors = [ ...this.vendors, ...moreVendors]
}
}
}
}
In my use case, I was using API Platform as a backend, using GraphQL pagination using a cursor.
<component
v-bind:is="canAdd ? 'v-combobox' : 'v-autocomplete'"
v-model="user"
:items="computedUsers"
:search-input.sync="search"
item-text="item.node.userProfile.username"
hide-details
rounded
solo
:filter="
(item, queryText, itemText) => {
return item.node.userProfile.username.toLocaleLowerCase().indexOf(queryText.toLocaleLowerCase()) > -1
} "
:loading="loading"
item-value="username"
class="text-left pl-1"
color="blue-grey lighten-2"
:label="label"
>
<template v-slot:selection="{ item }">
<v-chip v-if="typeof item == 'object'">
<v-avatar left>
<v-img v-if="item.node.userProfile.image" :src="item.node.userProfile.image" />
<v-icon v-else>mdi-account-circle</v-icon>
</v-avatar>
{{ item.node.userProfile.firstName }} {{ item.node.userProfile.lastName }}
</v-chip>
<v-chip v-else-if="typeof item == 'string'">
{{ item }}
</v-chip>
</template>
<template v-slot:item="{ item: { node } }">
<v-list-item-avatar >
<img v-if="node.userProfile.avatar" :src="node.userProfile.avatar" />
<v-icon v-else>mdi-account-circle</v-icon>
</v-list-item-avatar>
<v-list-item-content class="text-left">
<v-list-item-title>
{{ $t('fullName', { firstName: node.userProfile.firstName, lastName: node.userProfile.lastName } )}}
</v-list-item-title>
<v-list-item-subtitle v-html="node.userProfile.username"></v-list-item-subtitle>
</v-list-item-content>
</template>
<template v-slot:append-item="">
<div v-intersect="endIntersect" >
</div>
</template>
</component>
import { VCombobox, VAutocomplete } from "vuetify/lib";
import debounce from "@/helpers/debounce"
import { SEARCH_USER_BY_USERNAME } from "@/graphql/UserQueries";
const RESULTS_TO_SHOW = 5
export default {
props: {
canAdd: {
type: Boolean,
default: false,
},
value: [Object, String],
label: String,
},
components: { VCombobox, VAutocomplete },
apollo: {
users: {
query: SEARCH_USER_BY_USERNAME,
variables() {
return {
username: this.search,
numberToShow: RESULTS_TO_SHOW,
cursor: null,
}
},
watchLoading(isLoading) {
this.loading = isLoading
},
skip() {
if (this.search) {
return !(this.search.length > 1)
}
return true
},
},
},
data() {
return {
user: this.value,
search: "",
cursor: null,
loading: false,
};
},
watch: {
user(newValue) {
let emit = newValue
if (newValue) {
emit = newValue.node
}
this.$emit("input", emit);
},
value(newValue) {
if (this.user && this.user.node != newValue) {
if (newValue == null) {
this.user = null
}
else {
this.user = { node: newValue };
}
}
},
search(newValue) {
this.debouncedSearch(newValue)
},
},
methods: {
endIntersect(entries, observer, isIntersecting) {
if (isIntersecting && this.users && this.users.pageInfo.hasNextPage) {
let cursor = this.users.pageInfo.endCursor
this.$apollo.queries.users.fetchMore({
variables: { cursor: cursor},
updateQuery: (previousResult, { fetchMoreResult }) => {
let edges = [
...previousResult.users.edges,
...fetchMoreResult.users.edges,
]
let pageInfo = fetchMoreResult.users.pageInfo;
return {
users: {
edges: edges,
pageInfo: pageInfo,
__typename: previousResult.users.__typename,
}
}
}
})
}
},
debouncedSearch: debounce(function (search) {
if (this.users) {
this.$apollo.queries.users.refetch({
username: search,
numberToShow: RESULTS_TO_SHOW,
cursor: null,
});
}
}, 500),
filter(item, queryText) {
return item.node.userProfile.username.toLocaleLowerCase().indexOf(queryText.toLocaleLowerCase()) > -1
}
},
computed: {
computedUsers() {
if (this.users){
return this.users.edges
}
return []
},
skip() {
if (this.search) {
return this.search.length > 1
}
return false
}
}
};
</script>
这篇关于如何在 Vuetify Autocomplete 组件中创建无限滚动?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!