ruby-on-railsjquery-uiruby-on-rails-6rails-enginesruby-on-rails-7

How to use jqueryUI in a rails 6 or rails 7 alpha engine


I would be grateful if someone could show the exact steps needed to use jquery ui in a rails 6 or rails 7 Alpha 2 engine. I have been unable to get either importmap-rails to work in a rails 7 engine nor can I get webpacker to work in a Rails 6 engine or rails 7 alpha 2 engine. Given an engine called custom_page generated using

rails plugin new custom_page --mountable --full

I included the jquery-ui-rails in the gemspec as a dependency.

spec.add_dependency 'jquery-ui-rails'

Perhaps this should be a runtime_dependency? The full list of dependencies is

  spec.add_dependency "rails", "~> 7.0.0.alpha2"

  spec.add_dependency 'new_ckeditor'

  spec.add_dependency 'ancestry'
  spec.add_dependency 'friendly_id', '>= 5.4.0'
  spec.add_dependency 'pg_search'

  spec.add_dependency 'carrierwave', '~> 2.0'
  spec.add_dependency 'carrierwave-imageoptimizer'
  spec.add_dependency 'sassc-rails', '~> 2.0.0'
  spec.add_dependency 'pg', '~> 1.1'
  spec.add_dependency 'jquery-rails'
  spec.add_dependency 'jquery-ui-rails'

  spec.add_development_dependency "puma"

  #Testing Gems
  spec.add_development_dependency "rspec-rails", '>= 5.0'
  spec.add_development_dependency "factory_bot_rails"
  spec.add_development_dependency "guard-rspec"

  spec.add_development_dependency 'capybara', '>= 3.32'
  spec.add_development_dependency 'selenium-webdriver'
  spec.add_development_dependency 'launchy'
  spec.add_development_dependency 'database_cleaner-active_record'

I have required the same in the engine.rb

require 'jquery-ui-rails'
require 'friendly_id'
require 'ancestry'

    module CustomPage
      class Engine < ::Rails::Engine
        isolate_namespace CustomPage
    
        config.generators do |g|
          g.test_framework :rspec,
            fixtures: false,
            request: false,
            view_specs: false,
            helper_specs: false,
            controller_specs: false,
            routing_specs: false
          g.fixture_replacement :factory_bot
          g.factory_bot dir: 'spec/factories'
        end
      end
    end

I have added a simple test in a view

<p id="notice"><%= notice %></p>

<script type='text/javascript'>
  $(function() {
    $('.datepicker').datepicker();
  });
</script>

I have included the require in app/assets/stylesheets/custom_page/application.css

/*
 * This is a manifest file that'll be compiled into application.css, which will include all the files
 * listed below.
 *
 * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
 * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
 *
 * You're free to add application-wide styles to this file and they'll appear at the bottom of the
 * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
 * files in this directory. Styles in this file should be added after the last require_* statement.
 * It is generally better to create a new file per style scope.
 *
 *= require_tree .
 *= require_self
 */
 /*
  *= require jquery-ui
 */

And also in the app/assets/config/custom_page_manifest.js

//= link_directory ../stylesheets/custom_page .css
//= require jquery-ui

I get an error when inspecting the browser console in firefox showing

Uncaught ReferenceError: $ is not defined

I've obviously shown a Rails 7 alpha 2 example here, however the same issue occurs using rails 6.1.4

The purpose of this exercise is to enable me to use the jquery library jqtree which would be simple to setup if I were to be able to use importmap-rails, however, as per my open, as yet unanswered, question here, I am unable to do so.

So I'm really asking how to use jquery libraries in Rails 6.1.4 engines or in Rails 7 alpha 2 libraries


Solution

  • This wasn't as straightforward as I thought it would be. First of all, jquery-ui-rails and jquery-rails seem outdated. I don't think you should use those gems. We can use importmaps since we've set it up for the engine already. But the usage isn't limited to the engine.

    # config/importmap.rb
    
    # NOTE: pin jquery to jsdelivr. this will make jquery global on import;
    #       jspm wraps packages in a module [1], so jquery is not available globally.
    pin "jquery", to: "https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.js"
    
    # NOTE: a few answers suggested `jquery-ui-dist`. I couldn't add it with 
    #       `bin/importmap`; use https://generator.jspm.io/ to get the url.
    pin "jquery-ui-dist", to: "https://ga.jspm.io/npm:jquery-ui-dist@1.13.1/jquery-ui.js"
    
    # works fine
    pin "jqtree", to: "https://ga.jspm.io/npm:jqtree@1.6.2/lib/tree.jquery.js"
    
    # [1] someone, please, link to/explain what jspm does exactly to cause this.
    
    // app/javascript/application.js
    
    import "jquery";
    import "jquery-ui-dist";
    import "jqtree";
    
    console.log(window.$);   // jQuery is already global
    console.log($.ui);       // jquery-ui initialized on import
    console.log($().tree);   // jqtree also initialized
    

    If you'd like to stick to jspm for some reason, dynamic import() can be used to load jquery plugins.

    https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#dynamic_imports

    # config/importmap.rb
    
    pin "jquery", to: "https://ga.jspm.io/npm:jquery@3.6.0/dist/jquery.js"
    pin "jquery-ui-dist", to: "https://ga.jspm.io/npm:jquery-ui-dist@1.13.1/jquery-ui.js"
    pin "jqtree", to: "https://ga.jspm.io/npm:jqtree@1.6.2/lib/tree.jquery.js"
    
    // app/javascript/application.js
    
    import jQuery from "jquery";
    
    // NOTE: keep in mind, these will be lazily loaded; use first method if this 
    //       doesn't work in your set up.
    import("jquery-ui-dist");
    import("jqtree");
    
    console.log(window.$);      // undefined
    console.log(window.jQuery); // undefined
    console.log(jQuery().ui);   // undefined
    console.log(jQuery().tree); // undefined
    
    // NOTE: make jquery global
    window.$ = window.jQuery = jQuery;
    

    For rails 6 setup with jquery-ui-rails and jquery-rails gems. Make sure you require jquery as well. Don't touch manifest.js, you're not precompiling jquery, you're precompiling application.{css,js} which already includes jquery. You have to add require to application.js as well.

    https://github.com/rails/jquery-rails#installation

    https://github.com/jquery-ui-rails/jquery-ui-rails#require-everything

    // app/assets/javascripts/application.js
    
    //= require jquery
    //= require jquery-ui
    
    /* app/assets/stylesheets/application.css */
    
    /*= require jquery-ui */