Vuejs + Materializecss select field - vue.js

I have this code in my template:
<div class="input-field col s6">
<select v-on:change="selectChaned" v-model="item.size">
<option value="" disabled selected>Choose your option</option>
<option v-on:click="optionClicked" v-for="size in case_sizes" v-bind:value="{{ size }}">{{ size }}</option>
</select>
<label for="size">Size</label>
</div>
According to Materializecss docs, I call $('select').material_select(); to transform default select field into something cutie. What it also does - it replaces <select> and <option> tags with <ul> and <li>.
As a result I can't access value of item.size in my ViewModel js file. I even tried to listen for a click on option field and call optionClicked method (which should simply alert a message then), tried to listen for selectChaned. Nothing.
How can I get option value in ViewModel?
p.s. just for information: I only have problem with select field. Input field for example works fine:
<input placeholder="" name="name" type="text" class="validate" v-model="item.name">
In ViewModel I'm able to access item.name

It seems that Materialize doesn't dispatch any events so I couldn't find an elegant solution. But it does seem that the following Vuejs directive + jQuery workaround is working:
Vue.directive("select", {
"twoWay": true,
"bind": function () {
$(this.el).material_select();
var self = this;
$(this.el).on('change', function() {
self.set($(self.el).val());
});
},
update: function (newValue, oldValue) {
$(this.el).val(newValue);
},
"unbind": function () {
$(this.el).material_select('destroy');
}
});
And then in your HTML – bind <select> using v-select instead of v-model.

Vue.js 2.0
Template:
<div v-text="selected"></div>
<material-select v-bind="selected = selected || options[0].value" v-model="selected">
<option v-for="option in options" :value="option.value" v-text="option.name"></option>
</material-select>
Component:
"use strict";
Vue.component("material-select", {
template: '<select><slot></slot></select>',
props: ['value'],
watch: {
value: function (value) {
this.relaod(value);
}
},
methods:{
relaod : function (value) {
var select = $(this.$el);
select.val(value || this.value);
select.material_select('destroy');
select.material_select();
}
},
mounted: function () {
var vm = this;
var select = $(this.$el);
select
.val(this.value)
.on('change', function () {
vm.$emit('input', this.value);
});
select.material_select();
},
updated: function () {
this.relaod();
},
destroyed: function () {
$(this.$el).material_select('destroy');
}
});

Vue.directive('material-select', {
bind:function(el,binding,vnode){
$(function () {
$(el).material_select();
});
var arg = binding.arg;
if(!arg)arg="change";
arg = "on"+arg;
el[arg]=function() {
if (binding.expression) {
if (binding.expression in vnode.context.$data) {
vnode.context.$data[binding.expression] = el.value;
} else if (vnode.context[binding.expression] &&
vnode.context[binding.expression].length <= 1) {
vnode.context[binding.expression](el.value);
} else {
throw new Error('Directive v-' + binding.name + " can not take more than 1 argument");
}
}
else {
throw new Error('Directive v-' + binding.name + " must take value");
}
}
},
unbind:function(el) {
$(el).material_select('destroy');
}
});
new Vue({
el: '#exemple1',
data:function(){
return {
selected: '',
options:[
{value:"v1",text:'description 1'},
{value:"v2",text:'description 2'},
{value:"v3",text:'description 3'},
{value:"v4",text:'description 4'},
{value:"v5",text:'description 5'},
]
}
}
});
new Vue({
el: '#exemple2',
data:function() {
return{
selected: null,
options:[
{value:"v1",text:'description 1'},
{value:"v2",text:'description 2'},
{value:"v3",text:'description 3'},
{value:"v4",text:'description 4'},
{value:"v5",text:'description 5'},
]
}
},
methods:{
change:function(value){
this.selected = value;
alert(value);
}
}
});
<link href="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.98.0/css/materialize.min.css" rel="stylesheet"/>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.10/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.98.0/js/materialize.min.js"></script>
<h4>vue js materialize</h4>
<h5>Exemple1</h5>
<div id="exemple1">
<select v-material-select:change="selected" class="blue-text">
<option value="" disabled selected ><slot>Defaut message</slot></option>
<option v-for="option in options" :value="option.value">{{ option.text}}</option>
</select>
</div>
<h5>Exemple2</h5>
<div id="exemple2">
<select v-material-select:change="change" class="blue-text">
<option disabled selected ><slot>Choisir Votre Abonnement</slot></option>
<option v-for="option in options" :value="option.value">{{ option.text}}</option>
</select>
</div>

