Select2 doesn't work on Meteor

Hey guys,

I have been trying to get select2 to work on Meteor for quite some time, but still no success. I tried the NPM package, then I switched to natestrauser:select2, then I experimented both - and neither of these options worked.

NPM-only: I get (…).select2( ) is not a function
natestrauser:select2-only: I don’t get an error, but select2 isn’t loaded anyway
Both together: Same thing as only natestrauser:select2

In all cases, the output is just a bootstrap select-box (the same thing I get without select2).

My code looks like this:

HTML

 <div class="form-group mb-3">
    <select id="select-tag" class="select2 form-control select2-multiple" data-toggle="select2" 
         multiple="multiple" data-placeholder="Choose ...">
             <option value="AK">Alaska</option>
             <option value="HI">Hawaii</option>
    </select>
 </div>

JS

Template.Tags.onRendered(function () {

    $('[data-toggle="select2"]').select2();

});

I tried many different selectors for .select2( ). The id, the classes… nothing worked.

Any ideas?

Thanks!

For the npm version, how are you importing it?

import select2 from 'select2';

I just checked one of my projects that does use Select2, and the npm module doesn’t export the right thing, so you need to pick out the compiled js file (and the css) and that exports a factory function that you need to pass jQuery into to initialise it.

Putting it together:

import select2 from 'select2/dist/js/select2';
import 'select2/dist/css/select2.css';
import 'select2-bootstrap-theme/dist/select2-bootstrap.css';

import { jQuery } from 'meteor/jquery';
// Select2 needs to be globally initialized because it exports a factory function and does not init itself
select2(window, jQuery);

And of course the documentation doesn’t say any of this… What a pain :tired_face:
I’d totally forgotten about all this


I have this in a setupSelect2 file that I import wherever select2 is required. Putting it in a separate file just helps with dependency management when using bundle splittng

2 Likes

Now that is surprisingly complicated!
I did as you wrote and now Meteor recognizes the existance of select2 (doesn’t show me the error anymore). However, it still doesn’t initialize… could you share with me your jquery selector and html?

Thanks!

I differ on the above method, I import it dynamically in an onRendered function.

