在JS模块中使用Rails-UJS(Rails 6与Webpacker)

41

我刚刚转换到了Rails 6 (6.0.0.rc1),默认使用Webpacker gem来处理JavaScript资产,与Rails-UJS一起使用。我想在一些模块中使用Rails UJS,以便通过函数提交表单:

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

在之前安装了Webpacker的Rails版本中,我的模块中似乎可以“全局”使用Rails引用,但现在调用Rails.fire时出现了这个问题...
ReferenceError: Rails is not defined

我应该如何使来自@rails/ujsRails对于我的特定模块或所有模块都可用?
以下是我的设置...

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"

谢谢!

5个回答

57

在我的app/javascript/packs/application.js文件中:

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

然后无论我在写什么模块、控制器、组件:

import Rails from '@rails/ujs';

谢谢你的回答,对我来说很简单有效。现在我正在从npm包中导入form_controller.js,以便在多个Rails应用程序中重复使用它。在不同的控制器中多次执行import Rails from "@rails/ujs"是否有任何缺点?如果没有,我会将其标记为已接受的答案。 - R4ttlesnake
4
实际上没有什么缺点 - 我会说这正是在模块化的ES6世界中宣告每个文件依赖关系的正确姿势。无论被导入多少次,Webpack只会加载一次@rails/ujs。每个控制器都简单地得到相同的Rails导出,即使你使用不同的名称导入它,或者使用CommonJS的require()而不是ES6的import。请注意,只需在application.js中调用一次.start() - inopinatus
我想看看@ThienSuBS的答案,并在这里阅读文档:https://webpack.js.org/plugins/provide-plugin/。我遇到了同样的问题;我不想在每个使用它的文件中导入Rails,而webpack插件支持正是为这种情况构建的:“自动加载模块,而不必在任何地方导入或要求它们。” - Dan L
4
@Dan L:依赖全局变量是最臭名昭著的编程反模式之一,因此,我不支持ProvidePlugin,而是明确支持导入依赖项。编写依赖于全局运行时状态的模块是人们陷入这种困境的原因之一。尤其是考虑到问题是关于编写模块的。依赖全局状态的模块并不具备模块化。 - inopinatus
@inopinatus:我理解到全局变量无处不在的问题并且一般来说也同意。我对Rails特别是在全局空间中使用比较满意,因为它是一个实用库,抽象了常见的功能(比如 AJAX 请求),而这些功能我需要一直使用。我认为它类似于 jQuery;我不想在每个 JS 文件中都导入 jQuery,因为 jQuery 是我用来构建真正应用代码的工具。话虽如此,像你所提到的避免全局变量还是有很多智慧的。 - Dan L

20

首先,使用yarn add rails/ujs:

yarn add  @rails/ujs

并将其添加到 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

配置并加载Rails的js。

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

然后: 这是结果 -> 我在Firefox控制台中输入Rails后的结果


5

只需将其添加到您的 environment.js 文件中,这是我的示例(包括 bootstrap 和 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']
    })
)

1
我已经做到了。Webpack 报错 TS2304: 找不到名称 'Rails' - RWDJ
你是否也通过 package.json 添加了它?npm add @rails/ujs - Andrew Cetinic
抱歉,让我澄清一下。我可以导入这个包,但是使用全局变量 Rails 会出现 TS2304: Cannot find name 'Rails' 的错误。 - RWDJ
然而,它对于JQuery起作用了,所以无论如何谢谢你。只是不适用于Rails。 - RWDJ
2
请查看以下链接,以获取在JavaScript中获取Rails实例的方法:https://dev59.com/TlMI5IYBdhLWcg3wfbbN#58161486 - ThienSuBS
1
@ThienSuBS 这就是答案 - 我花了几周时间寻找它。非常感谢。 - rico_mac

4

我目前在研究6.0.0.rc2版本,但我认为我可以为您提供答案。

如果您将以下文件分离出来:

app/javascript/packs/application.js

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

改为:

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

你可以明显地移除掉console.log语句,只是为了试图找出问题所在。 然后在你的stimulus控制器中,你可以简单地这样做:

// 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)
  }
}

这里只是使用他们的小测试控制器,但我让它console.log输出,并且您可以调用rails_ujs.fire,所以这应该是您想要的 :)

如果这对您有用,请告诉我!


1
我认为最好的方法是使用expose-loader,并将其配置为与运行bundle exec rails webpacker:install:erb时webpacker相同的方式。
安装 expose-loader
$ yarn add expose-loader

创建一个配置文件。
  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"}
    }
    

将该加载器添加到environment.js

将以下两行添加到config/webpack/environment.js中:

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

完整的文件应该看起来像这样:

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

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

那样可以再次全局访问 Rails 对象。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接