Rails 5/6:如何在 webpacker 中包含 JS 函数? [英] Rails 5/6: How to include JS functions with webpacker?

查看:16
本文介绍了Rails 5/6:如何在 webpacker 中包含 JS 函数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试将 Rails 3 应用程序更新到 Rails 6,但由于无法访问我的 Javascript 函数,我在使用现在默认的 webpacker 时遇到了问题.

I am trying to update a Rails 3 app to Rails 6 and I have problems with the now default webpacker since my Javascript functions are not accessible.

我得到:ReferenceError: Can't find variable: functionName for all js function triggers.

I get: ReferenceError: Can't find variable: functionName for all js function triggers.

我所做的是:

  • 在/app/javascript 中创建一个 app_directory
  • 将我的开发 javascript 文件复制到 app_directory 并将其重命名为 index.js
  • console.log('Hello World from Webpacker'); 添加到 index.js
  • 添加了 import "app_directory"; 到/app/javascript/packs/application.js
  • 添加到/config/initializers/content_security_policy.rb:

  • create an app_directory in /app/javascript
  • copied my development javascript file into the app_directory and renamed it to index.js
  • added console.log('Hello World from Webpacker'); to index.js
  • added import "app_directory"; to /app/javascript/packs/application.js
  • added to /config/initializers/content_security_policy.rb:

Rails.application.config.content_security_policy do |policy|
  policy.connect_src :self, :https, "http://localhost:3035", "ws://localhost:3035" if Rails.env.development?
end

我将'Hello World from Webpacker' 记录到控制台,但是当我尝试通过 <div id="x" onclick="functionX()"></div> 访问一个简单的 JS 函数时 在浏览器中出现参考错误.

I get 'Hello World from Webpacker' logged to console, but when trying to access a simple JS function through <div id="x" onclick="functionX()"></div> in the browser I get the reference error.

我知道资产管道已被 webpacker 取代,这对于包含模块应该很好,但是我应该如何包含简单的 JS 函数?我错过了什么?

I understand that the asset pipeline has been substituted by webpacker, which should be great for including modules, but how should I include simple JS functions? What am I missing?

提前致谢?

推荐答案

有关从旧的资产管道迁移到新的 webpacker 工作方式的说明,您可以在此处查看:

For instructions on moving from the old asset pipeline to the new webpacker way of doing things, you can see here:

https://www.calleerlandsson.com/replacing-sprockets-with-webpacker-for-javascript-in-rails-5-2/

这是在 Rails 5.2 中从资产管道迁移到 webpacker 的方法,它让您了解 Rails 6 中的情况有何不同,因为 webpacker 是 javascript 的默认设置.特别是:

This is a howto for moving from the asset pipeline to webpacker in Rails 5.2, and it gives you an idea of how things are different in Rails 6 now that webpacker is the default for javascript. In particular:

现在是时候将所有应用程序 JavaScript 代码从app/assets/javascripts/到 app/javascript/.

Now it’s time to move all of your application JavaScript code from app/assets/javascripts/ to app/javascript/.

要将它们包含在 JavaScript 包中,请确保在app/javascript/pack/application.js:

To include them in the JavaScript pack, make sure to require them in app/javascript/pack/application.js:

require('your_js_file')

因此,在 app/javascript/hello.js 中创建一个文件,如下所示:

So, create a file in app/javascript/hello.js like this:

console.log("Hello from hello.js");

然后,在 app/javascript/packs/application.js 中,添加这一行:

Then, in app/javascript/packs/application.js, add this line:

require("hello")

(注意不需要扩展)

现在,您可以在浏览器控制台打开的情况下加载一个页面,并看到您好!"控制台中的消息.只需在 app/javascript 目录中添加您需要的任何内容,或者最好创建子目录以保持您的代码井井有条.

Now, you can load up a page with the browser console open and see the "Hello!" message in the console. Just add whatever you need in the app/javascript directory, or better yet create subdirectories to keep your code organized.

更多信息:

这个问题被诅咒了.以前被接受的答案不仅错误而且错误得离谱,而且最高票的答案仍然是一英里的距离.

