rails including javascript or files only on certain pages - javascript

Ok Im new to rails in general and these default loaders and cache loaders an all that stuff they make some sense to me. But my question is. If I want to include a certain JS file or 2 or specific script on a particular page how would I do that.
with rails I have
app/views/layouts/application.html.erb in this file I gather is the base template for my site/service, and in there I would put all the moving parts. However I have an occasional need where I only need a javascript file loaded on a particular page. But it needs to be called after the jquery file so jquery is loaded into memory prior to the file I want loaded. So Im not exactly sure how I would approach that. Cause in the layout I have the javascript loader line whatever it is exactly I dont remember but its :default none the less which means jquery and applications will load out by default from what the API tells me.
Which does bring me to another question the guy who initially set up the rails server we have added a file to the defaults I would like to mimic that but don't know how with that either.

The simplest way would be to just include the script file in the view where you need it. jQuery will have already been loaded in the layout.
Alternatively, you can use content_for, as ctcherry mentions. You can find a more detailed explanation here: Javascript Include Tag Best Practice in a Rails Application
Also, regarding you last question, I'm not sure I understand it correctly, but you can add more options to the javascript_include_tag separated by a comma:
javascript_include_tag :defaults, "my_other_file", "etc"

content_for might help you, look at the example that includes the piece of code: <%= yield :script %>
Alternatively, think about ways to allow the JS code to detect if it is begin executed on the correct page (maybe a class or id set on the body tag), and only execute if that condition is met. Then you can serve your entire javascript collection compressed and minified to the user's browser on the first page load, increasing site performance.

May be used for this:
<head>
<title>My blog</title>
<%= yield(:head) -%>
</head>
And send it there from a view:
<%- content_for(:head) do -%>
<%= javascript_include_tag :defaults -%>
<%- end -%>
It's good work!

Related

Confused about how to use vanilla JavaScript with Ruby on Rails

