I'm recently trying to reutilize my Vue ponents in some real-world application to remove unnecessary duplicates and clutter with <divs>
.
But I'm having trouble in doing so. After hours I managed to "acplish" it, but now the event fires twice and I don't know exactly why.
I've made a basic setup to show the problem:
Vueponent("bs-select",{
template:
`<div class="form-group">
<label class="control-label">{{ label }}</label>
<select2
ref="select2control"
:options="options"
:value="value"
@input="chosenOption"
></select2>
</div>`,
props: ["value", "label", "options"],
methods: {
chosenOption(val) {
this.$emit("input", val);
}
}
});
Vueponent("select2",{
template:
`<select :value="value" style="width: 100%">
<option value="" selected disabled>Choose...</option>
<option v-if="!options">{{ value }}</option>
<option v-for="option in options" :value="option.value">{{ option.text }}</option>
</select>`,
props: ["value", "options"],
mounted: function() {
const vm = this;
$(vm.$el)
.select2()
.on("change", function() {
console.log("CHANGE", vm.$el.value);
vm.$emit("input", vm.$el.value);
});
},
watch: {
value: function(val) {
$(this.$el)
.val(val)
.trigger("change");
}
}
});
new Vue({
el: "#app",
data: {
test: "bug",
options: [
{
value: "hello",
text: "Hello"
},
{
value: "bug",
text: "Bug"
}
]
}
})
* {
font-family: Arial;
font-size: 10px;
}
div {
}
<script src=".1.1/jquery.min.js"></script>
<script src=".0.5/js/select2.min.js"></script>
<link href=".0.5/css/select2.min.css" rel="stylesheet"/>
<script src=".5.17/vue.min.js"></script>
<div id="app">
<bs-select v-model="test" :options="options"></bs-select>
<br><br>
<button @click="test = 'bug'">
Set 'test' variable to 'bug' (Two-way check)
</button>
{{ test }}
</div>
<div>
Event is firing twice in console...
</div>
I'm recently trying to reutilize my Vue ponents in some real-world application to remove unnecessary duplicates and clutter with <divs>
.
But I'm having trouble in doing so. After hours I managed to "acplish" it, but now the event fires twice and I don't know exactly why.
I've made a basic setup to show the problem:
Vue.ponent("bs-select",{
template:
`<div class="form-group">
<label class="control-label">{{ label }}</label>
<select2
ref="select2control"
:options="options"
:value="value"
@input="chosenOption"
></select2>
</div>`,
props: ["value", "label", "options"],
methods: {
chosenOption(val) {
this.$emit("input", val);
}
}
});
Vue.ponent("select2",{
template:
`<select :value="value" style="width: 100%">
<option value="" selected disabled>Choose...</option>
<option v-if="!options">{{ value }}</option>
<option v-for="option in options" :value="option.value">{{ option.text }}</option>
</select>`,
props: ["value", "options"],
mounted: function() {
const vm = this;
$(vm.$el)
.select2()
.on("change", function() {
console.log("CHANGE", vm.$el.value);
vm.$emit("input", vm.$el.value);
});
},
watch: {
value: function(val) {
$(this.$el)
.val(val)
.trigger("change");
}
}
});
new Vue({
el: "#app",
data: {
test: "bug",
options: [
{
value: "hello",
text: "Hello"
},
{
value: "bug",
text: "Bug"
}
]
}
})
* {
font-family: Arial;
font-size: 10px;
}
div {
}
<script src="https://ajax.googleapis./ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare./ajax/libs/select2/4.0.5/js/select2.min.js"></script>
<link href="https://cdnjs.cloudflare./ajax/libs/select2/4.0.5/css/select2.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare./ajax/libs/vue/2.5.17/vue.min.js"></script>
<div id="app">
<bs-select v-model="test" :options="options"></bs-select>
<br><br>
<button @click="test = 'bug'">
Set 'test' variable to 'bug' (Two-way check)
</button>
{{ test }}
</div>
<div>
Event is firing twice in console...
</div>
I also Googled a lot and came to no conclusion on why this happens and/or how to fix this issue.
Any help is greatly appreciated.
Share Improve this question edited Nov 8, 2018 at 12:49 Fusseldieb asked Nov 8, 2018 at 11:46 FusseldiebFusseldieb 1,3743 gold badges20 silver badges47 bronze badges3 Answers
Reset to default 1After asking some of my friends, one figured it out that the "change" trigger must be in beforeUpdate
.
So, the solved code looks like this:
Vue.ponent("bs-select",{
template:
`<div class="form-group">
<label class="control-label">{{ label }}</label>
<select2
ref="select2control"
:options="options"
:value="value"
@input="chosenOption"
></select2>
</div>`,
props: ["value", "label", "options"],
methods: {
chosenOption(val) {
this.$emit("input", val);
}
}
});
Vue.ponent("select2",{
template:
`<select :value="value" style="width: 100%">
<option value="" selected disabled>Choose...</option>
<option v-if="!options">{{ value }}</option>
<option v-for="option in options" :value="option.value">{{ option.text }}</option>
</select>`,
props: ["value", "options"],
mounted: function() {
const vm = this;
$(vm.$el)
.select2()
.on("change", function() {
console.log("CHANGE", vm.$el.value);
vm.$emit("input", vm.$el.value);
});
},
beforeUpdate() {
$(this.$el).val(this.value).trigger('change.select2')
}
});
new Vue({
el: "#app",
data: {
test: "hello",
options: [
{
value: "hello",
text: "Hello"
},
{
value: "solved",
text: "Solved"
}
]
}
})
* {
font-family: Arial;
}
<script src="https://ajax.googleapis./ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare./ajax/libs/select2/4.0.5/js/select2.min.js"></script>
<link href="https://cdnjs.cloudflare./ajax/libs/select2/4.0.5/css/select2.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare./ajax/libs/vue/2.5.17/vue.min.js"></script></script>
<div id="app">
<bs-select v-model="test" :options="options"></bs-select>
<br><br>
{{ test }}
<br><br>
<button @click="test = 'solved'">
Set 'test' variable to 'solved'
</button>
</div>
It works quite nice, but he also suggested me to use this approach, which is a lot cleaner. I am currently using that now, but I leave the original answer to the question too, in case someone needs it.
I'm not sure what exactly you are trying to do here, but my guess is you were firing the onchange event twice, once when it actually changed and once in the watcher. Anyways you don't really need to use listeners like that when there are vue solutions available like this:
<div id="app">
<bs-select :value="test" v-on:change-value="test = $event" :options="options"></bs-select>
<br><br>
{{ test }}
<br><br>
<button @click="test = 'bug'">
Set 'test' variable to 'bug'
</button>
</div>
<div>
Event is firing twice in console...
</div>
ponent:
Vue.ponent("bs-select",{
template:
`<select :value="value" v-on:change="changeVal" style="width: 100%">
<option value="" selected disabled>Choose...</option>
<option v-if="!options">{{ value }}</option>
<option v-for="option in options" :value="option.value">{{ option.text }}</option>
</select>`,
props: ["value", "options"],
methods: {
changeVal: function(event) {
this.$emit('change-value', event.target.value)
}
}
});
new Vue({
el: "#app",
data: {
test: "bug",
options: [
{
value: "hello",
text: "Hello"
},
{
value: "bug",
text: "Bug"
}
]
}
})
https://jsfiddle/kv3bq1dw/
For anyone ing across this now, this is the proper way to get it to fire only once.
<template>
<select><slot></slot></select>
</template>
<script>
module.exports = {
props: ['options', 'value'],
mounted: function () {
console.log(this.options);
var vm = this;
$(this.$el)
// init select2
.select2({
data: this.options,
theme: 'bootstrap',
width: '100%',
placeholder: 'Select',
allowClear: true
})
.val(this.value)
.trigger('change')
// emit event on change.
.on('select2:select', function () { // bind to `select2:select` event
vm.$emit('input', this.value)
});
},
watch: {
value: function (value) {
// update value
$(this.$el)
.val(value)
.trigger('change');
},
options: function (options) {
// update options
$(this.$el).empty().select2({ data: options })
}
},
destroyed: function () {
$(this.$el).off().select2('destroy')
}
}
</script>