【问题标题】:Trying to use select2 with Importmaps on Rails 7尝试在 Rails 7 上使用 select2 和 Importmaps
【发布时间】:2022-05-06 00:32:42
【问题描述】:

我正在尝试在一个新的 Rails 7 应用程序上使用 Select2,但遇到以下问题:

我已将它固定到我的导入地图中并像这样导入它:

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:select2@4.1.0-rc.0/dist/js/select2.js"
pin "jquery", to: "https://ga.jspm.io/npm:jquery@3.6.0/dist/jquery.js"

(最后两行是我运行 bin/importmap pin select2 时添加的)

import "jquery";
import "select2";
import "@hotwired/turbo-rails";
import "controllers";
import "trix";
import "@rails/actiontext";

(已将 jquery 和 select2 移到末尾和开头 - 没有改变任何事情)。

当我在表单中时,我可以像这样使用 $ 访问元素:

$('#book_genre_ids');
...(returns the element)

但是当我手动尝试 - 在控制台中 - 在元素上运行 select2() 时,会发生以下情况:

$('#book_genre_ids').select2();
VM574:1 Uncaught TypeError: $(...).select2 is not a function
    at <anonymous>:1:22

我确实检查了网络源(chrome 控制台),我可以从 gap.jspm.io 找到 npm:jquery@3.6.0/dist 和 npm:select2@4.1.0-rc.0/dist。 我发现了一些指向正在加载的多个 jquery 库的资源,但是我在控制台的网络源中没有找到比上述更多的资源...

【问题讨论】:

    标签: ruby-on-rails jquery-select2 ruby-on-rails-7 import-maps


    【解决方案1】:

    Select2会将自己注册为一个jQuery函数.select2(),所以为了使用这个方法,select2 lib必须在jquery lib之后加载,否则找不到jquery引用,因此它无法注册jquery函数,因此会抛出错误$(...).select2 is not a function

    <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" />
    

    但是,import 是异步加载的,因此不能保证当select2 查找jquery 时,会加载此库。因此,尽管您按如下顺序设置导入它们:

    import "jquery";
    import "select2";
    

    select2 在需要时仍然找不到jquery

    幸运的是,gem importmap-rails 支持预加载 pin 模块 (modulepreload),所以基于此我想出了一个解决方案:在 select2 之前预加载 jquery

    # config/importmap.rb
    pin "jquery", to: "https://ga.jspm.io/npm:jquery@3.6.0/dist/jquery.js", preload: true
    pin "select2", to: "https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"
    

    然后我按照 Rails7 设置“@hotwired/stimulus”的方式设置“jquery”

    // app/javascript/controllers/application.js
    import { Application } from "@hotwired/stimulus"
    ...
    import jQuery from "jquery"
    window.jQuery = jQuery // <- "select2" will check this
    window.$ = jQuery
    ...
    

    现在在需要“select2”的刺激控制器上,您可以加载“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();
      }
    // ...
    

    注意:您的“select2”CDN“https://ga.jspm.io/npm:select2@4.1.0-rc.0/dist/js/select2.js”源在第一行包含import e from"jquery";因此它在这个解决方案中不起作用,所以我建议使用官方 cdn 链接:“https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min。 js”代替。

    更新

    如果您不想使用刺激,您可以固定另一个 js 文件来设置“select2”并将其加载到您的布局视图中

    // 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") %>
    ...
    

    注意:你必须使用javascript_import_module_tag,而不是javascript_importmap_tagsreference

    【讨论】:

    • 感谢详尽的回答!我确实将 jquery 的 importmap 更改为预加载,并且我还更改了 select2 的源。但是,我现在有点困惑。我只是想在表单上调用 select2 .... 在 Rails 6 中,我会使用一个特殊的类,然后通过 javascript 使用类似 $('.class-name').select2() .... 为什么我需要在刺激控制器上加载 select2 吗?
    • @tkhobbes 不用stimulus,看看我的更新。
    • 好的,谢谢 - 这行得通。我现在有一个问题,在我的表单中有两个选择框(一个由 select2 触发,一个由另一个触发)但我认为这是一个样式表问题......我想我现在需要导入相应的 select2 样式表。
    【解决方案2】:

    感谢林潘。但这还不够。对我来说, select2 的加载很简单,然后我就可以预料到了。它解决了开发和生产环境的 select2 初始化问题。

    查看 select2-rails gem 中的vendor/assets/javascripts 文件夹。该文件夹包含文件select2.js

    按以下顺序添加到您的app/assets/config/manifest.js 行:

    //= link jquery.min.js
    //= link select2.js
    

    这对我来说已经足够了,默认情况下可以使用 pin 和 import:

    # 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()
        })
      }
    }
    

    并在起点运行初始化程序:

    import { Select2Init } from 'application/utils/select2_init'
    
    new Select2Init().start()
    

    【讨论】:

    • 谢谢塞尔吉奥。实际上,我在 importmaps 上遇到了很多其他问题,所以我改用 jsbundling。
    猜你喜欢
    • 2023-01-24
    • 2023-02-14
    • 1970-01-01
    • 1970-01-01
    • 2022-07-24
    • 2015-11-06
    • 2021-12-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多