最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Vue component $emit firing twice - Stack Overflow

programmeradmin0浏览0评论

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 badges
Add a ment  | 

3 Answers 3

Reset to default 1

After 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>
发布评论

评论列表(0)

  1. 暂无评论