Jun
2
2020

Making Select2 Work with VueJS for Single and Multiselect Menus

Recently I was migrating a website from a traditional front end; HTML, CSS, jQuery, etc… to Blade with VueJS and all was well in the world until…

SELECT2. Getting the jQuery Select2 plugin to work with VueJS did not go well.

The select2 menu items were populating, the forms seemed great, easy swap- no problem!  LIES.  ALL LIES.  After finishing up the initial front end migration, naturally I tried to submit my beautiful new VueJS form to the server to make sure it functioned properly, at which point my day was shot.

The Problem

For single select menus using the Select2 plugin, only the first value that was selected was sent, and with multi-select menus, only the first value selected was sent.  And to make matters even better- once a value was selected, even if you changed it, the v-model ignored it completely.

I found many articles on this specific issue, including a Wrapper Component Example from the Vuejs docs themselves which worked great for a single select menu, BUT ALAS, the same problem persisted with multiselect menus.  Either no values were submitted, or only the first item selected was sent.

Ultimately the problem was that the onChange events issued by Select2 are completely ignored by VueJS (which is correct but annoying), so the solution was to bind and then manipulate the values on the select2 change events.

Making Select2 Work with VueJS

import Vue from "vue";
const app = new Vue({
	el: '#app',
});

Vue.component("select2", {
	props: ["options", "value"],
	template: "<select><slot></slot></select>",
	mounted: function() {
		var app = this
		$(this.$el)
			.val(this.value)
			.select2({
				data: this.options
			})
			// emit event on change.
			.on('change', function () {
				app.$emit('input', $(this).val())
			})
	},
	watch: {
		options: function (options) {
			// update options
			$(this.$el).select2({ data: options })
		},
		value: function (value) {
			if ([...value].sort().join(",") !== [...$(this.$el).val()].sort().join(","))
				$(this.$el).val(value).trigger('change');
		}
	},
	destroyed: function() {
		$(this.$el)
			.off()
			.select2("destroy");
	}
});

Usage in your components:

<select2 
	multiple 
	:class="[ 'form-control form-control-chosen' ]" 
	:options="data.options.multiselect_opts" 
	v-model="registration.multiple_things" 
	data-placeholder="Do you like to check multiple options?">
</select2>

Or for a single select:

<select2 
	:class="[ 'form-control form-control-chosen' ]" 
	:options="data.options.single_options_list" 
	v-model="registration.single_item" 
	data-placeholder="Do you like to check just one option?">
</select2>

Hopefully this saves someone some time and frustration! There are plentiful existing VueJS components and packages which already exist and can replace Select2, but if you’re trying to do a straight swap of some legacy code and would rather not go that route, this takes care of it!

Leave a comment