This question is cursed. The formerly accepted answer is not just wrong but grotesquely wrong, and the most upvoted answer is still missing the mark by a country mile.

上面的 anode84 仍在尝试以旧方式做事,如果您尝试这样做,webpacker 会妨碍您.当你转向 webpacker 时,你必须完全改变你做 javascript 的方式并考虑 javascript.没有范围问题".当您将代码放入网络包时,它是独立的,您可以使用导入/导出在文件之间共享代码.默认情况下,没有任何东西是全局的.

anode84 above is still trying to do things the old way, and webpacker will get in your way if you try that. You have to completely change the way you do javascript and think about javascript when you move to webpacker. There is no "scoping issue". When you put code in a web pack it's self-contained and you use import/export to share code between files. Nothing is global by default.

我明白为什么这令人沮丧.您可能和我一样,习惯于在 javascript 文件中声明一个函数,然后在 HTML 文件中调用它.或者只是在 HTML 文件的末尾添加一些 javascript.我从 1994 年开始做网络编程(不是打字错误),所以我看到一切都在多次演变.Javascript 已经发展.你必须学习新的做事方式.

I get why this is frustrating. You're probably like me, and accustomed to declaring a function in a javascript file and then calling it in your HTML file. Or just throwing some javascript at the end of your HTML file. I have been doing web programming since 1994 (not a typo), so I've seen everything evolve multiple times. Javascript has evolved. You have to learn the new way of doing things.

如果您想向表单或其他内容添加操作,您可以在 app/javascript 中创建一个文件来执行您想要的操作.要获取数据,您可以使用数据属性、隐藏字段等.如果该字段不存在,则代码不会运行.

If you want to add an action to a form or whatever, you can create a file in app/javascript that does what you want. To get data to it, you can use data attributes, hidden fields, etc. If the field doesn't exist, then the code doesn't run.

这是一个您可能会觉得有用的示例.如果表单具有 Google reCAPTCHA 并且用户在提交表单时未选中该框,我会使用它来显示弹出窗口:

Here's an example that you might find useful. I use this for showing a popup if a form has a Google reCAPTCHA and the user hasn't checked the box at the time of form submission:

// For any form, on submit find out if there's a recaptcha
// field on the form, and if so, make sure the recaptcha
// was completed before submission.
document.addEventListener("turbolinks:load", function() {
  document.querySelectorAll('form').forEach(function(form) {
    form.addEventListener('submit', function(event) {
      const response_field = document.getElementById('g-recaptcha-response');
      // This ensures that the response field is part of the form
      if (response_field && form.compareDocumentPosition(response_field) & 16) {
        if (response_field.value == '') {
          alert("Please verify that you are not a robot.");
          event.preventDefault();
          event.stopPropagation();
          return false;
        }
      }
    });
  });
});

请注意,这是自包含的.它不依赖任何其他模块,也没有其他任何模块依赖它.您只需将它放在您的包中,它就会监视所有表单提交.

Note that this is self-contained. It does not rely on any other modules and nothing else relies on it. You simply require it in your pack(s) and it will watch all form submissions.

这是在加载页面时加载带有 geojson 叠加层的 google 地图的另一个示例:

Here's one more example of loading a google map with a geojson overlay when the page is loaded:

document.addEventListener("turbolinks:load", function() {
  document.querySelectorAll('.shuttle-route-version-map').forEach(function(map_div) {
    let shuttle_route_version_id = map_div.dataset.shuttleRouteVersionId;
    let geojson_field = document.querySelector(`input[type=hidden][name="geojson[${shuttle_route_version_id}]"]`);

    var map = null;

    let center = {lat: 36.1638726, lng: -86.7742864};
    map = new google.maps.Map(map_div, {
      zoom: 15.18,
      center: center
    });

    map.data.addGeoJson(JSON.parse(geojson_field.value));

    var bounds = new google.maps.LatLngBounds();
    map.data.forEach(function(data_feature) {
      let geom = data_feature.getGeometry();
      geom.forEachLatLng(function(latlng) {
        bounds.extend(latlng);
      });
    });
    map.setCenter(bounds.getCenter());
    map.fitBounds(bounds); 
  });
});

