Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Binding a Select2 select to a Vue js model data

I'm using select2 to enhance an html select element. I want to bind the value of the select element to a Vue variable, however the Select2 seems to be preventing this.

What's the best way to achieve this data binding and event listening whilst retaining the Select2 behaviour. I imagine it's a case of linking the select2 events to the Vue instance.

I've made a fiddle demonstrating the problem (does not run below but works on jsfiddle):

$('#things').select2();

new Vue({
  el: "#vue-example",
  data: {
    thing: null,
    thing2: null
  },
  methods: {
    log: function(str) {
      $('#log').append(str + "<br>");
    }
  }
});
select {
  width: 50%;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.0/js/select2.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.0/css/select2.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/0.12.8/vue.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>

<div id="vue-example">
  <label>Select2 Select Box</label>
  <select name="things" id="things" v-model="thing" v-on="change: log('you changed thing1')">
    <option value="1">One</option>
    <option value="2">Two</option>
    <option value="3">Three</option>
    <option value="4">Four</option>
    <option value="5">Five</option>
  </select>
  <br>
  <br>
  <label>Native Select Box</label>
  <select name="things" id="things" v-model="thing2" v-on="change: log('you changed thing2')">
    <option value="1">One</option>
    <option value="2">Two</option>
    <option value="3">Three</option>
    <option value="4">Four</option>
    <option value="5">Five</option>
  </select>
  <pre>{{ $data | json }}</pre>
  <div id="log">

  </div>
</div>
like image 829
harryg Avatar asked Jul 24 '15 10:07

harryg


People also ask

Does Vue JS have two-way data binding?

Vue is also perfectly capable of powering sophisticated Single-Page Applications in combination with modern tooling and supporting libraries. The v-model directive makes two-way binding between a form input and app state very easy to implement.

What does binding mean in Vue?

The v-bind directive is a Vuejs directive used to bind one or more attributes, or a component prop to an element. If that attribute is binded to our data defined in Vuejs instance then dynamically changes can be observed as data changes.


3 Answers

A shorter solution:

Vue.directive('select2', {
    inserted(el) {
        $(el).on('select2:select', () => {
            const event = new Event('change', { bubbles: true, cancelable: true });
            el.dispatchEvent(event);
        });

        $(el).on('select2:unselect', () => {
            const event = new Event('change', {bubbles: true, cancelable: true})
            el.dispatchEvent(event)
        })
    },
});

Trae's answer is good but the value is already being stored on the select element, so all you have to do, is dispatch the event to let Vue know that we have changed it.

Just simply do:

<select v-model="myprop" v-select2>
...
like image 191
Wiktor Zawierucha Avatar answered Oct 23 '22 14:10

Wiktor Zawierucha


swift's answer is correct. You just need to identify and update that code to reflect the differences in Select2.

http://jsfiddle.net/qfy6s9Lj/10/

Vue.directive('selecttwo', {
  twoWay: true,
  bind: function () {
    $(this.el).select2()
    .on("select2:select", function(e) {
      this.set($(this.el).val());
    }.bind(this));
  },
  update: function(nv, ov) {
    $(this.el).trigger("change");
  }
});

new Vue({
  el: "#vue-example",
  data: {
    thing: null,
    thing2: null
  },
  methods: {
    log: function(str) {
      $('#log').append(str + "<br>");
    }
  }
});
select {
  width: 50%;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.0/js/select2.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.0/css/select2.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/0.12.8/vue.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>

<div id="vue-example">
  <label>Select2 Select Box</label>
  <select name="things" id="things" v-model="thing" v-selecttwo="thing" v-on="change: log('you changed thing1')">
    <option value="1">One</option>
    <option value="2">Two</option>
    <option value="3">Three</option>
    <option value="4">Four</option>
    <option value="5">Five</option>
  </select>
  <br>
  <br>
  <label>Native Select Box</label>
  <select name="things" id="things" v-model="thing2" v-selecttwo="thing2" v-on="change: log('you changed thing2')">
    <option value="1">One</option>
    <option value="2">Two</option>
    <option value="3">Three</option>
    <option value="4">Four</option>
    <option value="5">Five</option>
  </select>
  <pre>{{ $data | json }}</pre>
  <div id="log">

  </div>
</div>
like image 27
Trae Avatar answered Oct 23 '22 14:10

Trae


This solution works for the Chosen plugin, but you can do the same thing with Select2 to make it work:

http://jsfiddle.net/simplesmiler/qfy6s9Lj/8/

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) {
                // two-way set
                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");
like image 2
swift Avatar answered Oct 23 '22 14:10

swift