Adding page-specific CSS to Rails Asset Pipeline - ruby-on-rails

This is a question that several people have asked before, but none of the questions were quite asked or answered in a way that I found helpful, so I'm writing the question and answer that I would have found helpful.
I have a Rails 3.1+ app using the asset pipeline. There's one specific action that I want to have use different CSS. (In my specific case, I have a page that is intended to be printed, so it truly needs completely different CSS and does not need any Javascript.) Currently, I have only one application-specific CSS file. How do I add the new CSS file and direct the asset pipeline to use my file?
For example, right now, my app/assets looks like
app/assets
/javascript
application.js
custom.js.coffee
/css
application.css
custom.css.scss
I want to add a print.css file that is used by the view of a specific action. This view will not use the application.css file. How do I add print.css?

I found this blog post to be very helpful: http://blog.seancarpenter.net/2012/11/05/page-specific-javascript-with-the-asset-pipeline/. My answer paraphrases what this blogger already wrote and fills in a few missing details.
First, it's important that you've read and understood the Rails Guide to the Asset Pipeline. Unfortunately, this guide doesn't clearly explain how to add action-specific assets, but it does cover some concepts you need to know. Made sure you understand these ideas:
That the asset pipeline compiles Javascript, CSS, and other assets so that Rails servers can cache assets for better performance.
That manifest files use commands like require, require_tree, and require_self to indicate which files are compiled together.
That in order for the asset pipeline to work properly in production, you need to manually run rake assets:precompile to produce the compiled, minified assets in the public directory.
These ideas are the minimum "need-to-know" pieces of information about the asset pipeline. If you don't already understand these ideas, you don't have an "expert or enthusiast" level of knowledge about the pipeline, and unfortunately, SO isn't the right place to learn this stuff. Fortunately, the the Rails Guide to the Asset Pipeline is a short 15-minute read and can get you up to speed quickly if you need it.
Second, these are the changes you need to make in order to ensure that the asset pipeline correctly sees and handles your new print.css file.
Follow these steps:
Add your print.css file to app/assets/css.
You'll need to create a manifest file that will show Rails where to find print.css. You need to do this, even though you only have a single CSS file you're adding. This is an easy step:
Add a file called print.js to app/assets/javascript.
Add this line to print.js:
//= require print
This will be the only line in the entire print.js file. If I understand correctly, Rails expects manifest files to have the file extension .js, which is why we aren't using print.css as the manifest file.
We now need to instruct Rails to find and use the print.js manifest. Add the following line in your config/application.rb file:
config.assets.precompile += %w( print.js )
We're almost finished! However, the already-present application.js manifest includes the line //= require_tree . which means that it will include your print.css file. This will cause your print.css styling to affect your entire site, not just the single view. There are two ways of dealing with this:
If application.js and print.js do not share any assets, you can use the stub command in your application.js to exclude the assets used in print.js. What this does is instruct application.js to remove any of the assets that print.js references from its own list of referenced files. Our modified application.js looks like:
(snip...)
require_tree .
stub print
See this answer for more information.
If your print.js and application.js files share some assets, you'll need to move all of the assets used by application.js into subdirectories. I didn't do this myself, so I'm not the most help in this area. Look at this answer for instructions.
Now we have included print.css in the asset pipeline. We now need to direct Rails to use print.css in your specific view.
Let's say your action is in the reports controller, and that the action is named print_reports. This means we have a reports_controller.rb file and a print_reports.html.erb (or .haml) file. We need to make several changes to these files.
To start, add a new layout in app/views/layouts. Perhaps call it print.html.erb. We'll use this new layout for your print_reports.html.erb file. Set it up as you desire. For a page intended to be printed, this will likely be very simple, such as
<html>
<head>
<title="Print">
</head>
<body>
<%= yield %>
</body>
</html>
Using a separate layout the disadvantage that it's difficult to keep this layout and the layout used by the rest of the application in sync, but if you are using separate CSS files for the action, it's unlikely that you want the layout to be the same anyway.
Add a stylesheet_link_tag in the layout's header pointing to your print.css:
<html>
<head>
<title="Print"/>
<%= stylesheet_link_tag "print" %>
</head>
<body>
<%= yield %>
</body>
</html>
In the controller, we'll tell Rails to use our new layout for the action. Add the line layout 'print', only: [:print_reports] to your controller:
class reports_controller < ApplicationController
layout 'print', only: [:print_reports]
#snip
See this question for more information and a few different approaches.
At this point, when you run the app, your print_reports action should be using print.css correctly!
Remember to run rake assets:precompile before deploying on the server.