当页面加载时,我查找类为shuttle-route-version-map"的 div.对于我找到的每一个,数据属性shuttleRouteVersionId"(data-shuttle-route-version-id)都包含路由的 ID.我将 geojson 存储在一个隐藏字段中,根据该 ID 可以轻松查询该字段,然后初始化地图,添加 geojson,然后根据该数据设置地图中心和边界.同样,它是独立的,除了 Google 地图功能.

When the page loads, I look for divs with the class "shuttle-route-version-map". For each one that I find, the data attribute "shuttleRouteVersionId" (data-shuttle-route-version-id) contains the ID of the route. I have stored the geojson in a hidden field that can be easily queried given that ID, and I then initialize the map, add the geojson, and then set the map center and bounds based on that data. Again, it's self-contained except for the Google Maps functionality.

您还可以学习如何使用导入/导出来共享代码,这真的很强大.

You can also learn how to use import/export to share code, and that's really powerful.

所以,还有一个展示了如何使用导入/导出.这是一段简单的代码,它设置了一个观察者"来观察你的位置:

So, one more that shows how to use import/export. Here's a simple piece of code that sets up a "watcher" to watch your location:

var driver_position_watch_id = null;

export const watch_position = function(logging_callback) {
  var last_timestamp = null;

  function success(pos) {
    if (pos.timestamp != last_timestamp) {
      logging_callback(pos);
    }
    last_timestamp = pos.timestamp;
  }

  function error(err) {
    console.log('Error: ' + err.code + ': ' + err.message);
    if (err.code == 3) {
      // timeout, let's try again in a second
      setTimeout(start_watching, 1000);
    }
  }

  let options = {
    enableHighAccuracy: true,
    timeout: 15000, 
    maximumAge: 14500
  };

  function start_watching() {
    if (driver_position_watch_id) stop_watching_position();
    driver_position_watch_id = navigator.geolocation.watchPosition(success, error, options);
    console.log("Start watching location updates: " + driver_position_watch_id);  
  }

  start_watching();
}

export const stop_watching_position = function() {
  if (driver_position_watch_id) {
    console.log("Stopped watching location updates: " + driver_position_watch_id);
    navigator.geolocation.clearWatch(driver_position_watch_id);
    driver_position_watch_id = null;
  }
}

导出两个函数:watch_position"和stop_watching_position".要使用它,您需要在另一个文件中导入这些函数.

That exports two functions: "watch_position" and "stop_watching_position". To use it, you import those functions in another file.

import { watch_position, stop_watching_position } from 'watch_location';

document.addEventListener("turbolinks:load", function() {
  let lat_input = document.getElementById('driver_location_check_latitude');
  let long_input = document.getElementById('driver_location_check_longitude');

  if (lat_input && long_input) {
    watch_position(function(pos) {
      lat_input.value = pos.coords.latitude;
      long_input.value = pos.coords.longitude;
    });
  }
});

页面加载时,我们会查找名为driver_location_check_latitude"和driver_location_check_longitude"的字段.如果它们存在,我们会设置一个带有回调的观察者,当它们发生变化时,回调会用纬度和经度填充这些字段.这是如何在模块之间共享代码.

When the page loads, we look for fields called "driver_location_check_latitude" and "driver_location_check_longitude". If they exist, we set up a watcher with a callback, and the callback fills in those fields with the latitude and longitude when they change. This is how to share code between modules.

同样,这是一种非常的做事方式.如果模块化和组织得当,您的代码会更清晰、更可预测.

So, again, this is a very different way of doing things. Your code is cleaner and more predictable when modularized and organized properly.

这是未来,因此与之抗争(并设置window.function_name"就是与之抗争)将一事无成.

This is the future, so fighting it (and setting "window.function_name" is fighting it) will get you nowhere.

这篇关于Rails 5/6:如何在 webpacker 中包含 JS 函数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