How can I using select2 with webpack?
Asked Answered
C

6

42

I'm using webpack to manage all my assets, when I use this code to require select2 (https://github.com/select2/select2) I got the error

$(...).select2 is not function.

require.ensure(['./vendors/select2'],function (require) {
    require('./site');
});
// site.js
(function ($) {
    $(document).ready(function () {
        $(".js-1example-basic-single").select2();
    });
})(jQuery);

I think there is something wrong with module export. I tried many search but no hope.

Anyone please tell me what to do, It took me about 10 hours.

Thank you!

Checkoff answered 20/7, 2016 at 5:49 Comment(0)
M
72

You can run select2 in this way:

import $ from 'jquery';
import 'select2';                       // globally assign select2 fn to $ element
import 'select2/dist/css/select2.css';  // optional if you have css loader

$(() => {
  $('.select2-enable').select2();
});
Mccollough answered 20/7, 2016 at 8:33 Comment(6)
@Checkoff I've included properly working link in webpackbin.Mccollough
I'm really appreciate your help! @MccolloughCheckoff
@Mccollough link is dead.Eclosion
@SizzlingCode I have just created new webpackbin (and updated answer).Mccollough
@does this require that jquery be bundled? I'm loading my bundle into a site that already has jquery installed. So it's not part of my bundle. I tried import select2 in my loader.js file but I still get select2 is not a functionMadder
@Madder See the Webpack externals configuration option. externals: { jquery: "jQuery" } worked for me. This instructs Webpack to exclude jquery from your bundle and imports will still work.Hyla
N
37

For anyone using Parcel bundler to load select2, simply importing it didn't work.

I had to initialize it as follows instead:

//Import
import $ from 'jquery';
import select2 from 'select2';

//Hook up select2 to jQuery
select2($);

//...later
$(`select`).select2();

Without the hookup call and passing jQuery into the function, it wouldn't bind and result in a $(...).select2 is not function. error.

Ninny answered 8/4, 2018 at 20:35 Comment(3)
Thanks, in my case in my app(Meteor) with many packages, i must initialize like this : select2(window,$);Dixiedixieland
Laravel9 with Vite, you need to do select2(window,$); as wellSonger
Unbelievable how hard it was to find this golden nugget. But, it's what was necessary. Besides the import, you appear to have to pass the jquery obj to the select2 for the initialization to complete. Thank you !Enshrine
Y
10

Load the src version of Select2

Sean Larkin, one of the webpack developers says:

Most modules link the dist version in the main field of their package.json. While this is useful for most developers, for webpack it is better to alias the src version because this way webpack is able to optimize dependencies better...1

Following this advice, I prefer to require files under the src folder:

import "select2/src/js/jquery.select2.js";
import "select2/src/scss/core.scss";

Load language files statically

You'll then find there are various language-related hurdles to overcome. As soon as you insert $(".dropdown").select2() into your code, you'll see Uncaught Error: Cannot find module './i18n/en'. This is because the dynamic require designed for RequireJS is not working. It comes from the loadPath function in translation.js:

if (!(path in Translation._cache)) {
    var translations = require(path);
    Translation._cache[path] = translations;
}

In webpack parlance this is called a 'require expression'. My solution is to avoid ever reaching that line by priming the cache first. In my app code I put:

import EnglishTranslation from "select2/src/js/select2/i18n/en.js";
import { _cache } from "select2/src/js/select2/translation.js";
_cache["./i18n/en"]=EnglishTranslation;
_cache["en"]=EnglishTranslation;

You will need to do this for all the languages you wish to use. Then you can use the language features as documented, including $.fn.select2.defaults.set('language',"en") or language: en during initialisation. Overrides like language: { noResults: function() { return "abc"; } } work also.

Disable the contextless require

The above instructions give you a working select2, but Webpack will be complaining, Critical dependency: the request of a dependency is an expression. This means, "webpack needs to include all files inside the current folder and all files in child folders"2, which would be everything under select2/src/js/select2!

I found I could use imports-loader to disable the require() in the translation module completely, whilst leaving the define() call intact, so that it could still do its exports. Here's an excerpt from my webpack.config.js:

For webpack 5, imports-loader 4

module: {
   rules: [
      {
         test: /select2\/src\/js\/select2\/translation\.js$/,
         use: {
            loader: "imports-loader",
            options: {
               additionalCode: "var require=false;"
            }
         }
      },
   ]
}

For webpack 4 and imports-loader 0.8

module: {
    rules: [
        {
            test: /select2\/src\/js\/select2\/translation\.js$/,
            use: {
            loader: "imports-loader",
            options: "require=>false"
        }
     ]
}

Writing Custom Adapters

You can use webpack to import various components to write your own data adapters. The select2 adapter documentation assumes you will use something like the supplied Almond loader (e.g. $.fn.select2.amd.require), so it took me a while to realise I could do this kind of thing:

import * as Utils from "select2/src/js/select2/utils";
import * as ArrayAdapter from "select2/src/js/select2/data/array";

Hints

  1. Switch on $.fn.select2.defaults.set('debug',true); when diagnosing language issues
  2. On versions below 4.09, avoid strange language defaults by adding $("html").removeAttr("lang"); to your app.5
Yahrzeit answered 12/7, 2019 at 4:2 Comment(4)
You are a hero. Only things I changed are the import style to ES6: import Select from "select2/src/js/jquery.select2.js"; import { _cache } from "select2/src/js/select2/translation"; import EnglishTranslation from "select2/src/js/select2/i18n/en"; _cache["./i18n/en"]=EnglishTranslation; _cache["en"]=EnglishTranslation;Rushton
Thank you this solved my issue, but the imports-loader config has changed. options: "require=>false" is no longer a valid config string.Psychoneurotic
Thanks @gabn88, I have applied most of your suggestions.Yahrzeit
@Mikepote, I've updated my answer so that it now works with imports-loader v4.Yahrzeit
G
2

I was having the same error but using Laravel 10 and Vite, it solved as:

// Since I'm using admin-lte 🤷 included select2
import select2 from 'admin-lte/plugins/select2/js/select2';
select2(window, $);
Glare answered 11/3, 2023 at 20:50 Comment(2)
hei thank you this is solve the issue I currently got using vite js, luckly I came here matched the search, again thanks a lot! confirmed on laravel 10 vite jsWoden
If it helps, vote for my post ✌️Glare
M
1

You can simply do like this :

import $ from 'jquery';
import 'select2';


$('selector').select2(); //selector can be className, ID, tag name , attributeName , etc ...
Mikael answered 2/2, 2019 at 20:15 Comment(0)
W
0

Tried all above solutions but the only way I'v managed to use select2 with webpack and rails6 was to add script loaded to the package and use it like:

import 'select2';
import 'script-loader!select2/dist/js/select2.js';
import 'select2/dist/css/select2.css';

Wycliffite answered 2/1, 2020 at 10:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.