To make dropdowns easier, we can use the Vue Select plugin to add the dropdown.
It can do much more than the select element.
In this article, we’ll look at how to use the vue-select package to make more complex dropdowns.
Selectable Prop
We can set which options are selected able with the selectable
prop.
For example, we can write:
<template>
<div id="app">
<v-select
placeholder="Choose fruit"
:options="options"
:selectable="option => option !== 'grape'"
></v-select>
</div>
</template>
<script>
export default {
name: "App",
data() {
return {
options: ["apple", "orange", "grape"]
};
}
};
</script>
We set the selectable
prop to a function that returns the condition for the items that we want to disable.
Therefore, we disable the 'grape'
choice in the dropdown.
The placeholder
has the placeholder for the dropdown.
Limiting the Number of Selections
We can limit the number of selections with the selectable
prop.
For example, we can write:
<template>
<div id="app">
<v-select
placeholder="Choose fruit"
:options="options"
multiple
v-model="selected"
:selectable="() => selected.length < 3"
></v-select>
</div>
</template>
<script>
export default {
name: "App",
data() {
return {
selected: [],
options: ["apple", "orange", "grape", "banana", "pear"]
};
}
};
</script>
The selectable
prop is set to a function that checks the selected
array’s length.
If we have more than 3, then the choices will be disabled.
Pagination
We can paginate the choices in the dropdown.
For example, we can write:
<template>
<v-select :options="paginated" @search="query => search = query" :filterable="false">
<li slot="list-footer" class="pagination">
<button @click="offset -= 10" :disabled="!hasPrevPage">Prev</button>
<button @click="offset += 10" :disabled="!hasNextPage">Next</button>
</li>
</v-select>
</template>
<script>
import countries from "./countries";
export default {
data: () => ({
countries,
search: "",
offset: 0,
limit: 10
}),
computed: {
filtered() {
return this.countries.filter(country => country.includes(this.search));
},
paginated() {
return this.filtered.slice(this.offset, this.limit + this.offset);
},
hasNextPage() {
const nextOffset = this.offset + 10;
return Boolean(
this.filtered.slice(nextOffset, this.limit + nextOffset).length
);
},
hasPrevPage() {
const prevOffset = this.offset - 10;
return Boolean(
this.filtered.slice(prevOffset, this.limit + prevOffset).length
);
}
}
};
</script>
The countries
array is from the countryList
in https://gist.github.com/incredimike/1469814.
We populate the list-footer
slot with buttons to let us move through the pages.
Then we create the paginated
computed property to get the items on a given page.
Also, we have the hasNextPage
method to check if there’s the next page by seeing if there’s anything returned by slice
.
And we use a similar logic with the hasPrevPage
computed property.
The search
event is emitted when we enter something in the search box.
Infinite Scrolling
We can also add infinite scrolling to our v-select
dropdown.
For example, we can write:
<template>
<v-select
:options="paginated"
:filterable="false"
@open="onOpen"
@close="onClose"
@search="query => search = query"
>
<template #list-footer>
<li ref="load" class="loader" v-show="hasNextPage">Loading more options...</li>
</template>
</v-select>
</template>
<script>
import countries from "./countries";
export default {
data: () => ({
observer: null,
limit: 10,
search: ""
}),
mounted() {
this.observer = new IntersectionObserver(this.infiniteScroll);
},
computed: {
filtered() {
return countries.filter(country => country.includes(this.search));
},
paginated() {
return this.filtered.slice(0, this.limit);
},
hasNextPage() {
return this.paginated.length < this.filtered.length;
}
},
methods: {
async onOpen() {
if (this.hasNextPage) {
await this.$nextTick();
this.observer.observe(this.$refs.load);
}
},
onClose() {
this.observer.disconnect();
},
async infiniteScroll([{ isIntersecting, target }]) {
if (isIntersecting) {
const ul = target.offsetParent;
const scrollTop = target.offsetParent.scrollTop;
this.limit += 10;
await this.$nextTick();
ul.scrollTop = scrollTop;
}
}
}
};
</script>
We use the Intersection Observer API to check if we scrolled to the bottom of the list.
We start observing after the onOpen
method is called when we open the dropdown.
Then we stop observing when the onClose
method is called to stop observing.
The this.observer
property is created from the IntersectionObserver
constructor with the infiniteScroll
method.
We can get whether we scrolled to the bottom of the dropdown list with the isIntersecting
property.
Conclusion
We can add pagination and infinite scrolling with the dropdown with the Vue Select dropdown.
Also, we can disable and limit selections that we can choose.