How to create a custom selected list using Multiselect for Vue.js

Vue-Multiselect example
What we will be achieving

Getting started

First of all, I will assume you know your way around Vue.js and that you already have your environment set up and running with Vue.js.

  • Vue.js
  • Vue-Multiselect
  • Vue-Resource
  • Font Awesome
npm install vue --save
npm install vue-multiselect --save
npm install vue-resource --save

Create the Vue component

First we are going to set up Vue and include the Multiselect component.

new Vue({
components: {
Multiselect: window.VueMultiselect.default
},
data: {
options: [],
optionsProxy: []
}
}).$mount(‘#app’)
<div id="app">
<multiselect :options="options" :value="optionsProxy" placeholder="Search"></multiselect>
</div>
<multiselect :options="options" :value="optionsProxy"></multiselect>
  • options — This contains the array of items to display when the users click the select field
  • value — This will contain the selected items in an array
<multiselect :options="options" :value="optionsProxy"></multiselect>-----data: {
options: [],
optionsProxy: []
}
  • options — This must contain the array of items to display when users click the select field
  • optionsProxy — This will be filled in with the items selected by the user

Create the custom list used to display the selected resources

One of the most valuable things of Multiselect is that the list of items can be an array of objects, and those objects can contain several keys that you can use to display data after the user selected one.

<ul class="resources-list">
<template v-for="(resource, index) in selectedResources">
<li class="resource-item" :data-index="index">
<div class="resource-info">
<div class="resource-title" :id="index">
<span>{{ resource.name }}</span>
<span class="version">{{ resource.version }}</span>
</div>
<div class="resource-description">
<span>{{ resource.description }}</span>
</div>
<div class="resource-url">
<a :href="resource.latest" target="_blank">{{ resource.latest }}</a>
</div>
</div>
<div class="delete-controls" v-on:click.prevent="removeDependency(index)">
<i class="fa fa-times fa-fw"></i>
</div>
</li>
</template>
</ul>
  • resource-name — This prints out the name of the resource
  • resource-description — This prints out the description of the resource
  • resource-version — This prints out the version of the resource
  • resource-latest — This prints out the URL of the resource
  • removeDependency — Run function to remove resource from list
.resources-list {
margin-top: 15px;
padding: 0;
list-style: none;
}
.resources-list li {
display: flex;
align-items: center;
min-height: 50px;
padding: 10px 40px 10px 0;
border-bottom: 1px solid rgba(51, 51, 51, 0.1);
position: relative;
}
.resources-list li:last-child {
border-bottom: none;
}
.resources-list li .resource-title {
font-size: 1em;
color: #333;
}
.resources-list li .version {
opacity: 0.7;
margin-left: 5px;
font-size: 75%;
}
.resources-list li .resource-description,
.resources-list li .resource-url {
font-size: 0.8em;
color: #999;
margin-top: 5px;
}
.resources-list li .resource-url {
margin-top: 0;
}
.resources-list li .resource-description:empty {
display: none;
}
.resources-list li .delete-controls {
position: absolute;
right: 0;
width: 40px;
text-align: center;
color: #999;
cursor: pointer;
}
.resources-list li .delete-controls:hover,
.resources-list li .delete-controls:focus {
color: red;
}

Update Multiselect with important props

We need to add a few more props to Multiselect to achieve what we want, so let give that a go:

<multiselect :options="options" :value="optionsProxy" @input="updateSelected" @search-change="searchQuery" :multiple="true" :searchable="true" :close-on-select="true" placeholder="Search" :custom-label="customLabel" :loading="showLoadingSpinner"></multiselect>
  • @input— Runs a function, this is triggered when the user selects an item from the list
  • @search-change — Runs a function, this is triggered when a user starts a search — This only works when searchable is active
  • searchable — Accepts a Boolean, and makes the select field list searchable
  • multiple — Accepts a Boolean, and allows users to select multiple items from the list
  • close-on-select — Accepts a Boolean, and forces the list to close when the user selects an item — Use this is you want.
  • custom-label — Templated string that will be rendered as an item in the list
  • loading — Accepts a Boolean, and if true shows a loading spinner

Update Vue component

Data

data: {
options: [],
optionsProxy: [],
selectedResources: [],
showLoadingSpinner: false
}
  • selectedResources — This will be the array of selected items from the list
  • showLoadingSpinner — This will trigger the loading spinner in the Multiselect
methods: {
customLabel(option) {
return `${option.name} — ${option.version}`
},
updateSelected(value) {
value.forEach((resource) => {
// Adds selected resources to array
this.selectedResources.push(resource)
})
// Clears selected array
// This prevents the tags from being displayed
this.optionsProxy = []
},
cdnRequest(value) {
this.$http.get(`https://api.cdnjs.com/libraries?search=${value}&fields=version,description`)
.then((response) => {
// get body data
this.options = []
response.body.results.forEach((object) => {
this.options.push(object)
})
this.showLoadingSpinner = false
}, (response) => {
// error callback
})
},
searchQuery(value) {
this.showLoadingSpinner = true
// GET
this.cdnRequest(value)
},
removeDependency(index) {
this.selectedResources.splice(index, 1)
}
}
customLabel(option) {
return `${option.name} — ${option.version}`
}
updateSelected(value) {
value.forEach((resource) => {
// Adds selected resources to array
this.selectedResources.push(resource)
})
// Clears selected array
// This prevents the tags from being displayed
this.optionsProxy = []
}
this.optionsProxy = []
cdnRequest(value) {
this.$http.get(`https://api.cdnjs.com/libraries?search=${value}&fields=version,description`)
.then((response) => {
// get body data
this.options = []
response.body.results.forEach((object) => {
this.options.push(object)
})
this.showLoadingSpinner = false
}, (response) => {
// error callback
})
}
response.body.results.forEach((object) => {
this.options.push(object)
})
searchQuery(value) {
this.showLoadingSpinner = true
// GET
this.cdnRequest(value)
}
removeDependency(index) {
this.selectedResources.splice(index, 1)
}
<div class="delete-controls" v-on:click.prevent="removeDependency(index)">
<i class="fa fa-times fa-fw"></i>
</div>

Populate list on load

The last thing we need to do is to populate the list with resources when users load the page, for that we will use the created() lifecycle hook to query cdnjs.com with an empty string.

created() {
const value = ''
this.cdnRequest(value)
}

Conclusion

As you can see, with a bit of coding you can create amazing interfaces using this little Vue component.

--

--

Developer at Fliplet - Occasional writer - Video gamer - Netflix binge watcher - Curator at www.creativesorcerers.com

Love podcasts or audiobooks? Learn on the go with our new app.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Hugo Carneiro

Hugo Carneiro

350 Followers

Developer at Fliplet - Occasional writer - Video gamer - Netflix binge watcher - Curator at www.creativesorcerers.com