据我了解,您的所有JavaScript都合并为1个文件。当Rails将//= require_tree .
添加到application.js
清单文件的底部时,默认情况下会执行此操作。
这听起来像是一个真正的生命保护程序,但我有点担心特定于页面的JavaScript代码。此代码是否在每个页面上执行?我想要的最后一件事是,只需要在1页上需要为每个页面实例化我的所有对象。
此外,代码是否也存在冲突的可能性?
或者您是否在页面底部放置一个小script
标记,该标记只调用执行该页面的javascript代码的方法?
那么你不再需要require.js吗?
由于
编辑:我很感谢所有答案......我认为他们并没有真正解决问题。其中一些是关于样式的,似乎没有关联...而其他人只是提到javascript_include_tag
...我知道存在(显然......)但看起来Rails 3.1的方式是前进的将所有JavaScript包装到1个文件中,而不是在每个页面底部加载单独的JavaScript。
我能提出的最佳解决方案是使用div
或id
es将class
标记中的某些功能包装起来。在JavaScript代码中,您只需检查页面上是否有id
或class
,如果是,则运行与之关联的JavaScript代码。这样,如果动态元素不在页面上,则JavaScript代码不会运行 - 即使它已包含在由Sprockets打包的大量application.js
文件中。
我的上述解决方案的好处是,如果100个页面中的8个页面中包含搜索框,则它将仅在这8个页面上运行。您也不必在网站的8个页面上包含相同的代码。事实上,您永远不必在任何地方都可以在您的网站上添加手动脚本标记。
我认为这是我问题的实际答案。
答案 0 :(得分:155)
Asset Pipeline文档建议如何执行特定于控制器的JS:
例如,如果生成了
ProjectsController
,则app/assets/javascripts/projects.js.coffee
处会有一个新文件,而app/assets/stylesheets/projects.css.scss
会有另一个新文件。您应该将任何JavaScript或CSS唯一的控件放在各自的资产文件中,因为这些文件只能通过<%= javascript_include_tag params[:controller] %>
或<%= stylesheet_link_tag params[:controller] %>
等行加载到这些控制器。
答案 1 :(得分:77)
对于特定于页面的j,您可以使用Garber-Irish solution。
所以你的Rails javascripts文件夹可能看起来像两个控制器 - 汽车和用户:
javascripts/
├── application.js
├── init.js
├── markup_based_js_execution
├── cars
│ ├── init .js
│ ├── index.js
│ └── ...
└── users
└── ...
javascripts将如下所示:
// application.js
//=
//= require init.js
//= require_tree cars
//= require_tree users
// init.js
SITENAME = new Object();
SITENAME.cars = new Object;
SITENAME.users = new Object;
SITENAME.common.init = function (){
// Your js code for all pages here
}
// cars/init.js
SITENAME.cars.init = function (){
// Your js code for the cars controller here
}
// cars/index.js
SITENAME.cars.index = function (){
// Your js code for the index method of the cars controller
}
和markup_based_js_execution将包含UTIL对象的代码,以及支持DOM的UTIL.init执行。
不要忘记将它放到你的布局文件中:
<body data-controller="<%= controller_name %>" data-action="<%= action_name %>">
我还认为最好使用类而不是data-*
属性,以获得更好的页面特定css。正如Jason Garber所提到的:特定于页面的CSS选择器可能变得非常笨拙(当您使用data-*
属性时)
我希望这会对你有所帮助。
答案 2 :(得分:65)
我看到你已回答了自己的问题,但这是另一种选择:
基本上,你假设是
//= require_tree .
是必需的。不是。随意删除它。在我目前的应用程序中,第一个我正在使用3.1.x老实说,我已经制作了三个不同的顶级JS文件。我的application.js
文件只有
//= require jquery
//= require jquery_ujs
//= require_directory .
//= require_directory ./api
//= require_directory ./admin
这样,我可以使用自己的顶级JS文件创建子目录,只包含我需要的内容。
关键是:
require_tree
- Rails可让您更改其所做的假设application.js
没有什么特别之处 - assets/javascript
子目录中的任何文件都可以包含//=
的预处理器指令希望能帮助并为ClosureCowboy的回答添加一些细节。
Sujal
答案 3 :(得分:40)
另一种选择:创建特定于页面或模型的文件,您可以在assets/javascripts/
文件夹中创建目录。
assets/javascripts/global/
assets/javascripts/cupcakes
assets/javascripts/something_else_specific
您的主application.js
清单文件可以配置为从global/
加载其文件。特定页面或页面组可以有自己的清单,从其自己的特定目录加载文件。 Sprockets会自动将application.js
加载的文件与特定于页面的文件组合在一起,从而使该解决方案能够正常运行。
此技术也可用于style_sheets/
。
答案 4 :(得分:23)
我很欣赏所有答案......我认为他们并没有真正解决问题。其中一些是关于样式的,似乎没有关联...而其他人只是提到javascript_include_tag
...我知道存在(显然......)但看起来Rails 3.1的方式是前进的将所有Javascript包装成1个文件,而不是在每个页面底部加载单独的Javascript。
我能提出的最佳解决方案是使用div
或id
es将class
标记中的某些功能包装起来。在javascript代码中。然后,您只需检查页面上是否id
或class
,如果是,则运行与之关联的javascript代码。这样,如果动态元素不在页面上,则javascript代码不会运行 - 即使它已包含在由Sprockets打包的大量application.js
文件中。
我的上述解决方案的好处是,如果100个页面中的8个页面中包含搜索框,则它将仅在这8个页面上运行。您也不必在网站的8个页面上包含相同的代码。实际上,您永远不必在任何地方再次在您的网站上包含手动脚本标记 - 除了可能预加载数据。
我认为这是我问题的实际答案。
答案 5 :(得分:16)
我意识到我迟到了这个派对,但是我想提出一个我最近一直在使用的解决方案。但是,我先提一下......
Rails 3.1 / 3.2方式(不,先生。我不喜欢它。)
请参阅:http://guides.rubyonrails.org/asset_pipeline.html#how-to-use-the-asset-pipeline
为了完整性,我在这个答案中包括以下内容,因为它不是一个不可行的解决方案......虽然我并不关心它。
“Rails Way”是面向控制器的解决方案,而不是像这个问题的原始作者那样面向视图。有各自的控制器命名的控制器特定的JS文件。所有这些文件都放在一个文件夹树中,默认情况下不包含在任何application.js require指令中。
要包含特定于控制器的代码,以下内容将添加到视图中。
<%= javascript_include_tag params[:controller] %>
我厌恶这个解决方案,但它就在那里而且很快。据推测,您可以将这些文件称为“people-index.js”和“people-show.js”,然后使用类似"#{params[:controller]}-index"
的内容来获取面向视图的解决方案。再次,快速修复,但它并不适合我。
我的数据属性方式
叫我疯了,但是当我部署时,我希望我的所有JS编译并缩小为application.js。我不想记得在整个地方都包含这些小的落后者文件。
我将所有的JS加载到一个紧凑的,即将成为浏览器的缓存文件中。如果我的application.js的某一部分需要在页面上触发,我让HTML告诉我,而不是Rails。
我使用名为data-jstags
的自定义数据属性,而不是将我的JS锁定到特定的元素ID或使用标记类乱丢我的HTML。
<input name="search" data-jstag="auto-suggest hint" />
在每个页面上,我使用 - 在此处插入首选JS库方法 - 以在DOM加载完成后运行代码。此引导代码执行以下操作:
data-jstag
所以说我在application.js中的某处定义了以下内容:
function my_autosuggest_init(element) {
/* Add events to watch input and make suggestions... */
}
function my_hint_init(element) {
/* Add events to show a hint on change/blur when blank... */
/* Yes, I know HTML 5 can do this natively with attributes. */
}
var JSTags = {
'auto-suggest': my_autosuggest_init,
'hint': my_hint_init
};
bootstrapping事件将对搜索输入应用my_autosuggest_init
和my_hint_init
函数,将其转换为输入,在用户键入时显示建议列表,并提供某种形式当输入空白且未聚焦时输入提示。
除非某些元素标有data-jstag="auto-suggest"
,否则自动建议代码永远不会触发。但是,它始终存在,缩小并最终缓存在我的application.js中,以便我在页面上需要它。
如果您需要将其他参数传递给标记的JS函数,则必须应用一些创造力。添加数据参数属性,提出某种参数语法,甚至使用混合方法。
即使我有一些看似特定于控制器的复杂工作流程,我也只是在我的lib文件夹中为它创建一个文件,将其打包到application.js中,并用“new-thing-wizard”标记它。当我的引导程序命中该标记时,我的漂亮,花哨的向导将被实例化并运行。它在需要时运行该控制器的视图,但不以其他方式耦合到控制器。事实上,如果我对我的向导进行编码,我可能能够在视图中提供所有配置数据,因此可以在以后为任何其他需要它的控制器重复使用我的向导。
无论如何,这就是我一直在实现特定于页面的JS的一段时间,它对于简单的网站设计和更复杂/丰富的应用程序都有很好的帮助。希望我在这里提出的两种解决方案之一,我的方式或Rails方式,对将来遇到这个问题的任何人都有帮助。
答案 6 :(得分:7)
很久以前就已经回答并接受了这个问题,但我根据其中一些答案以及我对Rails 3 +的经验提出了自己的解决方案。
资产管道很好。使用它。
首先,在application.js
文件中,删除//= require_tree.
然后在你的application_controller.rb
中创建一个辅助方法:
helper_method :javascript_include_view_js //Or something similar
def javascript_include_view_js
if FileTest.exists? "app/assets/javascripts/"+params[:controller]+"/"+params[:action]+".js.erb"
return '<script src="/assets/'+params[:controller]+'/'+params[:action]+'.js.erb" type="text/javascript"></script>'
end
end
然后在您的application.html.erb
布局文件中,在现有的javascript包含中添加新的帮助程序,前缀为raw
帮助程序:
<head>
<title>Your Application</title>
<%= stylesheet_link_tag "application", :media => "all" %>
<%= javascript_include_tag "application" %>
<%= raw javascript_include_view_js %>
</head>
Voila,现在您可以使用您在rails中的其他位置使用的相同文件结构轻松创建特定于视图的javascript。只需将文件粘贴到app/assets/:namespace/:controller/action.js.erb
!
希望能帮到别人!
答案 7 :(得分:6)
<%= javascript_include_tag params[:controller] %>
答案 8 :(得分:6)
也许你会发现pluggable_js gem是合适的解决方案。
答案 9 :(得分:6)
您可以在布局文件中添加此行(例如application.html.erb)以自动加载控制器特定的javascript文件(生成控制器时创建的文件):
<%= javascript_include_tag params[:controller] %>
您还可以添加一行以自动加载脚本文件。
<%= javascript_include_tag params[:controller] + "/" + params[:action] %>
只需将页面脚本放入以控制器名称命名的子目录中即可。在这些文件中,您可以使用= require包含其他脚本。 如果文件存在,创建一个帮助程序来包含文件会很好,以避免浏览器中的404失败。
答案 10 :(得分:5)
LoadJS宝石是另一种选择:
LoadJS提供了一种在Rails应用程序中加载特定于页面的Javascript代码的方法,而不会失去Sprockets提供的魔力。您的所有Javascript代码将在一个Javascript文件中继续缩小,但其中某些部分仅针对某些页面执行。
答案 11 :(得分:3)
在application.html.erb中:
<body class="<%=params[:controller].parameterize%>">
假设您的控制器名为Projects,那将生成:
<body class="projects">
然后在projects.js.coffee:
jQuery ->
if $('body.projects').length > 0
$('h1').click ->
alert 'you clicked on an h1 in Projects'
答案 12 :(得分:2)
我没有看到真正把它放在一起的答案并为你解决。因此,我将尝试将 meleyal , sujal (la ClosureCowboy )放在 Ryan的的第一部分回答,甚至 Gal的关于Backbone.js的大胆声明......所有这些都是以简短明了的方式结合在一起的。而且,谁知道,我甚至可以满足 Marnen Laibow-Koser的要求。
资产/ Javascript角/的的application.js 强>
//= require jquery
//= require jquery_ujs
//= require lodash.underscore.min
...
视图/布局/的 application.html.erb 强>
...
</footer>
<!-- Javascripts ================================================== -->
<!-- Placed at the end of the document so the pages load faster -->
<%= javascript_include_tag "application" %>
<%= yield :javascript %>
</body>
</html>
视图/富/的 index.html.erb 强>
...
<% content_for :javascript do %>
<%= javascript_include_tag params[:controller] %>
<% end %>
资产/ Javascript角/的 foo.js 强>
//= require moment
//= require_tree ./foostuff
资产/ Javascript角/ foostuff /的 foothis.js.coffee 强>
alert "Hello world!"
从 application.js 中删除//= require_tree .
,并仅列出每个页面共享的JS。
上面 application.html.erb 中显示的两行告诉页面包含application.js和特定于页面的JS的位置。
上面 index.html.erb 中显示的三行告诉您的视图查找某些特定于页面的JS并将其包含在名为“:javascript”的命名yield区域(或无论你想要什么名字)。在此示例中,控制器为“foo”,因此Rails将尝试在应用程序布局中的:javascript yield区域中包含“foo.js”。
在 foo.js (或控制器命名的任何内容)中列出特定于页面的JS。列出公共库,树,目录等等。
将您的自定义页面特定的JS放在某个地方,您可以从其他自定义JS中轻松引用它。在这个例子中,foo.js需要foostuff树,所以把你的自定义JS放在那里,例如 foothis.js.coffee 。
这里没有严格的规定。如果需要,可以随意移动东西,甚至可以创建各种布局的多个屈服区域。这只是向前迈出的第一步。 (鉴于我们使用Backbone.js,我不会完全这样做。我也可以选择将foo.js放到一个名为foo而不是foostuff的文件夹中,但尚未确定。)
< / LI>您可以使用CSS和<%= stylesheet_link_tag params[:controller] %>
执行类似的操作,但这超出了问题的范围。
如果我在这里错过了一个明显的最佳实践,请给我一个便条,我会适应。 Rails对我来说是相当新的,老实说,到目前为止,我对它在企业开发中带来的混乱以及平均Rails程序产生的所有流量都没有给人留下深刻的印象。
答案 13 :(得分:2)
我同意你的回答,检查选择器是否在那里,使用:
if ($(selector).length) {
// Put the function that does not need to be executed every page
}
(没有人看到有人添加实际的解决方案)
答案 14 :(得分:2)
您还可以将js分组到文件夹中,并根据页面继续使用资产管道load your javascript selectively。
答案 15 :(得分:2)
这就是我解决造型问题的方法:(原谅Haml)
%div{:id => "#{params[:controller].parameterize} #{params[:view]}"}
= yield
这样我就会启动所有特定于页面的 .css.sass 文件:
#post
/* Controller specific code here */
&#index
/* View specific code here */
&#new
&#edit
&#show
这样你就可以轻松避免任何冲突。 说到 .js.coffee 文件,你可以初始化像;
这样的元素$('#post > #edit') ->
$('form > h1').css('float', 'right')
希望这对一些人有所帮助。
答案 16 :(得分:2)
只有在告诉Rails(Sprockets,而不是)合并它们时,才会合并JavaScripts。
答案 17 :(得分:1)
Paloma项目提供了管理特定于页面的JavaScript代码的有趣方法。
他们的文档中的用法示例:
var UsersController = Paloma.controller('Users'); // Executes when Rails User#new is executed. UsersController.prototype.new = function(){ alert('Hello Sexy User!' ); };
答案 18 :(得分:1)
虽然你在这里有几个答案,但我认为你的编辑可能是最好的选择。我们在团队中使用的Gitlab设计模式是Dispatcher模式。它的功能类似于您所说的内容,但页面名称由rails标记在body标签中。例如,在您的布局文件中,只需包含类似(在HAML中)的内容:
%body{'data-page' => "#{controller}:#{action}" }
然后在javascripts文件夹中的dispatcher.js.coffee
文件中只有一个闭包和一个switch语句,如下所示:
$ ->
new Dispatcher()
class Dispatcher
constructor: ->
page = $('body').attr('data-page')
switch page
when 'products:index'
new Products()
when 'users:login'
new Login()
您需要在单个文件中执行的操作(例如products.js.coffee
或login.js.coffee
)将它们放在一个类中,然后将该类符号全局化,以便您可以在调度程序中访问它: / p>
class Products
constructor: ->
#do stuff
@Products = Products
Gitlab有几个这方面的例子,如果你很好奇,你可能想要解决这个问题:)
答案 19 :(得分:1)
将所有的commom JS文件移动到'app / assets / javascript / global'之类的子文件夹,然后在application.js中,将//= require_tree .
行修改为//= require_tree ./global
。
现在您可以自由地将特定于控制器的JS放在'app / assets / javascript /'根目录中,它们将不会包含在已编译的JS中,仅在您通过= javascript_include_tag
调用它们时使用控制器/图。
答案 20 :(得分:1)
特别是如果你使用像Backbone JS这样的东西 - 每个页面都有自己的Backbone视图。然后erb文件只有一行内联javascript,它会激活正确的主干视图类。我认为它只是一行'胶水代码',因此它的内联是可以的。优点是你可以保留你的“require_tree”,让浏览器缓存所有的javascript。
show.html.erb中的,你会有类似的东西:
<% provide :javascript do %>
<%= javascript_include_tag do %>
(new app.views.ProjectsView({el: 'body'})).render();
<% end %>
<% end do %>
并在您的布局文件中,您需要:
<%= yield :javascript %>
答案 21 :(得分:1)
我有另一个解决方案,虽然原始对我来说很好,并且不需要任何花哨的选择性加载策略。放入你的nornal文档就绪函数,然后测试当前的windows位置,看看它是你的javascript用途的页面:
$(document).ready(function() {
if(window.location.pathname.indexOf('/yourpage') != -1) {
// the javascript you want to execute
}
}
这仍允许rails 3.x在一个小包中加载所有js,但不会产生很多开销或与js不适合的页面发生任何冲突。
答案 22 :(得分:1)
步骤1。删除require_tree。在你的application.js和application.css中。
第二步。在布局文件夹中编辑application.html.erb(通过rails默认值)。添加&#34;参数[:控制器]&#34;在以下标签中。
<%= stylesheet_link_tag 'application', params[:controller], media: 'all', 'data-turbolinks-track' => true %>
<%= javascript_include_tag 'application', params[:controller], 'data-turbolinks-track' => true %>
步骤3。在config / initializers / assets.rb中添加文件
%w( controller_one controller_two controller_three ).each do |controller|
Rails.application.config.assets.precompile += ["#{controller}.js", "#{controller}.js.coffee", "#{controller}.css", "#{controller}.scss"]
end
的引用: http://theflyingdeveloper.com/controller-specific-assets-with-rails-4/
答案 23 :(得分:0)
我将一些答案合并到:
申请助手:
module ApplicationHelper
def js_page_specific_include
page_specific_js = params[:controller] + '_' + params[:action]
if Rails.application.assets.find_asset(page_specific_js).nil?
javascript_include_tag 'application', 'data-turbolinks-track' => true
else
javascript_include_tag 'application', page_specific_js, 'data-turbolinks-track' => true
end
end
end
布局/ application.html.haml:
<!DOCTYPE html>
%html{lang: 'uk'}
%head
= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true
bla-bla-bla
= js_page_specific_include
bla-bla-bla
答案 24 :(得分:0)
首先:从application.js中删除\\=require_tree
第二:所有的JS代码都必须在/app/assets/javascritpt
分配,所有的CSS代码必须在/app/assets/stylesheets
答案 25 :(得分:0)
我之前使用此方法做过:http://theflyingdeveloper.com/controller-specific-assets-with-rails-4/。超级简单,依靠控制器来选择要加载的正确js。
答案 26 :(得分:0)
我没有试过这个,但看起来如下:
如果你有一个javascript的content_for(例如,其中包含真正的javascript),sprockets就不会知道它,因此这将与现在的方式相同。
如果你想从javascript的大包中排除一个文件,你会进入config / sprockets.yml文件并相应地修改source_files。然后,您只需要包含您在需要时排除的任何文件。
答案 27 :(得分:-2)
在Ryan的带领下,这就是我所做的 -
application.js.coffee
$ ->
view_method_name = $("body").data("view") + "_onload"
eval("#{view_method_name}()") if eval("typeof #{view_method_name} == 'function'")
view_action_method_name = $("body").data("view") + "_"+$("body").data("action")+"_onload"
eval("#{view_action_method_name}()") if eval("typeof #{view_action_method_name} == 'function'")
users.js.coffee(控制器特定的coffeescript,例如controller:users,action:dashboard)
window.users_dashboard_onload = () ->
alert("controller action called")
window.users_onload = () ->
alert("controller called")
application.html.haml
%body{:data=>{:view=>controller.controller_name, :action=>controller.action_name}}
答案 28 :(得分:-3)
以下是如何执行此操作,尤其是如果您不必为特定页面执行大量库,而只需要或多或少地运行几百行JS。
由于将Javascript代码嵌入到HTML中非常好,只需在app / views shared.js目录下创建,并将页面/页面特定代码放在 my_cool_partial.html.erb
中<script type="text/javascript">
<!--
var your_code_goes_here = 0;
function etc() {
...
}
-->
</script>
所以,现在从你想要的任何地方做到:
= render :partial => 'shared.js/my_cool_partial'
就是这样,k?