Best way to add page specific JavaScript in a Rail

2019-01-01 09:52发布

Rails 3 has some unobtrusive JavaScript which is pretty cool.

But I was wondering what the best way is to include additional JavaScript for a particular page.

For example, where I might have previously done:

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

We can now make it unobtrusive with something like

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

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

So I guess my question is where/how to include that JavaScript? I don’t want to fill up the application.js file because this JavaScript is only applicable to this one view. Should I include a custom JavaScript file for each page somehow, or stick it in an instance variable that the header looks for?

10条回答
牵手、夕阳
2楼-- · 2019-01-01 10:04

I prefer the following...

In your application_helper.rb file

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

and then in your particular view (app/views/books/index.html.erb in this example)

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

... seems to work for me.

查看更多
与风俱净
3楼-- · 2019-01-01 10:07

My understanding is that the asset pipeline is meant to decrease page load-time by mashing all your js together into one (minified) file. While this may seem repugnant on the surface, it is actually a feature that already exists in popular languages like C and Ruby. Things like "include" tags are meant to prevent multiple inclusion of a file, and to help programmers organize their code. When you write and compile a program in C, all that code is present in every part of your running program, but methods are only loaded into memory when that code is being used. In a sense, a compiled program does not include anything to guarantee that the code is nicely modular. We make the code modular by writing our programs that way, and the operating system only loads into memory the objects and methods we need for a given locality. Is there even such a thing as "method-specific inclusion"? If your rails app is restful, this is essentially what you are asking for.

If you write your javascript so that it augments the behaviour of HTML elements on the page, then those functions are 'page-specific' by design. If there is some complicated code that you wrote in such a way that it will execute regardless of its context, maybe consider binding that code to an html element anyway (you could use the body tag, as described in the Garber-Irish method). If a function executes conditionally, the performance will probably be smaller than all those extra script tags.

I am thinking of using the paloma gem, as described in the rails apps project. Then you can make your javascript page-specific by including page-specific functions in a paloma callback:

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

You use rails, so I know you love gems :)

查看更多
萌妹纸的霸气范
4楼-- · 2019-01-01 10:08

If you want to include javascript just on one page, you can include it on the page inline of course, however if you want to group your javascript and take advantage of the asset pipeline, minified js etc, it's possible to do so and have extra js assets which are combined and only loaded on specific pages by splitting your js into groups which only apply in certain controllers/views/sections of the site.

Move your js in assets into folders, with a separate manifest file for each, so if you had an admin js library that is only used on the backend, you might do this:

  • assets
    • javascripts
      • admin
        • ...js
      • admin.js (manifest for admin group)
      • application.js (manifest for app global group)
      • global
        • ...js

in the existing application.js

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

in a new admin.js manifest file

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

Make sure this new js manifest is loaded by editing config/production.rb

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

Then adjust your page layout so that you can include some extra js for the page head:

<%= content_for :header %>   

Then in views where you want to include this specific js group (as well as the normal application group) and/or any page-specific js, css etc:

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

You can of course do the same thing with css and group it in a similar way for applying only to certain areas of the site.

查看更多
怪性笑人.
5楼-- · 2019-01-01 10:09

Take a look at pluggable_js gem. You may find this solution easier to use.

查看更多
明月照影归
6楼-- · 2019-01-01 10:14

The preferred way to add JS is in footer, so you can do this way:

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 %>
查看更多
泪湿衣
7楼-- · 2019-01-01 10:21

These answers helped me a ton! If anyone wants a little more...

  1. You need to put javascripts into manifests if you want them precompiled. However, if you require every javascript file from application.js.coffee then all the javacsripts will be loaded every time you navigate to a different page, and the purpose of doing page-specific javascripts will be defeated.

Therefore, you need to create your own manifest file (e.g. speciifc.js) that will require all the page-specific javascript files. Also, modify require_tree from application.js

app/assets/javascripts/application.js

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

app/assets/javascripts/specific.js

//= require_tree ./specific

Then in your environments/production.rb add this manifest to the precompiled list with the config option,

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

Done! All the shared javascripts that should always be loaded will be placed in app/assets/javascripts/global folder, and the page-spcific javascripts in app/assets/javascripts/specific. You can simply call the page-specific javascripts from the view like

<%= javascript_include_tag "specific/whatever.js" %> //.js is optional.

This is sufficient, but I wanted to make a use of javascript_include_tag params[:controller] too. When you create controllers, an associated coffeescript file is generated in app/assets/javascripts like other people mentioned. There are truly controller-specific javascripts, which are loaded only when the user reaches the specific controller view.

So I created another manifest controller-specific.js

app/assets/javascripts/controller-specific.js

//= require_directory .

This will include all the automatically-generated coffeescripts associated with controllers. Also, you need to add it to the precompiled list.

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

查看更多
登录 后发表回答