Official Solution
It is documented in the official Rails Guides here: http://guides.rubyonrails.org/asset_pipeline.html#controller-specific-assets
Actually you can leave out the require_tree directive (located in application.css and application.js) then use this in your template:
For controller specific JavaScript:
<%= javascript_include_tag params[:controller] %>
For controller specific CSS:
<%= stylesheet_link_tag params[:controller] %>

All of you are putting very complicated answers.
1 Go to app/assets/stylesheets
2.Make a file with the extension .css
3.Go to config/initializers/assets.rb
4.Put this line of of code Rails.application.config.assets.precompile += %w( file.css )
5.Replace file.css with the file you created
6.Go to your html.erb file
7.Type this in the <head>, <%= stylesheet_link_tag "file" %>
8.Replace file with the filename(no extension in the name)
Good Job you linked the file!

There's one specific action that I want to have use different CSS.
Here's an alternative way to accomplish what you're looking for:
Add the controller name and action name to the app body in your /views/layouts/application.html.rb:
<body class="<%= controller_name %>-<%= action_name %>">
<%= yield %>
</body>
Then in your .scss file:
.controller_name-action_name {
// your css goes here
}
So if your controller was static_pages and your action was home:
.static_pages-home {
// your css goes here
}
Tada! Your css only appears for that specific action.

In your layout
<head>
// ...
<%= yield :stylesheets %>
</head>
In your view
<%= provide :stylesheets do %>
// your page-specific css
<% end %>

Related

Why rails loads all css files even when the page does not need that particular stylesheet?

New to Rails (Have used it for 3 weeks). I have several views and their corresponding scss file.
For example,
Views
view1.html.erb
view2.html.erb
view3.html.erb
Stylesheet
view1.scss
view2.scss
view3.scss
When I load view1.html inside <head> of the page all three stylesheet file are been loaded (I actually do now need view2.css and view3.css at this moment).
Is there a reason behind it? Why not just load the static file the current page need? For example, only load view1.css in page view1.html.
I do know in production environment all these will be combined into one single file. Is that because all these static assets will be cached by the browser so load a single file at the first time make the subsequent visit much faster?
The reason I ask this question is because I would like only include controller specific stylesheets in the page. I have tried code below but which cause non ppreprocess error.
<%= javascript_include_tag params[:controller] %>
<%= stylesheet_link_tag params[:controller] %>
I am still reading the doc at link below,
http://guides.rubyonrails.org/asset_pipeline.html#precompiling-assets
Thanks for your time viewing the question.
You can try achieve that in this way,
If you generate a controller named Userscontroller
Rails adds new files at app/assets/javascripts/users.js.coffee and another at app/assets/stylesheets/users.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] %>
First, disable the default loading of all stylesheets by removing any extra requires in the application.css manifest.
*= require_tree .
Remove this line or change to,
* require_tree . // removed '='
Tell Rails to compile our controller assets
By default, the asset pipeline only compiles application.js/.css and any files they require. Because we removed the require_tree . directive, we need to tell Rails about our controller-specific assets or they won’t get compiled.
Create a new initializer file called config/initializers/assets.rb and add the following (replacing the controller names with your own):
%w( controller_one controller_two controller_three ).each do |controller|
Rails.application.config.assets.precompile += ["#{controller}.js.coffee", "#{controller}.css.scss"]
end
Note: You can rename the css.scss to css also.
Check this link for details
Explicitly declare all controllers make me feel bad, therefore, we replace #Sravan 's solution to the code below,
Rails.application.config.assets.precompile << /(^[^_\/]|\/[^_])[^\/]*$/
Rails.application.config.assets.precompile += ["*.js.es6", "*.scss"]

How to load page specific rails 4 js files?

