R4ttlesnake
R4ttlesnake

Reputation: 1761

Using Rails-UJS in JS modules (Rails 6 with webpacker)

i just switched to Rails 6 (6.0.0.rc1) which uses the Webpacker gem by default for Javascript assets together with Rails-UJS. I want to use Rails UJS in some of my modules in order to submit forms from a function with:

const form = document.querySelector("form")
Rails.fire(form, "submit")

In former Rails versions with Webpacker installed, the Rails reference seemed to be "globally" available in my modules, but now i get this when calling Rails.fire

ReferenceError: Rails is not defined

How can i make Rails from @rails/ujs available to a specific or to all of my modules?

Below my setup…

app/javascript/controllers/form_controller.js

import { Controller } from "stimulus"

export default class extends Controller {
  // ...
  submit() {
    const form = this.element
    Rails.fire(form, "submit")
  }
  // ...
}

app/javascript/controllers.js

// Load all the controllers within this directory and all subdirectories. 
// Controller files must be named *_controller.js.

import { Application } from "stimulus"
import { definitionsFromContext } from "stimulus/webpack-helpers"

const application = Application.start()
const context = require.context("controllers", true, /_controller\.js$/)
application.load(definitionsFromContext(context))

app/javascript/packs/application.js

require("@rails/ujs").start()
import "controllers"

Thanks!

Upvotes: 40

Views: 20255

Answers (5)

Gray Kemmey
Gray Kemmey

Reputation: 1002

I think the best way is to use the expose-loader and configure it the same way webpacker would if you ran bundle exec rails webpacker:install:erb.


Install the expose-loader

$ yarn add expose-loader

Create a config file

  1. For loaders webpacker configures itself, it'll dump a config object in config/webpack/loaders. Create that folder if it doesn't exist.

  2. Create a file called config/webpack/loaders/expose.js

  3. Add this to that file:

    module.exports = {
      test: require.resolve('@rails/ujs'),
      use: [{
        loader: 'expose-loader',
        options: 'Rails'
       }]
    }
    
    // later versions of expose loader may allow the following API:
    module.exports = {
      test: require.resolve('@rails/ujs'),
      loader: 'expose-loader',
      options: {exposes: "Rails"}
    }
    

Add that loader to environment.js

Add these two lines to config/webpack/environment.js:

const expose = require('./loaders/expose')
environment.loaders.prepend('expose', expose)

The full file should look something like:

const { environment } = require('@rails/webpacker')
const expose = require('./loaders/expose')

environment.loaders.prepend('expose', expose)
module.exports = environment

That should give you access to the Rails object globally again.

Upvotes: 1

ThienSuBS
ThienSuBS

Reputation: 1622

First at all, using yarn add rails/ujs:

yarn add  @rails/ujs

And add to config/webpack/environment.js

const webpack = require('webpack')
environment.plugins.prepend('Provide',
  new webpack.ProvidePlugin({
    $: 'jquery',
    jQuery: 'jquery',
    Popper: ['popper.js', 'default'],
    toastr: 'toastr/toastr',
    ApexCharts: ['apexcharts', 'default'],
    underscore: ['underscore', 'm'],
    Rails: ['@rails/ujs']
  })
)
module.exports = environment

Config and load Rails js.

# pack/application.js
require("@rails/ujs").start()
global.Rails = Rails;

And Then: This is result -> My result when i typed Rails in Firefox Console

Upvotes: 19

inopinatus
inopinatus

Reputation: 3779

in my app/javascript/packs/application.js:

import Rails from '@rails/ujs';
Rails.start();

and then in whatever module, controller, component I'm writing:

import Rails from '@rails/ujs';

Upvotes: 57

ekr990011
ekr990011

Reputation: 744

I am currently messing around on 6.0.0.rc2 but I think I got an answer for you.

So if you separate out the:

app/javascript/packs/application.js

require("@rails/ujs").start()
import "controllers"

To instead:

export const rails_ujs = require("@rails/ujs")
console.log(rails_ujs)
rails_ujs.start()

You can obviously remove that console.log was just trying to figure things out. Then in your stimulus controller you can simply do:

// Visit The Stimulus Handbook for more details
// https://stimulusjs.org/handbook/introduction
//
// This example controller works with specially annotated HTML like:
//
// <div data-controller="hello">
//   <h1 data-target="hello.output"></h1>
// </div>

import { Controller } from "stimulus"
import { rails_ujs } from "packs/application.js"

export default class extends Controller {
  static targets = [ "output" ]

  connect() {
    // this.outputTarget.textContent = 'Hello, Stimulus!'
    console.log('hi')
    console.log(rails_ujs)
  }
}

Just using their little test controller here but I got it to console.log out and you can call rails_ujs.fire so that should be what you want :)

Let me know if this works for you!

Upvotes: 4

Andrew Cetinic
Andrew Cetinic

Reputation: 2835

Just add it to your environment.js file, here is mine (with bootstrap and jquery):

const {environment} = require('@rails/webpacker')
const webpack = require('webpack')

module.exports = environment

environment.plugins.prepend(
    'Provide',
    new webpack.ProvidePlugin({
        $: 'jquery',
        jQuery: 'jquery',
        jquery: 'jquery',
        'window.jQuery': 'jquery',
        "window.$": "jquery",
        Popper: ['popper.js', 'default'],
        Rails: ['@rails/ujs']
    })
)

Upvotes: 5

Related Questions