【问题标题】:Rails 6 Webpacker calling javascript function from Rails viewRails 6 Webpacker 从 Rails 视图调用 javascript 函数
【发布时间】:2020-08-29 19:22:24
【问题描述】:

我在使用 Webpacker 的 Rails 6 应用程序中具有以下 Javascript 结构。

app/javascript
  + packs
    - application.js
  + custom
    - hello.js

下图是上述JS文件中的内容

app/javascript/custom/hello.js

  export function greet(name) {
    console.log("Hello, " + name);
  }

app/javascript/packs/application.js

  require("@rails/ujs").start()
  require("jquery")
  require("bootstrap")

  import greet from '../custom/hello'

config/webpack/environment.js

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

const webpack = require('webpack')
environment.plugins.prepend('Provide',
  new webpack.ProvidePlugin({
    $: 'jquery',
    jQuery: 'jquery',
    Popper: ['popper.js', 'default']
  })
)

module.exports = environment

现在在我的 Rails 视图中,我正在尝试使用导入的函数 greet,如下所示

app/views/welcome/index.html.haml

  - name = 'Jignesh'

  :javascript

    var name = "#{name}"
    greet(name)

当我加载视图时,我在浏览器的控制台中看到 ReferenceError: greet is not defined 错误。

我试图寻找解决这个问题的方法,并在网上找到了许多资源,但没有一个能帮助我。最后,当我在建议中起草这个问题时,我发现 How to execute custom javascript functions in Rails 6 确实接近我的需要许多视图需要将数据从 Rails 视图传递到 JS 函数,以便将自定义文件移动到 app/javascript/custom 文件夹下。

如果有人能帮助我了解我遇到的ReferenceError 背后的原因,我将不胜感激。

注意

我不精通 Node 领域的 Javascript 开发,并且对 Webpacker、Webpack、Javascript 的模块、importexportrequire 语法等也很陌生,所以如果你发现任何愚蠢的东西,请多多包涵我在问什么。在尝试升级现有 Rails 应用程序以使用版本 6 时,我遇到了上述情况。

【问题讨论】:

    标签: javascript ruby-on-rails-6 webpacker


    【解决方案1】:

    默认情况下,Webpack 不会使模块在全局范围内可用。也就是说,有几种方法可以让您在 AJAX 请求之外将信息从 Ruby 传递到 JavaScript:

    1. window.greet = function() { ... } 并按照您的建议从视图中调用该函数是一种选择。我不喜欢在很多地方编写副作用代码,所以这是我最不喜欢的。

    2. 您可以使用expose-loader 查看。这意味着自定义您的 webpack 配置以将选定模块中的选定函数“公开”到全局范围。它可以在少数情况下很好地工作,但在许多用例中会变得乏味。

    3. 从入口点导出选定的函数并将 webpack 配置为打包 your bundle as a library。如果您更喜欢从视图中调用全局函数,这是我最喜欢的方法。 I've written about this approach specifically for Webpacker on my blog.

      // app/javascript/packs/application.js
      
      export * from '../myGlobalFunctions'
      
      
      // config/webpack/environment.js
      
      environment.config.merge({
        output: {
          // Makes exports from entry packs available to global scope, e.g.
          // Packs.application.myFunction
          library: ['Packs', '[name]'],
          libraryTarget: 'var'
        },
      })
      
      // app/views/welcome/index.html.haml
      
      :javascript
        Packs.application.greet("#{name}")
      
    4. 完全采用不同的方法,将 Ruby 变量附加到控制器中的全局对象,例如使用 gon gem。假设您按照说明设置 gem,gon 对象既可以作为 Ruby 对象使用,您可以在服务器端进行变异,也可以在 JavaScript 代码中作为全局变量读取。您可能需要想出一些其他方法来选择性地调用 greet 函数,例如对仅在给定页面或给定 url 呈现的特定选择器进行 DOM 查询。

      # welcome_controller.rb
      
      def index
        gon.name = 'My name'
      end
      
      // app/javascript/someInitializer.js
      
      window.addEventListener('DOMContentLoaded', function() {
        if (window.location.match(/posts/)) {
          greet(window.gon.name)
        }
      })
      

    【讨论】:

      【解决方案2】:

      @rossta 非常感谢您的详尽回答。它绝对应该对这篇文章的查看者有很大帮助

      我在寻找问题的解决方案时发现的您的第一个建议,我确实在我的问题中引用了它。和你一样,我也不喜欢它,因为它是一种解决方法。

      老实说,您的第二和第三条建议让我印象深刻,也许是因为我是 Webpack 概念的新手。

      您的第 4 种方法对我来说听起来更实用,事实上,在昨天发布我的问题之后,我尝试了一些类似的方法并且确实有效。我在下面分享解决方案以供参考

      app/javascript/custom/hello.js

        function greet(name) {
          console.log("Hello, " + name)
        }
      
        export { greet }
      

      app/javascript/packs/application.js

        require("@rails/ujs").start()
        require("bootstrap")
      

      请注意,在上面的文件中,我删除了require("jquery")。那是因为它已经在/config/webpack/environment.jsProvidePlugin 中全球可用(请参阅我的问题中的代码)。因此,不需要在此文件中要求它们。我在浏览时发现了这一点 http://blog.blackninjadojo.com/ruby/rails/2019/03/01/webpack-webpacker-and-modules-oh-my-how-to-add-javascript-to-ruby-on-rails.html中的“选项 4:向 environment.js 添加 Javascript”

      app/views/welcome/index.html.haml

        - first_name = 'Jignesh'
        - last_name = 'Gohel'
      
        = hidden_field_tag('name', nil, "data": { firstName: first_name, lastName: last_name }.to_json)
      

      注意:“数据”属性的想法来自https://github.com/rails/webpacker/blob/master/docs/props.md

      app/javascript/custom/welcome_page.js

        import { greet } from './hello'
      
        function nameField() {
          return $('#name')
        }
      
        function greetUser() {
          var nameData = nameField().attr('data')
      
          //console.log(nameData)
          //console.log(typeof(nameData))
      
          var nameJson = $.parseJSON(nameData)
      
          var name = nameJson.firstName + nameJson.lastName
      
          greet(name)
        }
      
        export { greetUser }
      

      app/javascript/packs/welcome.js

        import { greetUser } from '../custom/welcome_page'
      
        greetUser()
      

      注意:我在浏览https://blog.capsens.eu/how-to-write-javascript-in-rails-6-webpacker-yarn-and-sprockets-cdf990387463 时发现的单独包装的想法 在“不要像使用 Sprockets 一样尝试使用 Webpack!”部分下(引用段落以便快速查看)

      那么如何让一个按钮触发一个 JS 动作呢?从包中,您将行为添加到 HTML 元素。你可以使用 vanilla JS、JQuery、StimulusJS 来做到这一点。

      https://prathamesh.tech/2019/09/24/mastering-packs-in-webpacker/ 中的信息也帮助指导我解决问题。

      然后通过在底部添加以下内容来更新 app/views/welcome/index.html.haml

      = javascript_pack_tag("welcome")
      

      最后重新加载了页面,webpacker 编译了所有的包,我可以在控制台中看到带有视图中名称的问候语。

      我希望这可以帮助像我一样有类似需求的人。

      【讨论】:

      • 我也支持使用数据属性和从 DOM 中读取的想法;我本可以将它作为我列表中的第 5 项。
      猜你喜欢
      • 1970-01-01
      • 2020-08-20
      • 1970-01-01
      • 1970-01-01
      • 2020-01-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-07-01
      相关资源
      最近更新 更多