I have two views, one called dashboard and the other called home, both have their respective index.htm.erb files. The corresponding JavaScript files in the assets/javascripts folder do not seem to map to these views. For example, when I run an alert in home.js and then navigate to dashboard.html.erb the alert still runs.
How do I remedy this to get the functionality I am looking for?
I am confused as to how to write page specific raw JavaScript in Rails.
Edit: I could use the public folder but I had some ancillary issues with that and hence why I started using the files in the assets folder.
One of Rails' "opinions" about javascript is that it should be all concatenated into a single file (application.js), minified, and served to the client. This is an effort to minimize the number of requests the client needs to make when accessing your application (the goal is to have only three, one for your html, one for css, and one for javascript [excluding whatever images may be on the page.])
If you look in your application.js, you'll see a note saying that it is a 'manifest' file, and a line that looks like
require_tree .
Which says "load every javascript file in this folder into this file"
In order to load up some page-specific javascript, you'd need to
put a separate javascript file into app/assets/javascripts [call it custom.js, say]
stub out loading that file into the application manifest by writing stub custom in application.js
Include the custom javascript manually in your view (or, more better probably, a layout which renders your view): <%= javascript_include_tag 'custom' %>
However, I'd encourage you to look at whether you really need to separate this javascript, or whether it's a problem that can be solved by simply localizing your script to the page(s) it's intended for, which will keep the same functionality and keep your loads times ever-so-slightly faster.
$('body.some_custom_class').ready(function() {
alert('I'm running on this page!');
});
you can use content_for in your layout
app/views/layouts/application.html.erb
<html>
<head>
<title> ...</title>
....
<%= yield :javascript %>
....
</head>
....
</html>
and on your view app/views/home.html.erb
<% content_for :javascript do %>
<%= javascript_include_tag 'home' %>
<% end %>
other view content
you would also need to look at application.js and remove the //= require tree . line

Rendering Javascripts in Rails without Asset Pipeline

My Problem
I am developing a Rails site and would like to use Javascript to render partials in some of my views depending on certain conditions. For instance-- I'd like to have a partial view of a Devise log-in/sign-in prompt come up in a modal box if the user is not signed in when accessing certain pages.
I've had a lot of trouble figuring this out-- the first issue was that I tried using render in the asset pipeline which after some research found doesn't work to begin with.
I then tried putting a js.erb file into my public/javascripts folder but javascript_include_tag force appends '.js' to the file name and using regular src=/javascripts/... didn't render the '.erb' stuff but rather it would append the text <%=j render :partial ... %>
My Solution
I have come up with this solution here and would like to know if there is a better solution to keep clean code. I am going to have a few other Javascripts that will render the same thing over different views.
I have created a app/views/shared/javascripts directory where I will put [filename].html.erb files.
To get the Javascript to run correctly I will <%= render :partial => 'shared/javascripts/...' %> wherever I want that script to be run.
Inside that script is something like:
<script>
$(document).ready(function() {
...
$(.class).append("<%=j render :partial => 'shared/modal' %>");
...
});
</script>
Is There a Better Way?
As far as I can tell-- this will do what I want it to do. I'm just afraid that I'm looking at this all wrong. I'll be working on another part of the app for a while and I really hope to either verify that this is acceptable and decent or find the proper way to ensure that I can use ERB in my JS files.
I know this answer is 3 years late for the OP, but I found this question googling something else and thought I'd answer it as it's something we do quite a lot in our legacy app.
Suppose you want to render the javascript alert("Hello, world!"). You can create the partial _greetings.js.erb in the folder app/views/my_resources:
alert('<%= #msg %>')
And call it from a controller action:
#msg = "Hello, world!"
render partial: 'my_resources/greetings', formats: :js
Hope that helps someone.
One way to do this is to put your partial in your javascript and render it using something like Mustache. The problem with this approach is that if you're rendering a partial you're also rendering traditionally elsewhere in the Rails app, the Mustache template could easily get out of sync with the Rails partial.
Another way to do this would be to have an action which returns the rendered partial as a string, either as XML or wrapped in JSON, and use AJAX to request the rendered partial from that action. Then your Rails controller will handle both rendering the template (using render_to_string) and wrapping it in JSON for your javascript to consume and redisplay.

Use Rails asset helper to run a Javascript plug in

dealing with an interesting problem. I'm using a third party plug in, Galleria. It runs from the body of the view like so
<script>
Galleria.loadTheme('/assets/galleria/themes/classic/galleria.classic.min.js');
Galleria.run('.galleria'...
</script>
I've realized that with the Rails asset pipeline, I have to use helper tags, otherwise the asset doesn't load. Therefore I subbed the first line out like so:
Galleria.loadTheme("<%= javascript_include_tag 'galleria.classic.min.js', '/galleria/themes/classic' %>");
But I'm not sure what to do about the Galleria.run('.galleria' part, since that's not an asset, that's telling it to run the thing that it should have loaded. Leaving it as is has not worked, so wanted to see if there were any other ideas.
EDIT:
What ended up working was this lovely post from someone else, + a few bits that I had to add on myself:
How to use Galleria plugin with Rails 4 Pipeline
I think you want
Galleria.loadTheme("<%= javascript_include_tag '/galleria/themes/classicgalleria.classic.min.js'%>")
Assuming you've put the js in assets/javascripts?
You may need to add something to the assets path for compilation. I think you can't do the javascript_include_tag directly in line. That should generate a full <script></script> tag. What you really want is just the asset compiled filename. If you've put the javascript files in vendor/assets/javascripts then add them to the precompile list
# in environments/production.rb
config.assets.precompile += %w( galleria.classic.min.js )
then I think you can include it like this:
Galleria.loadTheme('/assets/galleria.classic.min.js');
You may need to use a different path helper to get the digest version if the non-digest version is not available. I should look something like:
Galleria.loadTheme('<%= asset_path 'galleria.classic.min.js' %>');
Good luck. Galleria is a nice slideshow.

Where should JavaScript with embedded Ruby code go in a Rails 3.1 app?

For a Rails 3.1 app, some of my site wide JavaScript is only included when certain real time, instance specific conditions are met. This means I can't put it in the new asset pipeline's application.js because that isn't parsed by erb for embedded Ruby within the current context. Basically, I'm including keyboard shortcuts, based on the current_user that is logged in.
My question: where should this JavaScript with embedded code go, so that the embedded Ruby is still parsed for each page access with the proper context (i.e. current, logged in user)?
The answer seems to just be to put it in the application.html.erb layout view at the bottom, but this seams like I'm hiding away javascript code in a non intuitive location.
I've tried creating an application2.js.erb file, but then I got errors about undefined variables, which I think might be because the asset engine only parses this file once before the output is cached and the scope isn't correct yet for things like current_user.
So, using application.html.erb works just fine here, and this isn't so much a question of how to get it to work functionally. Instead, I'm wondering if there's a more elegant way to incorporate the asset pipeline model here with my requirements and still keep most of my JavaScript in the assets/javascripts directory.
You should try to create app/assets/javascripts/application2.js.erb (or whatever better name you come up with)
And then put something like this in your app/assets/javascripts/application.js:
//= require application2
And then you can have
<%= javascript_include_tag 'application2' %>
wherever you want - for example in your application.html.erb.
Btw, if you want to customize what's included on a per-view basis you might find content_for useful. Check out this screencast
Ok, about unobtrusive js. It will be just a cocept (HAML):
In your view somewhere
# hotkeys are "Ctrl+C", "Ctrl+A"
-current_user.hotkeys.each do |hotkey|
%hotkey{ "data-key" => hotkey.key, "data-behavior" => hotkey.fn }
Then in your application.js
$(document).ready(function(){
if($("hotkey").length > 0){
$("hotkey").each{function(this){
key = $(this).data("key");
fn = $(this).data("behavior");
$(document).bind('keydown', key, fn);
}}
}
})
So just the same JS will extract from HTML hotkeys data and then bind it.
As some people have pointed out, the two options are:
Put your javascript inside the view (and as you say, this doesn't feel quite right).
Put it in a javascript file. Make a conditional inside your view that includes this javascript file if certain conditions are met.
If you need to pass more instance variables from the controller to your javascript, this gem called gon can make your life easier.
This allows you to use the default asset pipeline using the following javascript:
if(gon.conditional){
//your embedded js code here
}
If you want to know more about this gem, checkout this railcast where everything gets explained.

Using Rails 3.1, where do you put your "page specific" JavaScript code?

To my understanding, all of your JavaScript gets merged into 1 file. Rails does this by default when it adds //= require_tree . to the bottom of your application.js manifest file.
This sounds like a real life-saver, but I am a little concerned about page-specific JavaScript code. Does this code get executed on every page? The last thing I want is for all of my objects to be instantiated for every page when they are only needed on 1 page.
Also, isn't there potential for code that clashes too?
Or do you put a small script tag at the bottom of the page that just calls into a method that executes the javascript code for the page?
Do you no longer need require.js then?
Thanks
EDIT: I appreciate all the answers... and I don't think they are really getting at the problem. Some of them are about styling and don't seem to relate... and others just mention javascript_include_tag... which I know exists (obviously...) but it would appear that the Rails 3.1 way going forward is to wrap up all of your JavaScript into 1 file rather than loading individual JavaScript at the bottom of each page.
The best solution I can come up with is to wrap certain features in div tags with ids or classes. In the JavaScript code, you just check if the id or class is on the page, and if it is, you run the JavaScript code that is associated with it. This way if the dynamic element is not on the page, the JavaScript code doesn't run - even though it's been included in the massive application.js file packaged by Sprockets.
My above solution has the benefit that if a search box is included on 8 of the 100 pages, it will run on only those 8 pages. You also won't have to include the same code on 8 of the pages on the site. In fact, you'll never have to include manual script tags on your site anywhere ever again.
I think this is the actual answer to my question.
The Asset Pipeline docs suggest how to do controller-specific JS:
For example, if a ProjectsController is generated, there will be a new file at app/assets/javascripts/projects.js.coffee and another at app/assets/stylesheets/projects.css.scss. You should put any JavaScript or CSS unique to a controller inside their respective asset files, as these files can then be loaded just for these controllers with lines such as <%= javascript_include_tag params[:controller] %> or <%= stylesheet_link_tag params[:controller] %>.
Link to: asset_pipeline
For the page-specific js you can use Garber-Irish solution.
So your Rails javascripts folder might look like this for two controllers - cars and users:
javascripts/
├── application.js
├── init.js
├── markup_based_js_execution
├── cars
│ ├── init .js
│ ├── index.js
│ └── ...
└── users
└── ...
And javascripts will look like this:
// 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
}
and markup_based_js_execution will contain code for UTIL object, and on DOM-ready UTIL.init execution.
And don't forget to put this to your layout file:
<body data-controller="<%= controller_name %>" data-action="<%= action_name %>">
I also think that it is better to use classes instead of data-* attributes, for the better page-specific css. As Jason Garber have mentioned: page-specific CSS selectors can get really awkward (when you use data-*attributes)
I hope this will help you.
I see that you've answered your own question, but here's another option:
Basically, you're making the assumption that //= require_tree . is required. It is not. Feel free to remove it. In my current application, the first I'm doing with 3.1.x honestly, I've made three different top level JS files. My application.js file only has
//= require jquery
//= require jquery_ujs
//= require_directory .
//= require_directory ./api
//= require_directory ./admin
This way, I can create subdirectories, with their own top level JS files, that only include what I need.
The keys are:
You can remove require_tree - Rails lets you change the assumptions it makes
There's nothing special about the name application.js - any file in the assets/javascript subdirectory can include pre-processor directives with //=
Hope that helps and adds some details to ClosureCowboy's answer.
Another option: to create page- or model-specific files, you could create directories inside your assets/javascripts/ folder.
assets/javascripts/global/
assets/javascripts/cupcakes
assets/javascripts/something_else_specific
Your main application.js manifest file could be configured to load its files from global/. Specific pages or groups of pages could have their own manifests which load files from their own specific directories. Sprockets will automatically combine the files loaded by application.js with your page-specific files, which allows this solution to work.
This technique can be used for style_sheets/ as well.
I appreciate all the answers... and I don't think they are really getting at the problem. Some of them are about styling and don't seem to relate... and others just mention javascript_include_tag... which I know exists (obviously...) but it would appear that the Rails 3.1 way going forward is to wrap up all of your Javascript into 1 file rather than loading individual Javascript at the bottom of each page.
The best solution I can come up with is to wrap certain features in div tags with ids or classes. In the javascript code. Then you just check if the id or class is on the page, and if it is, you run the javascript code that is associated with it. This way if the dynamic element is not on the page, the javascript code doesn't run - even though it's been included in the massive application.js file packaged by Sprockets.
My above solution has the benefit that if a search box is included on 8 of the 100 pages, it will run on only those 8 pages. You also won't have to include the same code on 8 of the pages on the site. In fact, you'll never have to include manual script tags on your site anywhere ever again - except to maybe preload data.
I think this is the actual answer to my question.
I realize I'm coming to this party a bit late, but I wanted to throw in a solution that I've been using lately. However, let me first mention...
The Rails 3.1/3.2 Way (No, sir. I don't like it.)
See: http://guides.rubyonrails.org/asset_pipeline.html#how-to-use-the-asset-pipeline
I'm including the following for the sake of completeness in this answer, and because it's not an unviable solution... though I don't care much for it.
The "Rails Way" is a controller-oriented solution, rather than being view-oriented as the original author of this question requested. There are controller-specific JS files named after their respective controllers. All of these files are placed in a folder tree that is NOT included by default in any of the application.js require directives.
To include controller-specific code, the following is added to a view.
<%= javascript_include_tag params[:controller] %>
I loathe this solution, but it's there and it's quick. Presumably, you could instead call these files something like "people-index.js" and "people-show.js" and then use something like "#{params[:controller]}-index" to get a view-oriented solution. Again, quick fix, but it doesn't sit well with me.
My Data Attribute Way
Call me crazy, but I want ALL of my JS compiled and minified into application.js when I deploy. I don't want to have to remember to include these little straggler files all over the place.
I load all of my JS in one compact, soon-to-be browser cached, file. If a certain piece of my application.js needs to be fired on a page, I let the HTML tell me, not Rails.
Rather than locking my JS to specific element IDs or littering my HTML with marker classes, I use a custom data attribute called data-jstags.
<input name="search" data-jstag="auto-suggest hint" />
On each page, I use - insert preferred JS library method here - to run code when the DOM has finished loading. This bootstrapping code performs the following actions:
Iterate over all elements in the DOM marked with data-jstag
For each element, split the attribute value on space, creating an array of tag strings.
For each tag string, perform a lookup in a Hash for that tag.
If a matching key is found, run the function that is associated with it, passing the element as a parameter.
So say I have the following defined somewhere in my 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
};
The bootstrapping event is going to apply the my_autosuggest_init and my_hint_init functions against the search input, turning it into an input that displays a list of suggestions while the user types, as well as providing some kind of input hint when the input is left blank and unfocused.
Unless some element is tagged with data-jstag="auto-suggest", the auto-suggest code never fires. However, it's always there, minified and eventually cached in my application.js for those times that I need it on a page.
If you need to pass additional parameters to your tagged JS functions, you'll have to apply some creativity. Either add data-paramter attributes, come up with some kind of parameter syntax, or even use a hybrid approach.
Even if I have some complicated workflow that seems controller-specific, I will just create a file for it in my lib folder, pack it into application.js, and tag it with something like 'new-thing-wizard'. When my bootstrap hits that tag, my nice, fancy wizard will be instantiated and run. It runs for that controller's view(s) when needed, but is not otherwise coupled to the controller. In fact, if I code my wizard right, I might be able to provide all configuration data in the views and therefore be able to re-use my wizard later for any other controller that needs it.
Anyway, this is how I've been implementing page specific JS for a while now, and it has served me well both for simple site designs and for more complex/rich applications. Hopefully one of the two solutions I've presented here, my way or the Rails way, is helpful to anyone who comes across this question in the future.
This has been answered and accepted long ago, but I came up with my own solution based on some of these answers and my experience with Rails 3+.
The asset pipeline is sweet. Use it.
First, in your application.js file, remove //= require_tree.
Then in your application_controller.rb create a helper method:
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
Then in your application.html.erb layout file, add your new helper among the existing javascript includes, prefixed with the raw helper:
<head>
<title>Your Application</title>
<%= stylesheet_link_tag "application", :media => "all" %>
<%= javascript_include_tag "application" %>
<%= raw javascript_include_view_js %>
</head>
Voila, now you can easily create view-specific javascript using the same file structure you use everywhere else in rails. Simply stick your files in app/assets/:namespace/:controller/action.js.erb!
Hope that helps someone else!
You can add this line in your layout file (e.g. application.html.erb) to automatically load the controller specific javascript file (the one that was created when you generated the controller):
<%= javascript_include_tag params[:controller] %>
You also could add a line to automatically load a script file in a per-action basis.
<%= javascript_include_tag params[:controller] + "/" + params[:action] %>
Just put your page scripts into a subdirectoriy named after the controller name. In these files you could include other scripts using =require.
It would be nice to create a helper to include the file only if it exists, to avoid a 404 fail in the browser.
<%= javascript_include_tag params[:controller] %>
Maybe you will find pluggable_js gem as suitable solution.
The LoadJS gem is another option:
LoadJS provides a way to load page-specific Javascript code in a Rails app without loosing the magic provided by Sprockets. All your Javascript code will continue by minified in one Javascript file but some portions of it will only be executed for certain pages.
https://github.com/guidomb/loadjs
Philip's answer is quite good. Here is the code to make it work:
In application.html.erb:
<body class="<%=params[:controller].parameterize%>">
Assuming your controller is called Projects, that will generate:
<body class="projects">
Then in projects.js.coffee:
jQuery ->
if $('body.projects').length > 0
$('h1').click ->
alert 'you clicked on an h1 in Projects'
JavaScripts are only merged when you tell Rails (Sprockets, rather) to merge them.
This is how i solved the styling issue: (excuse the Haml)
%div{:id => "#{params[:controller].parameterize} #{params[:view]}"}
= yield
This way i start all the page specific .css.sass files with:
#post
/* Controller specific code here */
&#index
/* View specific code here */
&#new
&#edit
&#show
This way you can easily avoid any clashes.
When it comes to .js.coffee files you could just initialize elements like;
$('#post > #edit') ->
$('form > h1').css('float', 'right')
Hope this helped some.
You can also group the js in folders and continue to use the asset pipeline to load your javascript selectively depending on the page.
I agree with your answer, to check if that selector is there, use:
if ($(selector).length) {
// Put the function that does not need to be executed every page
}
(didn't see anyone add the actual solution)
I don't see an answer that really puts it all together and lays it out for you. Thus, I'll try to put meleyal, sujal (a la ClosureCowboy), the first part of Ryan's answer, and even Gal's bold statement about Backbone.js... all together in a way that is short and clear. And, who knows, I might even meet Marnen Laibow-Koser's requirements.
Example edits
assets/javascripts/application.js
//= require jquery
//= require jquery_ujs
//= require lodash.underscore.min
...
views/layouts/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>
views/foo/index.html.erb
...
<% content_for :javascript do %>
<%= javascript_include_tag params[:controller] %>
<% end %>
assets/javascripts/foo.js
//= require moment
//= require_tree ./foostuff
assets/javascripts/foostuff/foothis.js.coffee
alert "Hello world!"
Brief description
Remove //= require_tree . from application.js and list only the JS that each page shares.
The two lines shown above in application.html.erb tell the page where to include application.js and your page-specific JS.
The three lines shown above in index.html.erb tells your view to look for some page-specific JS and include it at a named yield region called ":javascript" (or whatever you want to name it). In this example, the controller is "foo" so Rails will attempt to include "foo.js" at the :javascript yield region in the application layout.
List your page-specific JS in foo.js (or whatever the controller is named). List common libraries, a tree, directories, whatever.
Keep your custom page-specific JS someplace where you can easily reference it apart from your other custom JS. In this example, foo.js requires the foostuff tree so put your custom JS there, such as foothis.js.coffee.
There are no hard rules here. Feel free to move things around and perhaps even create multiple yield regions of various names in various layouts if needed. This just shows one possible first step forward. (I don't do it exactly like this given our use of Backbone.js. I might also choose to drop foo.js down into a folder called foo instead of foostuff but haven't decided that yet.)
Notes
You can do similar things with CSS and <%= stylesheet_link_tag params[:controller] %> but this is beyond scope of the question.
If I missed a glaring best practice here, send me a note and I'll conisder adapting. Rails is fairly new to me and, honestly, I'm not terribly impressed so far with the chaos it brings by default to enterprise development and all the traffic the average Rails program generates.
I have another solution, which although primitive works fine for me and doesn't need any fancy selective loading strategies. Put in your nornal document ready function, but then test the current windows location to see if it is the page your javascript is intended for:
$(document).ready(function() {
if(window.location.pathname.indexOf('/yourpage') != -1) {
// the javascript you want to execute
}
}
This still allows all the js to be loaded by rails 3.x in one small package, but does not generate much overhead or any conflicts with pages for which the js isn't intended.
ryguy's answer is a good answer, even though its been downvoted into negative points land.
Especially if you're using something like Backbone JS - each page has its own Backbone view. Then the erb file just has a single line of inline javascript that fires up the right backbone view class. I consider it a single line of 'glue code' and therefore the fact that its inline is OK. The advantage is that you can keep your "require_tree" which lets the browser cache all the javascript.
in show.html.erb, you'll have something like:
<% provide :javascript do %>
<%= javascript_include_tag do %>
(new app.views.ProjectsView({el: 'body'})).render();
<% end %>
<% end do %>
and in your layout file, you'll need:
<%= yield :javascript %>
Move all your commom JS files to a sub-folder like 'app/assets/javascript/global' then in the application.js, modify the //= require_tree . line to //= require_tree ./global.
Now you are free to put your controller-specific JS on the 'app/assets/javascript/' root and they will not be included in compiled JS, being used just when you call them via = javascript_include_tag on your controller/view.
Though you have several answers here, I think your edit is probably the best bet. A design pattern that we use in our team that we got from Gitlab is the Dispatcher pattern. It does something similar to what you're talking about, however the page name is set in the body tag by rails. For example, in your layout file, just include something like (in HAML):
%body{'data-page' => "#{controller}:#{action}" }
Then only have one closure and a switch statement in your dispatcher.js.coffee file in your javascripts folder like so:
$ ->
new Dispatcher()
class Dispatcher
constructor: ->
page = $('body').attr('data-page')
switch page
when 'products:index'
new Products()
when 'users:login'
new Login()
All you need to do in the individual files (say products.js.coffee or login.js.coffee for example) is enclose them in a class and then globalize that class symbol so you can access it in the dispatcher:
class Products
constructor: ->
#do stuff
#Products = Products
Gitlab has several examples of this that you might want to poke around with in case you're curious :)
Paloma project offers interesting approach to manage page specific javascript code.
Usage example from their docs:
var UsersController = Paloma.controller('Users');
// Executes when Rails User#new is executed.
UsersController.prototype.new = function(){
alert('Hello Sexy User!' );
};
Step1. remove require_tree . in your application.js and application.css.
Step2. Edit your application.html.erb(by rails default) in layout folder. Add "params[:controller]" in the following tags.
<%= stylesheet_link_tag 'application', params[:controller], media: 'all', 'data-turbolinks-track' => true %>
<%= javascript_include_tag 'application', params[:controller], 'data-turbolinks-track' => true %>
Step3. Add a file in 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
references:
http://theflyingdeveloper.com/controller-specific-assets-with-rails-4/
I haven't tried this out, but it looks like the following is true:
if you have a content_for that is javascript (e.g. with real javascript within it), sprockets would not know about it and thus this would work the same way as it does now.
if you want to exclude a file from the big bundle of javascript, you would go into config/sprockets.yml file and modify the source_files accordingly. Then, you would just include any of the files that you excluded where needed.
I did it previously using this method: http://theflyingdeveloper.com/controller-specific-assets-with-rails-4/ . Super-easy, relies on controllers to select the proper js to load.
I combined some answers into:
Application helper:
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
layouts/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
First: remove \\=require_treefrom application.js
Second: all your JS code must be alocated at /app/assets/javascritpt and all your CSS code must be alocated at /app/assets/stylesheets
Following the lead from Ryan, here's what I have done-
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 (controller specific coffeescript,e.g 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}}
Here's how to do it especially if you don't have to execute tons of libraries for your specific page, but only to run a few hundreds lines of JS more or less.
Since it's perfectly fine to embed Javascript code into HTML, just create under app/views shared.js directory and place there your page/pages specific code inside my_cool_partial.html.erb
<script type="text/javascript">
<!--
var your_code_goes_here = 0;
function etc() {
...
}
-->
</script>
So now from wherever you want you simply do:
= render :partial => 'shared.js/my_cool_partial'
And that's it, k?

Categories