如何为 Ruby On Rails 编写 JavaScript?

问题描述

假设我有一个包含以下页面的网站:Home About Friends

我想要做的是创建一个简单的 JavaScript 文件(称为 test.js),因此当用户按下按钮时,我可以做一些简单的事情,例如:

function myFunction(name) {
  console.log("Name = " + name);
}

现在是我提出问题的部分。我注意到 ruby​​ 使用 project_name/app/views/layout/application.html.erb 并为所有三个页面<head>HomeAbout)提供相同的 Friends

然而,这不是我想在这里做的!我只希望 test.jsFriends 页面工作!

我是否在我的 project_name/app/views/home/friends.html.erb 中引用了它,如下所示:

<!-- At the bottom of friends.html.erb -->
<script type="text/javascript" src="directory-to-javascript-file"></script>

我也注意到在这个目录 project_name/app/javascript 下有一个 JavaScript 文件夹,里面有两个文件夹; channelspacks

这是我要添加我的 test.js 的位置吗?如果是,在哪个文件夹中?


总结:

  • 我必须将 test.js 文件保存在哪个文件夹中?
  • 在哪里引用 test.js 以便它只能被 Friends 页面看到?

解决方法

您有 2 种选择来放置 js,资产管道或 webpack。 资产管道可能更容易理解, 在 app/assets/javascripts/application.js 中,您可以简单地将您的测试函数放在所有 require 语句之后(或将其解压缩到一个独立文件并要求它)。确保您的 application.html.erb 有一个 <%= javascript_include_tag 'application'%>

你不需要在friends.html.erb中放置脚本标签,删除它,让布局和资产管道/webpacker为你处理

第二个选项是使用 webpacker。有很多关于如何在 Rails 中使用它的资源,最终熟悉它可能会更好,我只是想对于初学者来说,旧的资产管道可能会更容易一些; webpack 过程非常相似,所以一旦你理解了一个,你就可以轻松地转移到另一个。

在哪里引用 test.js 以便它只能被好友页面看到?

您可以为朋友控制器创建新布局。

class FriendsController < ApplicationController
  layout "friends"
end

所以在你的布局目录中创建一个friends.html.erb。在friends.html.erb 中使用<%= javascript_include_tag 'friends' %>。接下来为朋友创建一个清单(或包)文件: app/assets/javascripts/friends.js 并要求您在那里需要的任何东西。这意味着朋友控制器将使用自己单独的 javascript 获得自己的布局。

,

我认为这就是您要找的。​​p>

您提到您在 pack 中有目录 app/javascript,看来您正在使用 web packer 来处理 js 和 css。

所以这是适合您的解决方案。

test.js 目录中创建一个 pack

并在您在问题中提到的 project_name/app/views/home/friends.html.erb 中写下以下内容。

<%= javascript_pack_tag 'test' %>

并将所有 js 代码写入 test.js 文件。并且只会加载到提到的页面中。

如果您想与 homeabout 等其他区域相同。为此创建 js 文件并将 <%= javascript_pack_tag 'js file name' %> 包含到适当的 html.erb 文件中。

这将帮助您对每个模块的视图具有相同的布局,但仍然有自己的 uniq js,仅需要特定视图。

您可能需要重新启动 rails 服务器。

希望我的回答有帮助。

,

在 Rails 中,你真的想忘记“我只想在这个页面上加载这个 JS 文件”的概念——我认为这是一种反模式,会阻碍你。

Rails 使用 an assets pipeline / webpacker 来简化您的 JS 交付,使用 turbolinks 来“ajaxify”旧版本中的常规页面加载,Rails UJS 做了很多 ajax 技巧。这意味着您很可能会处理 ajax 和持久的浏览器会话,这会使这个想法成为一个真正的问题,因为您的 JS 会泄漏到您最初没有想到的上下文中,或者除非您重新加载整个页面,否则将无法正常工作.

而是从 event based programming 和行为的角度考虑,您可以用它们来增强 UI 元素。想想“我希望这种按钮做 X”而不是“我希望这个页面上的这个特定的东西做 X”。使用事件处理程序和 css 类来实现该目标:

document.addEventListener("click",(event) => { 
  if (!event.target.matches('.magic-button')) return;
  console.log('You clicked a magical button!');
});

这使您的 JS 与页面本身分离并可重用,实际上使您的初始问题没有实际意义。如果您不想在其他页面上进行操作,请不要添加 magic-button 类。

如果您真的想执行“每页”javascript,我已使用一种方法将 controller_nameaction_name 附加到 body 元素:

<body data-controller="<%= controller_name" %>" data-action="<%= action_name %>">

这让您只需读取数据属性即可知道您在应用程序中的位置:

// given controller = foos and action = bar the events are:
// * MyApp:loaded
// * MyApp:foos#bar
// * MyApp:foos
// * MyApp:#bar
const firePageEvents = (event) => {
  // read the data attributes off the body element
  const body = document.querySelector('body');
  const details = {
    action: body.dataset.action,controller: body.dataset.controller
  };
  const eventNames = [
      "MyApp:loaded",`MyApp:${controller}#${action}`,`MyApp:${controller}`,`MyApp:#${action}`
  ];
  // fire the events
  eventNames.forEach((name) => {
     let event = new CustomEvent(name,{ detail: details });
     document.dispatch(event);
  });
}

// fires the events either when turbolinks replaces the page
// or when the DOM is ready
if (window.Turbolinks && Turbolinks.supported) {
  document.addEventListener("turbolinks:load",(event) => { 
    firePageEvents(event);
  });
} else {
  document.addEventListener("DOMContentLoaded",(event) => { 
    firePageEvents(event);
  });
}
// this will be trigged when any page is loaded
document.addEventListener("MyApp:loaded",(event) => {
  switch(event.details.action) {
    case 'about':
      console.log('about page loaded');
      break;
    case 'friends':
      console.log('friends page loaded');
      break;
  }
});

// this will be trigged when the an action named "about" is rendered:
document.addEventListener("MyApp:#about",(event) => {
  console.log('About page loaded',event.details);
});

// this will be triggered on the users load page
document.addEventListener("MyApp:users#index",(event) => {
  console.log('User#index page loaded',event.details);
});

然后我们fire custom events您可以附加事件侦听器以使 JS 为特定控制器/动作触发。

此代码属于您的资产管道/包,并在其中有效地连接和交付。