The top answer was nice but didn't work for Vue 2.
Here is an update of which works (probably still a little hacky). I moved the jQuery hook into update() as the bind function called too early for materialize.
Vue.directive("select", {
"twoWay": true,
update: function(el, binding, vnode) {
if(!vnode.elm.dataset.vueSelectReady) {
$(el).on('change', function() {
vnode.context.$set(vnode.context, binding.expression, el.value);
});
$(el).material_select();
vnode.elm.dataset.vueSelectReady = true
}
},
unbind: function(el, binding, vnode) {
$(el).material_select('destroy');
}
});
HTML:
<select v-select=selected>
<option value="" disabled selected>Choose your option</option>
<option :value="item" v-for='item in items'>{{ item }}</option>
<label>Materialize Select</label>
</select>

You can make the dynamic select in Vue + Materializecss work with simple hacks
$('#select').val(1).material_select(); // Set value and reinitialize materializecss select
mounted () {
$("#select").change(function(){
this.update_result.category = $("#select").val();
}.bind(this)); // To set the user selected value to the data property
update_result.
}
If you are using meterializecss beta version the function name to initialize the select will differ.

I had a similar problem. The catch here is, you need to issue $('select').material_select(); only after the DOM of your Vue app is ready. So you can add a ready method to your Vue app and include $('select').material_select(); inside your ready method.
var vm = new Vue({
data: function() {
locations: ["Clayton", "Mt Meigs", "Birmingham", "Helena", "Albertville", "Albertville", "Grant"]
},
ready: function() {
$('select').material_select();
}});
Just make sure you include Jquery first, then materialize.js followed by Vue.js in your html file.

I want to include a working fiddle of custom select2 directive which I built for my project. It also supports multiple selects:
fiddle
data: function() {
return {
names: [
{id: 1, value: 'Alice'},
{id: 1, value: 'Bob'},
{id: 1, value: 'Simona'}
],
myStudents: {
names: ['Alice', 'Bob'],
}
}
},
directives: {
'select': {
twoWay: true,
params: ['options'],
bind: function () {
var self = this
$(this.el).select2().on('change', function() {
self.set($(self.el).val())
})
},
update: function (value) {
$(this.el).val(value).trigger('change')
},
},
},
<select multiple v-select="myStudents.names" name="names" v-model="myStudents.names">
<option v-for="name in names" value="{{ name.value }}">{{ name.value }}</option>
</select>

v- VueJs2.4
None of the above answers were for multiple select element. I got it working by traversing the select element options. This is not a correct approach and kind of hack but works.
Plunker
<h4>vue js materialize select</h4>
<div class="row" id="app" style="padding-bottom:2em;">
<div class="input-field col s12 m8">
<select multiple v-material-select:change="selected">
<option value="AngularJs">AngularJs</option>
<option value="Bootstrap3">Bootstrap3</option>
<option value="Bootstrap4">Bootstrap4</option>
<option value="SCSS">SCSS</option>
<option value="Ionic">Ionic</option>
<option value="Angular2">Angular2</option>
<option value="Angular4">Angular4</option>
<option value="React">React</option>
<option value="React Native">React Native</option>
<option value="Html5">Html5</option>
<option value="CSS3">CSS3</option>
<option value="UI/UX">UI/UX</option>
</select>
<label>Technologies Used</label>
</div>
<h2>Your selected options</h2>
<p>{{$data.selected}}</p>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.100.2/js/materialize.min.js"></script>
<script src="https://unpkg.com/vue#2.4.4/dist/vue.js"></script>
<script> Vue.directive("material-select", {
bind: function(el, binding, vnode) {
$(function() {
$(el).material_select();
});
var arg = binding.arg;
if (!arg) arg = "change";
arg = "on" + arg;
el[arg] = function() {
vnode.context.$data.selected = [];
for (let i = 0; i < 12; i++) {
if (el[i].selected === true) {
vnode.context.$data.selected.push(el[i].value);
}
}
};
},
unbind: function(el) {
$(el).material_select("destroy");
}
});
var app = new Vue({el: "#app",data: { selected: []},
ready: function() {
$("select").material_select(); }});</script>