import("../select2.js").then(({default: select2}) => {
import "../select2.css";
$(".select2").select2({
    minimumResultsForSearch: 2,
    width: "100%",
    matcher: function(term, text) {
      return text.toUpperCase().indexOf(term.toUpperCase()) == 0;
    }
  });

Html:

<select id="cname" name="type" class="form-control select2 form-control-lg" placeholder="Select Corpus Name">
 <option></option>
 {{#each cname}}
   <option value="{{this}}">{{this}}</option>
 {{/each}}
</select>
2 Likes

Here’s the selector:

Template.mediaLibraryDashboard.onRendered(function() {
    this.$('.searchByTag').select2();
});

@jh65592, have you copied the source of select2 directly into your project?
Looking at the import path makes me think you have?

import("../select2.js")
1 Like

Yes, I copied from the dist folder on Github and place in the ui directory one-level up from the html pages directory.

Still can’t get it to work… it looks like Meteor is completely ignoring the select2 call… it’s there (because I don’t receive any alerts), but it doesn’t generate any effect

Maybe your problem is that when the onRendered hook fires your #each loop isn’t done yet?

 {{#each cname}}
   <option value="{{this}}">{{this}}</option>
 {{/each}}
1 Like

I’m not using {{#each}} … for testing purposes I am just hardcoding the option tags

The following worked for me

Template.select2template.onRendered(async function select2onRendered() {
    import jQuery from 'jquery/dist/jquery';
    import 'select2/dist/css/select2.min.css';
    import select2 from 'select2/dist/js/select2';
    select2(window, jQuery);

    this.$('select').select2();
});
1 Like

@jamgold

When using your solution, select2 does work, but only 1 out of ~20 times I load the template - the other times, the select tag is rendered without it…

I finally got it to work! My solution was:

  • Use the Atmosphere package (natestrauser:select2) instead of the one from NPM
  • Create an async check that guarantees that the DOM element is already loaded when select2 is applied

HTML

<div class="form-group mb-3" id="select-tag-div">
      <select class="select-tag-dropdown" id="select-tag" name="state">
              <option value="AL">Alabama</option>
              <option value="WY">Wyoming</option>
      </select>
</div>

JS

Template.Select2Box.onRendered(async function select2onRendered(){

    function rafAsync() {
        return new Promise(resolve => {
            requestAnimationFrame(resolve); //faster than set time out
        });
    }

    async function checkElement(selector) {
        while (!document.getElementById(selector)) {
            await rafAsync()
        }
        return true;
    }

    checkElement('select-tag')
        .then(() => {
            $('.select-tag-dropdown').select2();
        });

});

Thanks guys! :slight_smile:

Select2 July 2023

Hi folks

Thanks to @patrickcneuhaus for a success story.
I didn’t really want to go the async check route - surely there’s a better way.

So I downloaded the package from @natestrauser’s Github project,

modified it and put it my app’s packages folder.
I also use Bootstrap 5 in my project, so found a CSS file for Bootstrap 5.
https://apalfrey.github.io/select2-bootstrap-5-theme/

See the tree output of my package below:

├── bootstrap5-theme.css
├── lib
│   └── select2
│       └── dist
│           ├── css
│           │   ├── select2.css
│           │   └── select2.min.css
│           └── js
│               ├── i18n
│               │   ├── af.js
│               │   ├── ar.js
│               │   ├── az.js
│               │   ├── bg.js
│               │   ├── bn.js
│               │   ├── bs.js
│               │   ├── ca.js
│               │   ├── cs.js
│               │   ├── da.js
│               │   ├── de.js
│               │   ├── dsb.js
│               │   ├── el.js
│               │   ├── en.js
│               │   ├── es.js
│               │   ├── et.js
│               │   ├── eu.js
│               │   ├── fa.js
│               │   ├── fi.js
│               │   ├── fr.js
│               │   ├── gl.js
│               │   ├── he.js
│               │   ├── hi.js
│               │   ├── hr.js
│               │   ├── hsb.js
│               │   ├── hu.js
│               │   ├── hy.js
│               │   ├── id.js
│               │   ├── is.js
│               │   ├── it.js
│               │   ├── ja.js
│               │   ├── ka.js
│               │   ├── km.js
│               │   ├── ko.js
│               │   ├── lt.js
│               │   ├── lv.js
│               │   ├── mk.js
│               │   ├── ms.js
│               │   ├── nb.js
│               │   ├── ne.js
│               │   ├── nl.js
│               │   ├── pl.js
│               │   ├── ps.js
│               │   ├── pt-BR.js
│               │   ├── pt.js
│               │   ├── ro.js
│               │   ├── ru.js
│               │   ├── sk.js
│               │   ├── sl.js
│               │   ├── sq.js
│               │   ├── sr-Cyrl.js
│               │   ├── sr.js
│               │   ├── sv.js
│               │   ├── th.js
│               │   ├── tk.js
│               │   ├── tr.js
│               │   ├── uk.js
│               │   ├── vi.js
│               │   ├── zh-CN.js
│               │   └── zh-TW.js
│               ├── select2.full.js
│               ├── select2.full.min.js
│               ├── select2.js
│               └── select2.min.js
├── LICENSE.txt
├── package.js
├── README.md
└── versions.json

Here’s my package.js

Package.describe({
  name: "bradzo:select2",
  summary: "Select2 is a jQuery based replacement for select boxes.",
  version: "4.0.3",
  git: "https://github.com/nate-strauser/meteor-select2.git"
});

Package.onUse(function(api) {
  // api.versionsFrom("METEOR@0.9.0");
  api.versionsFrom("METEOR@1.2");

  // api.addFiles([
  //   "lib/select2/dist/js/select2.js"
  // ], "client", { bare: true });

  // change addFiles to mainModule
  api.mainModule('lib/select2/dist/js/select2.js', 'client');

  api.addFiles([
    "lib/select2/dist/css/select2.css"
  ], "client");

  api.addFiles([
    // https://cdn.jsdelivr.net/npm/select2-bootstrap-5-theme@1.3.0/dist/select2-bootstrap-5-theme.min.css
    './bootstrap5-theme.css'
  ], 'client');
});

I downloaded version 4.0.13 of Select2 from

and dropped the dist folder in lib/ (to follow nate’s original package entry) - I also removed the .gitsubmodules file because I’m not that au fait with submodules :slight_smile:

Set some options for Select2
I wanted to use it inside a Bootstrap modal, and Select2 has me covered :slight_smile: see dropDownParent below.
To make Select2 use the Bootstrap 5 css file, set the theme param to "bootstrap-5’
The data param is set to a local array of JSON objects { id: “abc123”, text: “Option text” }
I also wanted to let the user add entries that aren’t in the select list, hence the tags: true param.

Template.myTemplate.onRendered(function() {

  $(document).ready(function() {

    $('#selectedCustomer').select2({
      // https://select2.org/troubleshooting/common-problems#select2-does-not-function-properly-when-i-use-it-inside-a-bootst
      dropdownParent: $('#newInvoiceModal'),
      // https://apalfrey.github.io/select2-bootstrap-5-theme/
      theme: 'bootstrap-5',
      data: items,
      // this allows to add a new customer by name
      tags: true
    });
    console.log("doc ready")

  });
});

Upshot of all is that I got it to work relatively painlessly, albietly after a couple of days mucking around with other bootstrap select libraries - Select2 seemed to give me the functionality I wanted.

My current Meteor version for my project is 2.7.3

I hope this helps someone else.

Cheers
Brad

PS - I just published this to Atmosphere - bradzo:select2
:slight_smile: