在Rails 3应用程序中添加页面特定JavaScript的最佳方法?


159

Rails 3具有一些不引人注目的JavaScript,这非常酷。

但是我想知道最好的方法是为特定页面包含其他JavaScript。

例如,我以前可能做过的地方:

<%= f.radio_button :rating, 'positive', :onclick => "$('some_div').show();" %>

我们现在可以通过类似

<%= f.radio_button :rating, 'positive' %>

# then in some other file
$('user_rating_positive').click(function() {
  $('some_div').show();
}

所以我想我的问题是在哪里/如何包含该JavaScript?我不想填写application.js文件,因为此JavaScript仅适用于该视图。我应该以某种方式为每个页面包含一个自定义JavaScript文件,还是将其粘贴到标题所要查找的实例变量中?



对于那些想要全面了解其工作原理和最佳方法的人,请阅读railsapps.github.io/rails-javascript-include-external.html。这是迄今为止我在该主题上看到的最好的文档。这不仅对Rails来说是一本好书,对从事Web开发的任何人来说都是一本好书。这就是为什么最好以Rails的方式做事。我将来肯定会使用Unholy Rails。哇。
DutGRIFF 2014年

Answers:


153

我想做的是将每个视图的Javascript包含在一个content_for :head块中,然后yield将其包含在您的应用程序布局中。例如

如果很短,则:

<% content_for :head do %>
  <script type="text/javascript">
    $(function() {
      $('user_rating_positve').click(function() {
        $('some_div').show();
      }
    });
  </script>
<% end %>

或者,如果更长,则:

<% content_for :head do %>
  <script type="text/javascript">
    <%= render :partial => "my_view_javascript"
  </script>
<% end %>

然后,在您的布局文件中

<head>
  ...
  <%= yield :head %>
</head>

40
为什么不使用<%= javascript_include_tag "my_javascipt_file" %>
AJP

5
@AJP我想这会破坏资产流水线的目的
lulalala 2012年

2
@lulalala,但是答案中的代码不是以一种更凌乱的方式吗?
AJP 2012年

8
@AJP比较麻烦,但是发出的http请求比您的答案少一个。
lulalala 2012年


103

如果您只想在页面上包含javascript,则当然可以将其内联地包含在页面中,但是,如果您想对javascript进行分组并利用资产管道,缩小的js等优势,则可以这样做并拥有额外的资源通过将js分为仅适用于网站的某些控制器/视图/部分的组来组合并仅加载到特定页面上的js资产。

将资产中的js移动到文件夹中,每个文件夹都有一个单独的清单文件,因此,如果您有仅在后端使用的admin js库,则可以执行以下操作:

  • 资产
    • JavaScripts
      • 管理员
        • ... js
      • admin.js(用于管理组的清单)
      • application.js(适用于应用程序全局组的清单)
      • 全球
        • ... js

在现有的application.js中

//= require jquery
//= require jquery_ujs
//= require_tree ./global // requires all js files in global folder

在新的admin.js清单文件中

//= require_tree ./admin // requires all js files in admin folder

通过编辑config / production.rb确保此新的js清单已加载

config.assets.precompile += %w( admin.js )

然后调整页面布局,以便为页面标题添加一些额外的js:

<%= content_for :header %>   

然后在要包含此特定js组(以及普通应用程序组)和/或任何特定于页面的js,css等的视图中:

<% content_for :header do %>
  <%= javascript_include_tag 'admin' %>  
<% end %>

当然,您可以使用css执行相同的操作,并以类似的方式将其分组以仅应用于网站的某些区域。


如果将所有清单保留在同一位置,则可以告诉Rails预编译该文件夹中的所有内容。这样,您只需要编辑一次production.rb,而不必跟踪添加到它的内容。
分心2012年

不知道您可以这样做-这样您可以拥有一个“清单”文件夹,其中包含您的清单以及各个js组?我可以尝试一下。我确实希望它们在默认情况下有一个更合理的设置,因为似乎他们假设您只是将所有js放入一个导入到一个大文件中。
肯尼·格兰特

奇怪的是,如果使用//= require_tree ./global,则会出现Sprockets :: FileNotFound错误,但如果//= require_directory ./global使用Rails 3.2.12 则不会。
2013年

2
看来您最终将获得许多脚本标签。我以为资产管道的目标是只有一个脚本标签?我只写JavaScript,以便它们是特定于页面的。
Ziggy

1
我觉得我明白了。但是,避免使用其他脚本标签对于页面加载时间非常重要。
Ziggy

12

这些答案帮助了我很多!如果有人想要更多...

  1. 如果您希望对JavaScript进行预编译,则需要将它们放入清单中。但是,如果您需要其中的每个javascript文件,application.js.coffee那么每次您导航到另一个页面时,所有javacsripts都将被加载,并且无法实现使用特定于页面的javascript的目的。

因此,您需要创建自己的清单文件(例如speciifc.js),该清单文件将需要所有页面特定的javascript文件。另外,require_treeapplication.js

app / assets / javascripts / application.js

//= require jquery
//= require jquery_ujs
//= require_tree ./global

app / assets / javascripts / specific.js

//= require_tree ./specific

然后在您environments/production.rb的清单中使用config选项将此清单添加到预编译列表中,

config.assets.precompile += %w( specific.js )

做完了!所有应始终加载的共享 javascript都将放在app/assets/javascripts/global文件夹中,而页面专用javascript会放在中app/assets/javascripts/specific。您可以简单地从视图中调用特定于页面的javascript,例如

<%= javascript_include_tag "specific/whatever.js" %> //.js是可选的。

这足够了,但是我也想利用它javascript_include_tag params[:controller]。当您创建控制器时,会app/assets/javascripts像提到的其他人一样生成关联的coffeescript文件。确实有特定于控制器的 javascript,仅当用户到达特定的控制器视图时才加载。

所以我创建了另一个清单 controller-specific.js

app / assets / javascripts / controller-specific.js

//= require_directory .

这将包括与控制器关联的所有自动生成的咖啡脚本。另外,您需要将其添加到预编译列表中。

config.assets.precompile += %w( specific.js controller-specific.js )


非常感谢您提供详细的答案。我应该把这条线放在哪里 javascript_include_tag params[:controller]。目前,我的application.html.erb有这一行javascript_include_tag 'application'。我应该用另一行替换吗?
simha 2013年

1
另外,我应该把这行<%= javascript_include_tag "specific/whatever.js" %>放在一个content_for :header块吗?
simha 2013年

1
我很奇怪,你需要惹麻烦config.assets.precompile。难道不是所有内容都会app/assets/javascripts/*.js自动预编译吗?
AlexChaffee 2014年

@AlexChaffee只有自动可用的文件是application.cssapplication.js。其他文件必须包含在其他文件中,或使用列出config.assets.precompile在此处阅读更多内容
成祖2014年

清单声明已移动。我正在运行Rails 4.2.0。发现在production.rb文件此评论:#config.assets.precompileconfig.assets.version已经搬到配置/初始化/ assets.rb
生硬

11

我更喜欢以下...

在您的application_helper.rb文件中

def include_javascript (file)
    s = " <script type=\"text/javascript\">" + render(:file => file) + "</script>"
    content_for(:head, raw(s))
end

然后在您的特定视图中(在此示例中为app / views / books / index.html.erb)

<% include_javascript 'books/index.js' %>

...似乎为我工作。


9

如果您不想使用资产管道或复杂的解决方法来获取必要的页面特定javascript(我表示同情),这是最简单,最可靠的方法,该方法与上述答案相同,但是用更少的代码即可用:

<%= javascript_include_tag "my_javascipt_file" %>

注意:与包含的答案相比,每个include标记确实需要一个http请求 content_for :head


javascript_include_tag正是我一直在寻找的东西,给AJP喝彩
Tom McKenzie

您将“ my_javascipt_file”放在哪里?在资产/ javascript中?但是,如果是这样,不是//= require .在application.js中使用a来自动获取吗?
2013年

@Linus是的,//= require .它将把该脚本带到您网站上的任何页面,因此您必须执行Kenny Grant(使用//= require_tree ./global)或建议的bjg之类的操作。
AJP 2013年

1
对于我来说,这是迄今为止最简单的方法,我将其更改//= require_tree .//= require_directory .application.js,然后在assets \ javascripts中创建了一个子目录。请注意,您必须在config \ initializers \ assets.rb中包括新目录,以通过添加以下行来预编译js:Rails.application.config.assets.precompile += %w( new_dir/js_file.js )
Jonesy


3

我的理解是,资产管道旨在通过将所有js合并到一个(最小化)文件中来减少页面加载时间。虽然从表面上看这似乎令人反感,但实际上它是流行语言(例如C和Ruby)中已经存在的功能。诸如“ include”标签之类的东西旨在防止文件的多次包含,并帮助程序员组织其代码。当您使用C编写和编译程序时,所有代码都存在于正在运行的程序的每个部分中,但是方法仅在使用该代码时才加载到内存中。从某种意义上说,已编译程序不包含任何保证代码完全模块化的内容。通过以这种方式编写程序,我们使代码模块化,并且操作系统仅将给定位置所需的对象和方法加载到内存中。甚至还有“方法特定的包含”之类的东西吗?如果您的Rails应用程序很安静,那么这实际上就是您要的内容。

如果您编写JavaScript来增强页面上HTML元素的行为,则这些功能在设计上是“特定于页面的”。如果您以某种方式编写了一些复杂的代码,无论其上下文如何,该代码都将执行,那么无论如何,可以考虑将该代码绑定到html元素(您可以使用body标签,如Garber-Irish方法所述)。如果一个函数有条件地执行,则性能可能会比所有这些额外的脚本标记小。

我正在考虑使用Paloma gem,如rails apps项目中所述。然后,您可以通过在paloma回调中包含特定于页面的功能,来使您的javascript特定于页面:

Paloma.callbacks['users']['new'] = function(params){
    // This will only run after executing users/new action
    alert('Hello New Sexy User');
}; 

您使用滑轨,所以我知道您喜欢宝石:)


3

您不应该在资产管道之外加载JS或CSS文件,因为您会失去使Rails如此出色的重要功能。而且您不需要其他宝石。我相信使用尽可能少的宝石,这里没有必要使用宝石。

您想要的就是所谓的“控制器特定的Javascript”(“操作特定的Javascript包含在底部”)。这使您可以为特定的CONTROLLER加载特定的JavaScript文件。尝试将Javascript连接到View是一种。向后,并且不遵循MVC设计模式,您希望将其与Controller或Controller内部的动作相关联。

不幸的是,无论出于何种原因,Rails开发人员都决定默认情况下,每个页面都将加载资产目录中的每个JS文件。为什么他们决定这样做而不是默认情况下启用“特定于控制器的Javascript”,我永远不会知道。这是通过application.js文件完成的,该文件默认包含以下代码行:

//= require_tree .

这称为指令。这就是链轮用来加载asset / javascripts目录中的每个JS文件的方式。默认情况下,链轮会自动加载application.js和application.css,而require_tree指令将加载其各自目录中的每个JS和Coffee文件。

注意:当使用脚手架时(如果您不是脚手架,现在是开始的好时机),Rails会自动为您生成该脚手架控制器的咖啡文件。如果您希望它生成标准的JS文件而不是咖啡文件,请删除Gemfile中默认启用的咖啡宝石,然后脚手架将创建JS文件。

好的,启用“特定于控制器的Javascript”的第一步是从application.js文件中删除require_tree代码,或者如果仍然需要全局JS文件,则将其更改为asset / javascripts目录中的文件夹。IE浏览器:

//= require_tree ./global

步骤2:进入config / initializers / assets.rb文件,并添加以下内容:

%w( controllerone controllertwo controllerthree ).each do |controller|
  Rails.application.config.assets.precompile += ["#{controller}.js", "#{controller}.css"]
end

插入所需的控制器名称。

第3步:用此替换您的application.html.erb文件中的javascript_include_tag(注意params [:controller]部分:

<%= javascript_include_tag 'application', params[:controller], 'data-turbolinks-track': 'reload' %>

重新启动服务器和中提琴!用脚手架生成的JS文件现在仅在调用该控制器时加载。

是否需要在控制器 IE / articles / new中的特定ACTION上加载特定的JS文件?而是这样做:

application.html.erb

<%= javascript_include_tag "#{controller_name}/#{action_name}" if AppName::Application.assets.find_asset("#{controller_name}/#{action_name}") %>

config / initializers / assets.rb

config.assets.precompile += %w(*/*)

然后在资产/ javascripts文件夹中添加一个与您的控制器同名的新文件夹,并将与您的动作同名的js文件放入其中。然后将其加载到该特定操作上。


1

好的,也许这就像有史以来最糟糕的工作,但是我创建了一个仅渲染出.js文件的控制器方法

控制者

def get_script
   render :file => 'app/assessts/javascripts/' + params[:name] + '.js'
end
def get_page
   @script = '/' + params[:script_name] + '.js?body=1'
   render page
end

视图

%script{:src => @script, :type => "text/javascript"}

如果由于某种原因我们不想这样做,请告诉我。


1

添加JS的首选方法是在页脚中,因此您可以这样做:

show.html.erb:

<% content_for :footer_js do %>
   This content will show up in the footer section
<% end %>

layouts / application.html.erb

<%= yield :footer_js %>
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.