The possible solution that I found is to use an input, and attach it to a dropdown content. It works well with vue even when you are dynamically creating dropdown. And its reactive, that you don't have to emit any other event to bind values.
Codepen: https://codepen.io/aaha/project/editor/DGJNLE
<style>
input{
cursor: pointer;
}
.caret{
float:right;
position: relative;
cursor: pointer;
top:-50px;
}
ul{
width: 100%;
}
</style>
<script>
Vue.component('paper-dropdown', {
template: '<div> \
<div class="input-field">\
<input type="text" class="dropdown-button" v-bind:data-activates="_id"\
v-bind:value="value"> \
<label>{{label}}</label> \
</div> \
<i class="material-icons caret">arrow_drop_down</i>\
<ul v-bind:id="_id" class="dropdown-content"> \
<li v-for="item in options" v-on:click="setselected"><a v-bind:value="item">{{item}}</a></li> \
</ul>\
</div>',
watch: {
value: function(){
Materialize.updateTextFields();
}
},
computed:{
_id: function(){
if(this.id != null) return this.id;
return Math.random().toString(36).substr(2);
}
},
props: {
label:{
type: [String, Number],
default: ''
},
options:{
type: Array,
default: []
},
placeholder:{
type: String,
default: 'Choose your option'
},
value:{
type: String,
default: ''
},
id:{
type: String,
default: 'me'
}
},
methods:{
setselected: function(e){
this.$emit('input', e.target.getAttribute("value"));
}
},
mounted: function(){
$('.dropdown-button').dropdown({
inDuration: 300,
outDuration: 225,
constrainWidth: false, // Does not change width of dropdown to that of the activator
hover: false, // Activate on hover
gutter: 0, // Spacing from edge
belowOrigin: false, // Displays dropdown below the button
alignment: 'left', // Displays dropdown with edge aligned to the left of button
stopPropagation: false // Stops event propagation
}
);
}
});
</script>

I did something much more simple, only on mounted:
....
mounted() {
$(this.$el)
.find(".mdb-select")
.material_select();
const self = this;
$(this.$el).on("change", function(e) {
self.$emit('input', this.inputValue);
});
},
.....

Related

vuejs checkbox need to change method

