在 JS 模块中使用 Rails-UJS(带有 webpacker 的 Rails 6)

Posted

技术标签:

【中文标题】在 JS 模块中使用 Rails-UJS(带有 webpacker 的 Rails 6)【英文标题】:Using Rails-UJS in JS modules (Rails 6 with webpacker) 【发布时间】:2019-10-01 08:16:29 【问题描述】:

我刚刚切换到 Rails 6 (6.0.0.rc1),它默认使用 Webpacker gem 和 Rails-UJS 一起用于 javascript 资产。我想在我的一些模块中使用 Rails UJS,以便从函数提交表单:

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

在安装了 Webpacker 的旧 Rails 版本中,Rails 引用似乎在我的模块中“全局”可用,但现在我在调用 Rails.fire 时得到了这个...

ReferenceError: Rails is not defined

如何使@rails/ujs 中的Rails 可用于特定模块或我的所有模块?

在我的设置之下……

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"

谢谢!

【问题讨论】:

【参考方案1】:

在我的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" 有什么缺点吗?如果没有,我会将此标记为已接受的答案。 没有缺点——事实上,我想说这正是我们应该在模块化 ES6 世界中声明每个文件的依赖项的方式。 Webpack 将只加载一次@rails/ujs,无论它被导入多少次。每个控制器都简单地获得 Rails 的相同导出,即使您使用不同的名称导入它,或者使用 CommonJS require() 而不是 ES6 import,这仍然是正确的。请注意只给.start() 打一次电话,可能是在您的application.js 中。 我会看到@ThienSuBS 的回答并在此处阅读文档:webpack.js.org/plugins/provide-plugin。我有同样的问题;我不想在我使用的每个文件中都导入 Rails,而 webpack 插件支持正是针对这种情况构建的:“自动加载模块,而不必在任何地方导入或要求它们。” @Dan L:依赖全局变量是最臭名昭著的编程反模式之一,所以不,我不支持ProvidePlugin,而是专门支持导入依赖项。编写依赖于全局运行时状态的模块是人们一开始就陷入这种混乱的方式。考虑到问题是关于编写模块,更是如此。依赖于全局状态的模块不是……模块化的。 @inopinatus:我理解对全球各地的关注,并且普遍同意。我对Rails 很满意,特别是在全局空间中,因为它是一个实用程序库,可以抽象出我一直想使用的常见功能(如 AJAX 请求)。我认为它类似于 jQuery;我不想在每个 JS 文件上都导入 jQuery,因为 jQuery 是我用来构建真实应用程序代码的工具。也就是说,避免像你提到的那样使用全局变量有很多智慧。【参考方案2】:

首先,使用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;

然后: 这是结果->

【讨论】:

【参考方案3】:

只需将其添加到您的 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']
    )
)

【讨论】:

我已经做到了。 Webpack 哭了TS2304: Cannot find name 'Rails'. 你也是通过 package.json 添加的吗? npm 添加@rails/ujs 我很抱歉。让我澄清一下。我可以导入包,但使用全局变量Rails 会得到TS2304: Cannot find name 'Rails' 请参阅它以在 javascript 中获取 Rails 实例。 ***.com/a/58161486/4631412 @ThienSuBS 这是一个 - 我花了数周时间寻找答案。非常感谢【参考方案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 只是想弄清楚事情。 然后在您的刺激控制器中,您可以简单地执行以下操作:

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

只是在这里使用他们的小测试控制器,但我把它弄到了控制台。注销,你可以打电话给rails_ujs.fire,这应该是你想要的:)

让我知道这是否适合你!

【讨论】:

【参考方案5】:

我认为最好的方法是使用expose-loader 并像运行bundle exec rails webpacker:install:erb 时那样配置它。


安装expose-loader

$ yarn add expose-loader

创建配置文件

    对于加载器,webpacker 自己配置,它会在config/webpack/loaders 中转储一个配置对象。如果该文件夹不存在,则创建该文件夹。

    创建一个名为config/webpack/loaders/expose.js的文件

    将此添加到该文件:

    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 对象。

【讨论】:

以上是关于在 JS 模块中使用 Rails-UJS(带有 webpacker 的 Rails 6)的主要内容,如果未能解决你的问题,请参考以下文章

在异步 AWS Lambda 函数中使用带有 node-fetch 模块的 node.js 时遇到问题

您如何使用带有 require 和模块导出的 npm 包作为纯 JS 库

无法在nodejs中导入带有require()的模块

我可以使用带有反应的节点模块吗

在带有属性的ES6模块上使用Closure Compiler

路径必须是一个字符串(需要带有node.js http模块的url)