vue-taggable-select
A Vue component that makes long, unwieldy select boxes user friendly.
Check out a demo here
What it Does
vue-taggable-select provides an elegant, user-friendly component to replace long, unwieldy multi select elements. Great for users. Simple for developers.
How simple?
This simple:
This simple
<vue-taggable-select
:taggable="true"
v-model="fruit"
:options="['apple','cherry','banana','pear', 'tomato']"
></vue-taggable-select>
What It Does Not Do
Nope no regular selects. See vue-single-select for this.
Vue Single Select
No ajax loading.
Usage
Install and Use Via CDN
<div id="app">
<lable>Choose a fruit!</lable>
<vue-taggable-select
v-model="fruit"
:options="fruits"
></vue-taggable-select>
</div>
<script src="https://unpkg.com/vue@latest"></script>
<script src="https://unpkg.com/vue-taggable-select@latest"></script>
<script>
new Vue({
el:"#app",
data: {
fruit: null,
fruits: ['peach','pear','apple','orange']
}
});
</script>
Install Via NPM
$ npm i vue-taggable-select
Register it
In your component:
import VueTaggableSelect from "vue-taggable-select";
export default {
components: {
VueTaggableSelect
},
}
Globally:
import VueTaggableSelect from "vue-taggable-select";
Vue.component('vue-taggable-select', VueTaggableSelect);
Use It
<vue-taggable-select
make-it-taggable="good!"
:taggable="true"
v-model="fruit"
:options="['apple','banana','cherry','tomato']"
:required="true"
></vue-taggable-select>
Use It Again
<vue-taggable-select
name="maybe"
placeholder="pick a post"
you-want-to-select-a-post="ok"
v-model="post"
out-of-all-these-posts="makes sense"
:options="posts"
a-post-has-an-id="good for search and display"
option-key="id"
the-post-has-a-title="make sure to show these"
option-label="title"
></vue-taggable-select>
Use It Again
<vue-taggable-select
you-want-to-select-a-reply="yes"
v-model="reply"
out-of-all-these-replies="yep"
:options="replies"
a-reply-only-has-a-reply="sounds about right"
option-label="reply"
seed-an-initial-value="what's seed mean?"
initial="seed me"
you-only-want-20-options-to-show="is 20 enough?"
:max-results="20"
></vue-taggable-select>
Dont like the Styling?
You can override some of it. Like so:
<vue-taggable-select
id="selected-reply"
name="a_reply"
option-label="reply"
v-model="reply"
:options="replies"
you-like-huge-dropdowns="1000px is long!"
max-height="1000px"
:classes='{
icons: "icons"
active: "active",
wrapper: "multi-select-wrapper",
searchWrapper: "search-wrapper",
searchInput: "search-input",
pill: "pill",
required: "required",
dropdown: "dropdown"
}'
></vue-taggable-select>
Then all you need to do is provide some class definitions like so:
.active {
background-color: pink;
}
.multi-select-wrapper {
display: block;
font-size: 16px;
}
.search-input {
color: black;
}
.pill {
padding: .5em;
}
... and so on.
Note: Bootstrap 3 Users May want to increase the size of the icons.
If so do this:
.icons svg {
height: 1em;
width: 1em;
}
Kitchen Sink
Meh, see props below.
Why vue-taggable-select is better
-
It handles custom label/value props for displaying options.
Other select components require you to conform to their format. Which often means data wrangling.
-
It's easier on the DOM.
Other components will load up all the options available in the select element. This can be heavy. vue-taggable-select makes an executive decision that you probably will not want to scroll more than N options before you want to narrow things down a bit. You can change this, but the default is 30.
-
Snappy Event Handling
- up and down arrows for selecting options
- enter to select first match
- remembers selection on change
- hit the escape key to, well, escape
- hit delete to remove the last selection
-
Lightweight
- Why are the other packages so big and actually have dependencies?
-
It works for regular 'POST backs' to the server.
If you are doing a regular post or just gathering the form data you don't need to do anything extra to provide a name and value for the selected option.
-
Mine just looks nicer
A lot nicer!
-
It's simple!!
Available Props
props: {
value: {
required: true
},
taggable: {
type: Boolean,
required: false,
default: () => false
},
classes: {
type: Object,
required: false,
default: () => {
return {
icons: 'icons',
active: 'active',
wrapper: "multi-select-wrapper",
searchWrapper: "search-wrapper",
searchInput: "search-input",
pill: "pill",
required: "required",
dropdown: "dropdown"
};
}
},
name: {
type: String,
required: false,
default: () => ""
},
options: {
type: Array,
required: false,
default: () => []
},
optionLabel: {
type: String,
required: false,
default: () => null
},
optionKey: {
type: String,
required: false,
default: () => null
},
placeholder: {
type: String,
required: false,
default: () => "Search Here"
},
maxHeight: {
type: String,
default: () => "220px",
required: false
},
inputId: {
type: String,
default: () => "multi-select",
required: false
},
initial: {
type: String,
required: false,
default: () => null
},
required: {
type: Boolean,
required: false,
default: () => false
},
maxResults: {
type: Number,
required: false,
default: () => 30
},
tabindex: {
type: String,
required: false,
default: () => {
return "";
}
},
keyboardDelete: {
type: Boolean,
required: false,
default: () => {
return true;
}
},
getOptionDescription: {
type: Function,
default(option) {
if (this.optionKey && this.optionLabel) {
return option[this.optionKey] + " " + option[this.optionLabel];
}
if (this.optionLabel) {
return option[this.optionLabel];
}
if (this.optionKey) {
return option[this.optionKey];
}
return option;
}
},
getOptionValue: {
type: Function,
default(option) {
if (this.optionKey) {
return option[this.optionKey];
}
if (this.optionLabel) {
return option[this.optionLabel];
}
return option;
}
}
},