I am reading rails guides documentation for asset pipeline.
It states that coffeescript page specific generated files
are by default ready to user if there is a require_tree directive on the manifest.
This is not working with me I have to do include this
<%= javascript_include_tag params[:controller] %>
on the specific controller.
What am I missing ?
The asset pipeline will compress all of your JS into a single file, application.js. In order to call JS for a specific page, you will need to organize your JS by controller and action. There is a gem, RailsScript that does this automatically and it's compatible with Turbolinks which can give you a single page application feel.
RailsScript only takes a few minutes to learn, https://github.com/gemgento/rails_script.
A specific example using rails script:
# app/assets/javascripts/users.js.coffee
window.App ||= {}
class App.Users extends App.Base
show: ->
alert('The users#show action!')
I think you are misunderstanding the asset-pipeline in general. It doesn't load the javascript-files individually, but rather all the .js.coffee files will get compiled into one big js-file, which you have to include in your views/layout like this
<%= stylesheet_link_tag "application", media: "all", "data-turbolinks-track" => true %>
If you want some js-code that is only available in one view, you definitely should not include that into the asset-pipeline.
Not sure if I've misunderstood your first paragraph, but I think what the line means is that if your application.js manifest contains a line like:
//= require_tree .
Then yes indeed, page specific javascript, or coffeescript will be loaded, not only for that specific page, for for all pages. If you want to constrain assets to certain pages like you've described, you will need a file located in app/assets/javascripts/ with the pluralized name of the controller, and .js.
I would personally create this as another manifest for that specific page, that way I can load multiple assets. Lets say you have a controller called UsersController, with various assets used by that controller's views. What you then need, in order for the line you wrote in your question to work, is a .js filed users.js or users.js.coffee in the app/assets/javascript directory.
Alternatively, to maintain the naming convention, I do something like this:
<%= javascript_include_tag "application-#{params[:controller]}"%>
and then of course name my file appropriate (application-users.js).
Also, when you do this, you'll want to stop your page-specific assets from loading for all controllers. Simply remove the //= require_tree . line and replace it with explicit //= require lines as needed.
Here's a way to do page-specific javascript in rails.
Install the jquery-readyselector.js plugin. (It's 18 lines)
a. Copy the contents of https://raw.github.com/Verba/jquery-readyselector/master/jquery.readyselector.js
b. Paste the contents to a new file at assets/javascripts/jquery_readyselector.js
c. Require jquery-readyselector
// assets/javascripts/application.js
//= require jquery_readyselector
//= require_tree .
Create CSS classes so we have a way to reference each page individually.
<%# views/layouts/application.html.erb %>
<body class="<%= controller_name %> <%= action_name %>">
Now we can scope our javascript to our page using CSS.
// assets/javascripts/posts.js
$(".posts.index").ready(function() {
});

How do I use Controller specific stylesheets in Rails 3.2.1?

Using Rails 3.2.1
I created a simple controller called Home using the command:
rails g controller Home index
And it created a new controller and view for me:
Notice how there are two stylesheets, one "Application" and one "Home". I can't find any documentation to support this assumption but I'm guessing you put styles that will only be applied to the "Home" views, in the Home.css.scss file, correct?
So as a test, I added in some global styles to Application.css.scss.erb and ran the application.
The styles applied as expected.
Next, I added in some rules to the Home.css.scss file and I visited a "Home/index" view, yet the style in that file wasn't attached, neither as a seperate CSS reference link, or even appended to the single Application.css.scss file. This is highly confusing to me, since the comments say:
// Place all the styles related to the Home controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/
Why aren't the rules written in Home.css.scss applied to my website?
It can work this way and Marek is quite correct, the answer is in the guide.
In the introduction to section 2.1:
For example, if you generate a ProjectsController, Rails will also add 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] %>.
So to set your application up to load controller specific stylesheets:
First, disable the default loading of all stylesheets by removing any extra requires in the application.css manifest.
Typically you'll see an entry like this:
*= require_tree .
If you still want to load some common css files, you can move them to a subdirectory and do something like this:
*= require_tree ./common
Second, In your application's layout add the suggested stylesheet_link_tag eg
<%= stylesheet_link_tag "application", :media => "all" %>
<%= stylesheet_link_tag params[:controller] %>
In this example we first load the application css file, we then load any css file that matches the current controller name.
I've solved this problem with a simple solution. I add to body the controller name as a class, editing views/layouts/application.html.slim:
body class=controller.controller_name
Or views/layouts/application.html.erb:
<body class="<%= controller.controller_name%>">
And then in my css I just use body.controller_name as a namespace:
/* example for /users/ */
body.users {
color: #000;
}
body.users a {
text-decoration: none;
}
For small projects I think it's fine.
I don't think it works that way (Home.css being applied only to Home controller actions). The different files are just for separation, to make it clearer what are the CSS rules describing. You can read this guide about the asset pipeline. I'm guessing you altered the default application.css.scss and removed the line importing all CSS files from app/assets/stylesheets.
TL;DR:
Ignore the comment, it's not made by Sass. But put:
#import "*";
into your application.css.scss file, and it will automatically import all the controller scss files.
Full read:
Disclaimer: This is my current understanding of the asset pipeline flow with and without Sass.
I think this comment is written by the standard Rails Asset pipeline (sprockets), and not by Sass:
// Place all the styles related to the Home controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/
The standard pipeline will handle scss files but doesn't presume an application.css.scss file. But if you create such a file with Sass, then Sass will compile it to the application.css file.
If you use the normal Rails asset pipeline, without Sass, then sprockets would load the css file into the application.css file automatically (if that file has the default *= require_tree . line in it).
When you use Sass, with an application.css.scss file, Sass will compile this file into a application.css file. (I assume it would overwrite or take precedence over any application.css file you already had).
To get your home.css.scss file (and other controller files) automatically included, put this line into your application.css.scss file:
#import "*";
For reference, see this question:
Is it possible to import a whole directory in sass using #import?

