Using Select2 in VueJs and importing JQuery
I want to use Select2 inside Vue js so I used this package but I can't import JQuery correctly and it gives me this error:
Uncaught (in promise) TypeError: $(...).find(...).select2 is not a function
the package imported JQuery at the top of script tag like this:
import $ from 'jquery';
The project is Laravel 10 with Ineria js.
@waves I know how to use it but the problem is with the Vite. The JQuery and Select2 are not available. I imported both of them like this in bootstrap.js:
import $ from 'jquery';
window.jQuery = window.$ = $;
import select2 from 'select2';
window.select2 = select2;
Don't mess around with the script in the package, if you pulled it in with npm or yarn it will be the latest stable version, so the issue will not be with the code in the package, it will be with how you implement it.
Can you show us your code of the component where you are using select2?
There is a good guide here which show how to use implement select2 in vue2: https://github.com/godbasin/vue-select2.
You shouldn't need to touch anything in bootstrap.js for using select2.
@CamKem I just copied code from this File and add some customization. because this package is a single file there's no difference. I did this before with Laravel Mix and it worked perfectly. I know the problem is Vite but I don't know how to fix it. by the way I also used the package directly and it is not working because JQuery is not defined.
@Armani All changes you need to make to Vite will happen in the vite.config.js file, so if it's related to vite the changes or additions will go in here.
However, it really doesn't sound like a Vite issue, from the description on your error. It seems like are importing jQuery twice, remove it from the the bootstrap.js as if you have it there & also in your select2.js you will be double importing it and that will cause issues.
Is there anywhere else within that app's components that would cause jQuery to be loaded twice? (As you said you were getting that error before adding the extra lines to bootstrap.js)
@CamKem Don't worry there's no duplication of importing JQuery. this is my Select.vue file:
<template>
<div>
<select class="form-control" :id="id" :name="name" :disabled="disabled" :required="required"></select>
</div>
</template>
<script>
export default {
name: 'Select2',
data() {
return {
select2: null
};
},
emits: ['update:modelValue'],
props: {
modelValue: [String, Array], // previously was `value: String`
id: {
type: String,
default: ''
},
name: {
type: String,
default: ''
},
placeholder: {
type: String,
default: ''
},
options: {
type: Array,
default: () => []
},
disabled: {
type: Boolean,
default: false
},
required: {
type: Boolean,
default: false
},
settings: {
type: Object,
default: () => {}
},
display: {
type: String,
default: 'name'
}
},
watch: {
options: {
handler(val) {
this.setOption(val);
},
deep: true
},
modelValue: {
handler(val) {
this.setValue(val);
},
deep: true
},
},
methods: {
setOption(val = []) {
this.select2.empty();
this.select2.select2({
placeholder: this.placeholder,
...this.settings,
data: this.formatOptions(val)
});
this.setValue(this.modelValue);
},
setValue(val) {
if (val instanceof Array) {
this.select2.val([...val]);
} else {
this.select2.val([val]);
}
this.select2.trigger('change');
},
formatOptions(options) {
return $.map(options, (obj) => {
obj.text = obj[this.display]; // replace name with the property used for the text
return obj;
});
}
},
mounted() {
this.select2 = $(this.$el)
.find('select')
.select2({
placeholder: this.placeholder,
...this.settings,
data: this.formatOptions(this.options)
})
.on('select2:select select2:unselect', ev => {
this.$emit('update:modelValue', this.select2.val());
this.$emit('select', ev['params']['data']);
});
this.setValue(this.modelValue);
},
beforeUnmount() {
this.select2.select2('destroy');
}
};
</script>
and inside app.blade.php I imported js and css traditionally:
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title inertia>{{ config('app.name') }}</title>
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/select2.min.css" rel="stylesheet" />
@vite(['resources/scss/app.scss'])
@routes
@vite(['resources/js/app.js', "resources/js/Pages/{$page['component']}.vue"])
@inertiaHead
</head>
<body>
@inertia
<script src="https://code.jquery.com/jquery-3.6.4.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/select2.min.js"></script>
</body>
</html>
Also there's no import inside bootstrap.js and everything works fine, but I don't want to do it this way.
@Armani you have a JQuery CDN link in your app.blade.php, this will cause a conflict with the importing of JQuery in the bootstrap.js. If you want to keep the import in bootstrap.js you will need to remove the CDN link. That should solve it.
@CamKem I said there's no import of JQuery inside bootstrap.js. read carefully.
@Armani yes, but it will imported by the select2 CDN below the JQuery, so it will be double imported. Somewhere what you are facing is a double import of JQuery.
If you see these stackoverflow questions, the answer is always the same double import of jQuery.
If it’s not the action import of jQuery doing it, then it’s related to how you are calling jQuery causing it to pull in the jQuery from the CDN twice.
https://stackoverflow.com/questions/58269585/select2-is-not-a-function
https://stackoverflow.com/questions/41277344/select2-is-not-working
https://stackoverflow.com/questions/20034522/select2-is-not-a-function
@CamKem In the component I didn't import anything, also inside boostrap.js, I only added cdn links inside app.blade.php. I just showed you that the problem is with the Vite.
@Armani I really don’t think the issue is relating to vite, as vite is asset bundling & that error does not point to an issue with assets. Either way, as it’s a really bad idea to use a CDN in a production environment & you are using Vue anyway, I don’t understand why you don’t use it how most people would?
import as global component.
// import Select2Component
import Select2 from 'v-select2-component';
Vue.component('Select2', Select2);
new Vue({
// ...
})
import into a single component.
// import Select2Component
import Select2 from 'v-select2-component';
<template>
<div>
<Select2 v-model="myValue" :options="myOptions" :settings="{ settingOption: value, settingOption: value }" @change="myChangeEvent($event)" @select="mySelectEvent($event)" />
<h4>Value: {{ myValue }}</h4>
</div>
</template>
<script>
export default {
// declare Select2Component
components: {Select2},
data() {
return {
myValue: '',
myOptions: ['op1', 'op2', 'op3'] // or [{id: key, text: value}, {id: key, text: value}]
}
},
methods: {
myChangeEvent(val){
console.log(val);
},
mySelectEvent({id, text}){
console.log({id, text})
}
}
}
</script>
@CamKem I don't use CDN links, It was just a test. also I use Vue Js 3 not 2. You can test it on your side, install the package and try to use it then you can see what am I facing.
Then if still a problem you could try adding it to vite.config.js as I mentioned further up. Something like:
export default defineConfig({
//...
optimizeDeps: {
//...
include: [
"V-select2-component”
// include other packages that may broke the build
]
}
});
I just gave it a go & it seems the package is depreciated. There seems to be a conflict in the way that it is expecting jQuery to have a default export. Even on GitHub the packages has been archived. I don’t know enough about the jQuery package to work it out without spending time on looking through thing & don’t have time now (it’s 11:47pm here)
If you don’t figure it out, I’ll take a look tomorrow afternoon & see if I can work out what’s going on.
Please or to participate in this conversation.