I have a dropdown menu that triggers a method to get data based on what is selected in the dropdown. I would like to add a condition that changes the #change method if a checkbox is selected. The method would switch based on which checkbox is selected. I guess a v-if that runs a check somewhere--Im not sure.
new Vue({
el: "#app",
data: {
selection: 'Select a option',
todos: [{
name: 'apple'
}, {
name: 'oranges'
}, {
name: 'carrots'
}]
},
methods:{
changeP (event) {
this.getSetA();
// alert("this is set a");
},
changeP1 (event) {
this.getSetB();
alert("this is set b");
},
changeP2 (event) {
this.getSetC();
alert("this is set c");
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<h2>Fruit:</h2>
<select v-model="selection" #change="changeP($event)">
<option value="Select a option" disabled>Choose an fruit:</option>
<option v-for="(t, i) in todos" :key="i" :value="t.name">{{ t.name }}</option>
</select>
<br>
<input type="checkbox" id="vehicle1" name="vehicle1" value="apple"> Apples
<input type="checkbox" id="vehicle2" name="vehicle2" value=""> Oranges
<input type="checkbox" id="vehicle3" name="vehicle3" value="">pears
</div>
new Vue({
el: '#app',
data: {
selection: 'Select an option',
selectOptions: [ { name: 'apple' }, { name: 'oranges' }, { name: 'carrots' }],
checkboxOptions: [ 'vehicle1', 'vehicle2', 'vehicle3' ],
checkedItems: [],
message: '',
},
methods: {
onDropdownChange() {
this.message = '';
if (this.checkedItems.length) {
this.message = `You've selected: `;
for(let i = 0; i < this.checkedItems.length; i++) {
this.message += this.checkedItems[i] + ' ';
}
}
switch(this.selection) {
case 'apple':
return this.onAppleSelected();
case 'oranges':
return this.onOrangesSelected();
default:
return this.onCarrotsSelected();
}
},
onAppleSelected() {
if (this.message.length) {
this.message += 'and apple.';
return;
}
this.message = `You've selected apple`;
},
onCarrotsSelected() {
this.message += ' CARROT!'
},
onOrangesSelected() {
this.message += ' No Scurvy';
}
},
template: `
<div id="app">
<h2>Fruit:</h2>
<select v-model="selection" #change="onDropdownChange">
<option value="Select an option">Choose</option>
<option v-for="(o, i) in selectOptions" :key="o.name" :value="o.name">{{o.name}}</option>
</select>
<div style="display:block" v-for="(o, i) in checkboxOptions" :key="o">
<label :for="o">{{o}}</label>
<input type="checkbox" :id="o" :name="o" :value="o" v-model="checkedItems" />
</div>
<p>{{message}}</p>
</div>
`
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
</div>
You should probably have a single method that decides which method to call based on the state of the component.

How can I get a specifc selection in select vue.js?

How are you?
I'm studying Vue and I'm stuck on the current task not knowing where to go.
I have a select that when I click I need to show on screen only what corresponds to that selection. For example, when placing the "to do" option in the select, only the tasks with a concluded=false should appear on the screen. I've only gotten this far and I need help to continue. Can you help me? Thanks
This is my App.vue
<template>
<div id="app">
<h1>Lista de Tarefas</h1>
<List :data="list" #remove="handleRemove"/>
<Form #add="addNewTask" #onChange="handleN"/>
</div>
</template>
<script>
import List from "./components/List.vue";
import Form from "./components/Form.vue";
export default {
components: {
List,
Form,
},
data() {
return {
list: [],
};
},
methods: {
addNewTask(newTask) {
this.list.push(newTask);
},
handleRemove(item) {
const index = this.list.findIndex(i => i.id === item.id)
this.list[index].excluded = true
},
handleN(item) {
const index = this.list.findIndex(i => i.id === item.id)
this.list[index].concluded = true
}
},
};
</script>
This is my List.vue
<template>
<ul>
<select v-model="selected" #change="onChange($event)">
<option disabled value="">Escolha a visualização</option>
<option v-for="option in options" :key="option.text">
{{ option.text }}
</option>
</select>
<li v-for="item in itens" :key="item.id">
<input type="checkbox" id="checkbox" v-model="item.concluded" />
<label for="checkbox"> {{ item.description }} </label>
<button #click="() => $emit('remove', item)">Excluir</button>
</li>
</ul>
</template>
<script>
export default {
props: {
data: {
type: Array,
default: () => {},
},
},
data() {
return {
selected: "",
options: [
{ text: "Todos", value: "1" },
{ text: "A fazer", value: "2" },
{ text: "Concluído", value: "3" },
{ text: "Deletado", value: "4" },
],
};
},
computed: {
itens() {
return this.data.filter((item) => item.excluded === false);
},
},
methods: {
onChange(event) {
console.log(event.target.value);
return this.data.filter((item) => item.concluded === false);
},
},
};
</script>
This is my Form.vue
<template>
<form #submit.prevent="handleNewTask">
<input type="text" v-model="newTask" placeholder="Insira a tarefa"/>
<input type="submit" value="Adicionar"/>
</form>
</template>
<script>
import Task from '../types/Task.js'
export default {
data() {
return {
newTask: "",
};
},
methods: {
handleNewTask() {
this.$emit('add', new Task(this.newTask))
this.newTask = ''
}
},
};
</script>
And this is my Task.js
export default class {
constructor(description) {
this.description = description,
this.id = Math.random(),
this.concluded = false,
this.excluded = false
}
}
I watch some tutorials, read the documentation and some StackOverflow questions but I really can't get out of here
Thanks in advance for the help
Based on how you have structured your app, our only concern should be with the List.vue file.
Your goal is to filter the results based on the selection (selected property). However, your issue is that you are not even using that anywhere.
I know you are hard coding the filter on the onChange method but that is, first of all wrong because you aren't really changing anything (you are returning an array), and secondly it's inefficient.
A better way to do it is to update the computed itens function like so:
itens() {
return this.data.filter((item) => {
if (this.selected === '1'){
return item.concluded === false
} else if (this.selected === '2'){
// filter another way
} else if (... // so on and so forth
});
},
Also, I would filter out the excluded items before sending them to the component. If you aren't going to use it, don't send it.
Remove the onChange event on the <select> and the associated method since they are now unused.

How to make a validation and show message if default value is selected in dropdown in vue

I have a dropdown that I want to give a validation.
So here is my dropdown component:
<template>
<div class="custom-select" :tabindex="tabindex" #blur="open = false">
<div class="selected" :class="{ open: open }" #click="open = !open">
{{ selected.name }}
</div>
<div class="items" :class="{ selectHide: !open }">
<div v-if="defaultValue != ''">{{defaultValue}}</div>
<div v-for="(option, i) of options" :key="i" #click=" selected = option; open = false; $emit('input', option);">
{{ option.name }}
</div>
</div>
</div>
</template>
<script>
export default {
props: {
options: {
type: Array,
required: true,
},
defaultValue: {
type: String,
required: false,
default: "Choose an option",
},
tabindex: {
type: Number,
required: false,
default: 0,
},
},
data() {
return {
open: false,
selected: this.setDefaultValue(),
};
},
mounted() {
this.$emit("input", this.selected);
},
methods: {
setDefaultValue () {
if (this.defaultValue == '' && this.options.length > 0) {
return this.options[0];
}
return { name: this.defaultValue};
}
}
};
</script>
and in my parent component, I am using this dropdown, the fetched value from API call and filled with variations. So what I am trying to do is if the value is not selected (default: "Choose an option"), I want to give an error message which is saying, the dropdown is mandatory.
<Dropdown
:options="getVariations"
class="select"
#input="getSelected"
/>
<script>
import Dropdown from "../components/Dropdown";
import apiHelper from "../helpers/apiHelper";
export default {
components: {Dropdown},
data() {
return {
selected: "",
variationId: "",
selectedObject: null
};
},
computed: {
getVariations() {
return this.product.attributes.variations
}
},
methods: {
getSelected(opt) {
this.selectedObject = opt;
this.selected = opt.description;
this.variationId = opt.id;
}
},
};
</script>
This is a simple example of what you are asking for:
// v-model property
selectedVModel: null
// html
<select id="testselect" v-model="selectedVModel" required>
<option :value="null">Default</option>
<option value="bar">Option 1</option>
<option value="foo">Option 2</option>
<option value="baz">Option 3</option>
</select>
<div v-if="!selectedVModel">Error! Choose something!</div>
You can handle the error inside of your child component. In this example, if the v-model as selectedVModel is empty, the <div> with v-if="!selectedVModel" will be shown. As selectedVModel is null it will automatically select <option :value="null">Default</option>. If you chosse one of the options selectedVModel get´s a value so v-if="!selectedVModel" results into false, no error then. If you select Default again, the value turns to null again which results into true at v-if="!selectedVModel", so the error will be visible again.
Combine this simple example with your code and let me know if it helped you.
If you want to validate in your parent component, try to check selectedObject.id :
Vue.component('Dropdown', {
template: `
<div class="custom-select" :tabindex="tabindex" #blur="open = false">
<div class="selected" :class="{ open: open }" #click="open = !open">
{{ selected.name }}
</div>
<div class="items" :class="{ selectHide: !open }">
<div v-if="defaultValue != ''">{{defaultValue}}</div>
<div v-for="(option, i) of options" :key="i" #click=" selected = option; open = false; $emit('input', option);">
{{ option.name }}
</div>
</div>
</div>
`,
props: {
options: {
type: Array,
required: true,
},
defaultValue: {
type: String,
required: false,
default: "Choose an option",
},
tabindex: {
type: Number,
required: false,
default: 0,
},
},
data() {
return {
open: false,
selected: this.setDefaultValue(),
};
},
mounted() {
this.$emit("input", this.selected);
},
methods: {
setDefaultValue () {
if (this.defaultValue == '' && this.options.length > 0) {
return this.options[0];
}
return { name: this.defaultValue};
}
}
})
new Vue({
el: '#demo',
data() {
return {
selected: "",
variationId: "",
selectedObject: null,
product: {
attributes: {
variations: [{id: 1, name: 'name1', description: 'desc1'}, {id: 2, name: 'name2', description: 'desc2'},{id: 3, name: 'name3', description: 'desc3'},]
}
}
};
},
computed: {
getVariations() {
return this.product.attributes.variations
}
},
methods: {
getSelected(opt) {
this.selectedObject = opt;
this.selected = opt.description;
this.variationId = opt.id;
},
submit() {
if (!this.selectedObject.id) alert('pls select option')
else console.log(this.selectedObject)
}
},
})
Vue.config.productionTip = false
Vue.config.devtools = false
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="demo">
<button #click="submit">submit</button>
<Dropdown
:options="getVariations"
class="select"
#input="getSelected"
/>
</div>

#click bound to each item within v-for loop executed many times when clicking

I have a #click bound to individual items within a v-for loop. In the resulting render, I should have one #click for each item, so clicking one item should trigger the function bound to the item once.
Yet it triggers it as many times as there are items. Why?
<ul>
<li :key="option.value" v-for="option in options">
<QuizCheckbox
#click.native="handleClick(option.value)"
>
{{ option.value }}
</QuizCheckbox>
</li>
</ul>
...
methods: {
handleClick(val) {
console.log(val);
},
EDIT:
If I replaced ... with a simple element, then clicking that doesn't trigger the problem. So it's the <QuizCheckbox> component who's the culprit. However, nothing in it seems to indicate what could cause the problem. Here's the content of QuizCheckbox.vue:
<template>
<div :class="['quiz-checkbox', {'quiz-checkbox--checked': shouldBeChecked}]">
<div :class="['form-checkbox']">
<label class="form-checkbox__label">
<slot/>
<input
:checked="shouldBeChecked"
:value="value"
#change="updateInput"
class="form-checkbox__input"
type="checkbox"
/>
<span class="form-checkbox__checkmark"></span>
</label>
</div>
</div>
</template>
<script>
export default {
model: {
prop: 'modelValue',
event: 'change'
},
props: {
value: {
type: String,
},
modelValue: {
type: [Boolean, Array],
default: false
}
},
computed: {
shouldBeChecked() {
if (this.modelValue instanceof Array) {
return this.modelValue.includes(this.value);
}
return this.modelValue;
}
},
created() {
if (!this.$slots.default) {
console.error('QuizCheckbox: requires label to be provided in the slot');
}
},
methods: {
updateInput(event) {
const isChecked = event.target.checked;
if (this.modelValue instanceof Array) {
const newValue = [...this.modelValue];
if (isChecked) {
newValue.push(this.value);
} else {
newValue.splice(newValue.indexOf(this.value), 1);
}
this.$emit('change', newValue);
} else {
this.$emit('change', isChecked);
}
}
}
};
</script>
The code you post seems fine. Although simplified here is the code you post running:
new Vue({
el: 'ul',
data: {
options: [
{
value: 'option1',
},
{
value: 'option2',
}
]
},
methods: {
handleClick(val) {
console.log(val);
},
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<ul>
<li :key="option.value" v-for="option in options">
<div
#click="handleClick(option.value)"
>
{{ option.value }}
</div>
</li>
</ul>
The problem must be elsewhere.

vuejs2 and chosen select issue

Good day, pls have a look at this bin. It was written Vue 0.12 version and chosen js. How can i make it work with vue2 version. i really need this as a directive not as a component.
`<div id='search`-results'>
Vue model value <br>
{{city | json}}
<hr>
Select value:
<!-- note the `v-model` and argument for `v-chosen` -->
<select class="cs-select" v-model="city" options="cities" v-chosen="city"></select>
<select v-model="city" options="cities"></select>
Vue.directive('chosen', {
twoWay: true, // note the two-way binding
bind: function () {
$(this.el)
.chosen({
inherit_select_classes: true,
width: '30%',
disable_search_threshold: 999
})
.change(function(ev) {
this.set(this.el.value);
}.bind(this));
},
update: function(nv, ov) {
// note that we have to notify chosen about update
$(this.el).trigger("chosen:updated");
}
});
var vm = new Vue({
data: {
city: 'Toronto',
cities: [{text: 'Toronto', value: 'Toronto'},
{text: 'Orleans', value: 'Orleans'}]
}
}).$mount("#search-results");
Here it is implemented as a wrapper component that supports v-model and a slot for the options. This makes it a drop-in replacement for a standard select widget, at least as far as basic functionality. The updated(), happily, will notice changes to the options list as well as to the value.
Since two-way directives are not supported in Vue2, I do not believe there is a way to implement this as a directive. If you really need that, you will want to use Vue1.
var vm = new Vue({
el: '#search-results',
data: {
city: 'Toronto',
cities: [{
text: 'Toronto',
value: 'Toronto'
}, {
text: 'Orleans',
value: 'Orleans'
}]
},
components: {
'chosenSelect': {
template: '<select class="cs-select" v-model="proxyValue" ><slot></slot></select>',
props: ['value', 'options'],
computed: {
proxyValue: {
get() {
return this.value;
},
set(newValue) {
this.$emit('input', newValue);
}
}
},
mounted() {
$(this.$el)
.chosen({
inherit_select_classes: true,
width: '30%',
disable_search_threshold: 999
})
.change((ev) => {
this.proxyValue = ev.target.value;
});
},
updated() {
$(this.$el).trigger('chosen:updated');
}
}
}
});
setTimeout(() => { vm.cities.push({text: 'Houston', value: 'Worth it'}); }, 1000);
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/chosen/1.4.2/chosen.proto.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/chosen/1.4.2/chosen.jquery.min.js"></script>
<link href="//cdnjs.cloudflare.com/ajax/libs/chosen/1.4.2/chosen.css" rel="stylesheet" />
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.3.3/vue.min.js"></script>
<div id='search-results'>
Vue model value
<br> {{city | json}}
<hr> Select value:
<chosen-select v-model="city">
<option v-for="item in cities" :value="item.value">{{item.text}}</option>
</chosen-select>
<select v-model="city">
<option v-for="item in cities" :value="item.value">{{item.text}}</option>
</select>
</div>