Javascript and CSS specific files, how to include them by convention ? Rails 3.1

How can I include specific JS or CSS files (by convention ?) with Ruby Rails 3.1 ?
I have a view :
views/project/index.html.erb
And I want to include a specific javascript file for this page. I put it in
assets/javascripts/project/index.js
Same for another view :
home/index.html
Thanks
In the application.css and application.js file, be sure to remove the line \\= require tree.
Then, manually list all the css/js files you want included in each manifest file, for example:
// application.js
//= global.js
//= everywhere.js
Then, I would setup a yield in your header or your closing body tag for your application layout file, for instance (in haml)
%head
%title Some Page
= stylesheet_link_tag 'application'
= yield :stylesheets
Then in your particular view, say _example_partial.html.haml, do this:
- content_for :stylesheets do
= stylesheet_link_tag 'example_partial'
-# the rest of your view goes here
You do the exact same thing with Javascript files, just using javascript_include_tag instead of stylesheet_link_tag.
This will let you quickly and easily assemble view-specific javascript / css payloads. There may be a more sophisticated way to handle this using the asset pipeline, but I would suggest that if the asset pipeline is already minifying and merging you major stylesheets that this kind of +1 css / js file per view is not going to cause a major performance hit. Just try to make sure you don't overdo it with dozens of separate files loading into a single view.

How do I use CSS with a ruby on rails application?

How do I use CSS with RoR? When I link externally, I'm never able to see the files. I cp'd the .css file to every folder I could think of...views, controller, template, and nothing seems to work.
What do I need to do to enable external CSS files with a rails application? I'm new to rails, so forgive me if this is basic.
Put the CSS files in public/stylesheets and then use:
<%= stylesheet_link_tag "filename" %>
to link to the stylesheet in your layouts or erb files in your views.
Similarly you put images in public/images and javascript files in public/javascripts.
If you are using rails > 3 version, then there is a concept called asset pipeline. You could add your CSS to
app/assets/stylesheets
then it will automatically be picked up by the app. (this is useful as rails will automatically compress the CSS files)
read more here about the asset pipeline
Use the rails style sheet tag to link your main.css like this
<%= stylesheet_link_tag "main" %>
Go to
config/initializers/assets.rb
Once inside the assets.rb add the following code snippet just below the Rails.application.config.assets.version = '1.0'
Rails.application.config.assets.version = '1.0'
Rails.application.config.assets.precompile += %w( main.css )
Restart your server.
I did the following...
place your css file in the app/assets/stylesheets folder.
Add the stylesheet link <%= stylesheet_link_tag "filename" %> in your default layouts file (most likely application.html.erb)
I recommend this over using your public folder. You can also reference the stylesheet inline, such as in your index page.
The original post might have been true back in 2009, but now it is actually incorrect now, and no linking is even required for the stylesheet as I see mentioned in some of the other responses. Rails will now do this for you by default.
Place any new sheet .css (or other) in app/assets/stylesheets
Test your server with rails-root/scripts/rails server and you'll see the link is added by rails itself.
You can test this with a path in your browser like testserverpath:3000/assets/filename_to_test.css?body=1
To add to the above, the most obvious place to add stylesheet_link_tag is in your global application layout - application.html.erb.
With Rails 6.0.0, create your "stylesheet.css" stylesheet at app/assets/stylesheets.
Have you tried putting it in your public folder? Whenever I have images or the like that I need to reference externally, I put it all there.

Resources