'Trying to use select2 with Importmaps on Rails 7
I am trying to use Select2 on a new Rails 7 app and am struggling as follows:
I have pinned it into my import maps and imported it like so:
pin "application", preload: true
pin "@hotwired/turbo-rails", to: "turbo.min.js", preload: true
pin "@hotwired/stimulus", to: "stimulus.min.js", preload: true
pin "@hotwired/stimulus-loading", to: "stimulus-loading.js", preload: true
pin_all_from "app/javascript/controllers", under: "controllers"
pin "trix"
pin "@rails/actiontext", to: "actiontext.js"
pin "select2", to: "https://ga.jspm.io/npm:[email protected]/dist/js/select2.js"
pin "jquery", to: "https://ga.jspm.io/npm:[email protected]/dist/jquery.js"
(the two last lines were added when I run bin/importmap pin select2)
import "jquery";
import "select2";
import "@hotwired/turbo-rails";
import "controllers";
import "trix";
import "@rails/actiontext";
(have moved both jquery and select2 to the end as well as to the beginning - didn't change a thing).
When I am in a form, I can access an element with $ like so:
$('#book_genre_ids');
...(returns the element)
But when I manually try - in the console - to run select2() on an element, here's what happens:
$('#book_genre_ids').select2();
VM574:1 Uncaught TypeError: $(...).select2 is not a function
at <anonymous>:1:22
I did check the network sources (chrome console), and I could find npm:[email protected]/dist and npm:[email protected]/dist from gap.jspm.io. I found some resources that pointed at multiple jquery libraries being loaded, but I didn't find more than the above in the network sources in the console...
Solution 1:[1]
Select2 will register itself as a jQuery function .select2()
, so in order to using this method, the select2
lib must be loaded after the jquery
lib, otherwise it could not find jquery
reference, hence it could not register jquery
function, hence the error $(...).select2 is not a function
will be throw.
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/js/select2.min.js" />
However, the import
is asynchronous loading so it's not guarantee that when select2
look for jquery
, this lib is loaded. So although you setup import them in order as below:
import "jquery";
import "select2";
The select2
is still not found the jquery
when it needed.
Fortunately, the gem importmap-rails support preloading pin modules (modulepreload), so base on that i come up with a solution: preload jquery
before select2
# config/importmap.rb
pin "jquery", to: "https://ga.jspm.io/npm:[email protected]/dist/jquery.js", preload: true
pin "select2", to: "https://cdn.jsdelivr.net/npm/[email protected]/dist/js/select2.min.js"
Then i follow the way Rails7 setup "@hotwired/stimulus" to setup "jquery"
// app/javascript/controllers/application.js
import { Application } from "@hotwired/stimulus"
...
import jQuery from "jquery"
window.jQuery = jQuery // <- "select2" will check this
window.$ = jQuery
...
now on stimulus controllers where "select2" is needed, you could load "select2"
// app/javascript/controllers/demo_controller.js
import { Controller } from "@hotwired/stimulus"
import "select2"
export default class extends Controller {
initialize() {
$('.js-example-basic-multiple').select2();
}
// ...
Note: your "select2" CDN "https://ga.jspm.io/npm:[email protected]/dist/js/select2.js" source contain import e from"jquery";
at the first line hence it'll not work in this solution, so i recommend using the official cdn link: "https://cdn.jsdelivr.net/npm/[email protected]/dist/js/select2.min.js" instead.
update
In case you don't want to use stimulus, you could pin another js file to setup "select2" and load it in your layout views
// app/javascript/utils.js
import "select2"
$(document).ready(function () {
$('.js-example-basic-multiple').select2();
});
# config/importmap.rb
...
pin "utils"
# app/views/products/show.html.erb
<%= javascript_import_module_tag("utils") %>
...
Note: you have to use javascript_import_module_tag
, not javascript_importmap_tags
, reference
Solution 2:[2]
Thanks to Lam Phan. But it is not enough. For me, loading of select2 was resolved simple, then i'm expected. It resolved select2 initialization for development and production enviroments both.
See the vendor/assets/javascripts
folder inside select2-rails gem. That folder contains the file select2.js
Add to your app/assets/config/manifest.js
lines in such order:
//= link jquery.min.js
//= link select2.js
That is enough for me and pins and import working by default:
# config/importmap.rb
pin 'jquery', to: 'jquery.min.js'
...
pin 'select2'
pin 'application'
pin_all_from 'app/javascript', under: 'application'
// app/javascript/application.js
import 'jquery'
...
import 'select2'
# app/views/layout/application.html.haml
...
%head
= javascript_importmap_tags 'application'
// app/javascript/utils/select2_init.js
export class Select2Init {
start() {
$(function() {
$('.select2').select2()
})
}
}
and run initializer in start point:
import { Select2Init } from 'application/utils/select2_init'
new Select2Init().start()
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 | Sergio Belevskij |