When using Element UI's el-select, if there are too many option, loading all at once can cause page lag and white screen on dropdown click. We can implement dynamic loading with a custom directive.
First, define a global diretcive in main.js:
Vue.directive('infinite-scroll', {
bind(el, binding) {
const scrollContainer = el.querySelector('.el-select-dropdown .el-select-dropdown__wrap');
scrollContainer.addEventListener('scroll', function () {
const isBottom = this.scrollHeight - this.scrollTop <= this.clientHeight;
if (isBottom) {
binding.value();
}
});
}
});
Then, create a reusable component named RemoteSelect.vue:
<template>
<el-select
size="small"
multiple
v-bind="$attrs"
v-infinite-scroll="loadMore"
collapse-tags
filterable
:clearable="clearableFlag"
:placeholder="placeholder"
:filter-method="handleFilter"
v-model="value"
@change="onChange"
@visible-change="handleVisibility"
>
<el-option
v-for="item in items"
:key="item.number"
:value="item.number"
:label="item.name"
>
{{ item.name }}
</el-option>
</el-select>
</template>
<script>
import _ from 'lodash';
import { api } from '@/apis';
export default {
model: {
prop: 'inputValue',
event: 'change',
},
props: {
clearableFlag: {
type: Boolean,
default: false,
},
inputValue: {
type: [Array, String],
default: () => [],
},
placeholder: {
type: String,
default: '请选择',
},
size: {
type: String,
default: 'medium',
},
},
watch: {
inputValue(newVal) {
this.value = newVal;
},
},
data() {
return {
value: '',
items: [],
total: 0,
searchParams: {
name: '',
currentPage: 1,
pageSize: 20,
},
isFirstLoad: true,
};
},
methods: {
loadMore() {
const { pageSize, currentPage } = this.searchParams;
if (pageSize * currentPage < this.total) {
this.searchParams.currentPage++;
this.fetchItems();
}
},
fetchItems() {
// Example: replace with actual API call
// api.getList(this.searchParams).then((res) => {
// if (res.data.success) {
// this.items = [...this.items, ...res.data.data.list];
// this.total = res.data.data.total;
// }
// });
},
handleFilter: _.debounce(function (val) {
this.searchParams.name = val;
this.searchParams.currentPage = 1;
this.items = [];
this.total = 0;
this.fetchItems();
}, 500),
handleVisibility(visible) {
if (!visible) {
if (this.searchParams.name !== '') {
this.handleFilter('');
}
} else if (this.isFirstLoad) {
this.isFirstLoad = false;
this.fetchItems();
}
},
onChange(val) {
this.$emit('change', val);
},
},
};
</script>
Finally, use the component in your page:
<template>
<RemoteSelect
placeholder="请输入"
v-model="keyword"
@change="handleSearch"
/>
</template>
<script>
import RemoteSelect from '@/components/RemoteSelect.vue';
export default {
components: {
RemoteSelect,
},
data() {
return {
keyword: '',
};
},
methods: {
handleSearch() {
// search logic
},
},
};
</script>
Try it in your project!