ruby template erb - ruby-on-rails

I have a layout.html.erb file w hich should act as a common file for all the pages to decorate as shown below.
<%= render :partial => "layouts/header" %>
<%= render :partial => "layouts/leftsidemenu" %>
<body>
<%= #content_for_layout %>
</body>
<%= render :partial => "layouts/footer" %>
How can I configure this rails framework so that, so that I do not want to include layout.html.erb in all the pages as
<%= render :partial => "layouts/layout" %>
I need to configuration file to decorate, as we do in Struts framework using sitemesh decorator.xml file.
thanks in advance
Mahesh

First of all, default layout for Rails app is in <rails_app>/app/views/layouts/application.html.erb and is used because all your controllers inherit from ApplicationController (see the name, as convention Rails use layout with same base name as controller, or name of parent controller and so on).
Second, your layout should look something like that:
<%= render :partial => "header" %>
<%= render :partial => "leftsidemenu" %>
<body>
<%= yield %>
</body>
<%= render :partial => "footer" %>
of even paste content from header and footer to this layout. More informations about layouts you can find in this guide.
If you want to change some aspect of page, for example title, then you can do so with layouts too:
# header.html.erb
<head>
<title>
<%= yield(:title) of "Default title" %>
</title>
</head>
# page.html.erb
<% content_for :title do %>
Specific title
<% end %>
Page content
If you want to use layout from different file, then you can do so that way:
# ApplicationController.rb
class ApplicationController < ActionController::Base
# ...
layout 'your_layout' # file in app/views/layouts
# ...
end

Take a look at the Rails docs for structuring layouts, especially the yield and content_for tags. You can also specify a layout with layout in a controller, or a default one for all controllers in your application controller.

Related

Rails: Correct way of making exception of "Yield" by using "Content_for"?

I have a Rails app, in which I fit my entire website into a 980width container with the following code in my 'application.html.erb' file:
<div class="container_980 white shadow-horizontal">
<div class="container">
<%= render 'layouts/flashes' %>
<%= yield %>
</div>
</div>
Now, I want to make 2 file exceptions for fitting the content within the container. I want the index page and another page to expand across the entire page, so I need to get those two pages outside of the common 'yield' set above.
I tried doing so with:
<% if current_page?(root_url) %>
<%= yield :index %>
<% elsif current_page?(:controller => "tracks", :action => "show", :id => params[:id])) %>
<%= yield :show_track %>
<% else %>
<div class="container_980 white shadow-horizontal">
<div class="container">
<%= render 'layouts/flashes' %>
<%= yield %>
</div>
</div>
and
<% content_for :show_track do %>
blah blah blah
<% do %>
THE PROBLEM: The show_track page doesn't load. I did some searching, and it seems like the above method should work, but it's not, and I was wondering if I needed to do something else as the "show" page was made through scaffoldaing(RESTful).
Is there a better way to take out the 2 pages from the container than using if..else conditions?
Is there a better way to take out the 2 pages from the container than using if..else conditions?
This is subjective, but I would use nested layouts, then define the layouts for each page type in the controller.
First your basic top level layout. I'm calling it "application", the default, but you could call it whatever. Note how if there's content_for? :application it will yield it, otherwise it will just yield. This is key to the setup. All nested layouts should follow a similar pattern; in this way they can render further nested child layouts, or be used as layouts themselves.
<!-- layouts/application.html.erb -->
<html>
<body>
<%= content_for?(:application) ? yield(:application) : yield %>
</body>
</html>
Then for the container, you'd define layout which can be nested inside "application", this one setting up your container HTML and rendering content inside.
<!-- layouts/container.html.erb -->
<%= content_for :application do %>
<div class="container_980 white shadow-horizontal">
<div class="container">
<%= render 'layouts/flashes' %>
<%= content_for?(:container) ? yield(:container) : yield %>
</div>
</div>
<% end %>
<%= render :file => "layouts/application" %>
Then just move your conditional logic to the controller, like:
layout :determine_layout
protected
function determine_layout
# pseudocode here, you get it
(index or tracks) ? "application" : "container"
end
You could stop there. Continue to see how you might further nest layouts.
However you could go further, and use the nested layout setup to nest arbitrary numbers of different layouts. Say, for example, that tracks had another content block you needed to fill. You could define another nested layout, like:
<!-- layouts/tracks.html.erb -->
<%= content_for :some_other_block do %>
// stuff that should be in some other block
<% end %>
<%= content_for :container do %>
// stuff that should be in the container
<% end %>
<%= render :file => "layouts/container" %>
Then in your controller, you'd change your determine_layout to set the "tracks" layout for tracks, e.g.:
function determine_layout
# pseudocode here, you get it
if index
"application"
elsif tracks
"tracks"
else
"container"
end
end

Rails 3 nested layouts: how to add at the end?

Is it possible to manipulate the placeholders so that I can not only set their content, but also add/remove content in a particular order? For example:
layouts/base.html.erb (a base layout meant to be extended):
<!DOCTYPE html>
<html>
<head>
<title><%= yield :title %></title>
<%= yield :stylesheets %>
<%= yield :javascripts %>
<%= yield :csrf %>
</head>
<body>
<div class='container-fluid'>
<%= yield :header %>
<%= content_for?(:content) ? yield(:content) : yield %>
<%= yield :footer %>
</div>
</body>
</html>
layouts/application.html.erb (this is the layout I will be using for the most part of my app, it inherits from the base layout):
<% content_for :stylesheets do %>
<%= stylesheet_link_tag "application", :media => "all" %>
<% end %>
<% content_for :javascripts do %>
<%= javascript_include_tag "application" %>
<% end %>
<% content_for :csrf do %>
<%= csrf_meta_tags %>
<% end %>
<%= render :template => 'layouts/base' %>
Now I want a layout for a specific controller, which may need to add more javascript links, or maybe completely remove them. Let's say I want to add only one file after the other javascripts. So far I got this:
layouts/some_controller.html.erb (this is a layout for a particular controller, it should inherit from the application layout):
<% content_for :javascripts do %>
<script src="/assets/some_javascript_that_depends_on_jquery.js" type="text/javascript"></script>
<% end %>
<%= render :template => 'layouts/application' %>
This won't work, because it will place some_javascript_that_depends_on_jquery.js at the beginning of the :javascripts placeholder, and I need it at the end because it depends on jquery.
It would suck to have to extend the base layout directly, and keep track of any change made to the application layout to apply it to the controller-specific layout too.
What would be the recommended way to deal with this situation?
In application.html.erb, Keep the contents of content_for :javascripts in a partial
Here your partial will have
<%= javascript_include_tag "application" %>
Then, call the same partial in addition with other javascripts in other layout.
Another way,
You can call one helper which will have a hash like this:
js_files = {"application_controller" => ["js_file_1"], "some_controller" => ["js_file_1","js_file_2"]}
Now, fetch the js files and construct the javascript include tag in run time based on controller in your content for.
Hope this will be more flexible.
Sorry for not formatting.
Always keep one js file per controller.
<%= javascript_include_tag params[:controller] %>
lets take example of users controller then there will be users.js.coffee file.
If you want to have multiple js files for users controller then you can require those files inside users.js.coffee file
vi users.js.coffee
//= require 'a'
//= require 'b'
/* my extra js code will go here */
This can't be done as in other frameworks where you just extend layouts and then modify the inherited blocks at will.
Rails sort of forces you to keep it simple.

How do you find out what controller/action you are in via ruby code?

I have a different subheader partial I want to render dependent on where I'm at in my application. How do I go about determining where I'm at via ruby? Or do I need to parse the URL?
Example :
If I'm at my root level I want to use /home/subheader, if I'm in controller 'test' I want to render /test/subheader, etc... etc...
basically looking for this part:
(in my application view)
<%- if ############ %>
<%= render :partial => '/home/subheader' %>
<%- elsif ########### %>
<%= render :partial => '/test/subheader' %>
<%- else %>
<%= render :partial => '/layouts/subheader' %>
<%- end %>
Thanks
You can use current_page?
if current_page? :controller => 'home', :action => 'index'
do_this
end
or use the controller's method controller_name
if controller.controller_name == 'home'
do_that
end
If you're using this in a per-controller basis, you should probably need layouts or use different templates, rendering different partials depending in controller/action is a code smell.
P.S: You could also try to get the params[:controller] and params[:action] variables, but I am not sure if they are passed correctly if your route is non the standard /:controller/:action
A slightly easier way to manage this would be to use content_for. For example:
#app/layouts/application.html.erb
<html>
<body>
<h1>My Application</h1>
<%= yield(:subheader) || render(:partial => 'layouts/subheader') %>
<%= yield %>
</body>
</html>
This layout will first try to render the subheader content that was passed in from the view, otherwise it will render the partial 'layouts/subheader'. Then in each view that requires a custom subheader, all you have to do is:
#app/views/home/index.html.erb
<% content_for :subheader, render(:partial => 'subheader') %>
And in your other controller, you could use something completely different, like:
#app/views/other/show.html.erb
<% content_for :subheader do %>
<h2>A different subheader</h2>
<% end %>

Avoiding repetitive "content_for" in views

I have a submenu placed in my layout wich differs from controller to controller, but not between each controllers method views. What I am currently doing is the following:
<% content_for( :submenu ) do %>
<%= render :partial => 'submenus/correct_submenu' %>
<% end %>
In every view for a method
My applications layout then has this in it
<%= yield :submenu %>
However, this feels kind of repetitive, doing it for each view. Is there some way to do this per controller?
My suggest is to have a convention for this, so if you have a ProductsController then the submenu would be submenus/products_menu. This way you can write a helper that looks like:
def render_submenu
content_for(:submenu) { render :partial => "submenus/#{controller.controller_name}_menu" }
end
You can then call this by doing:
<%= render_submenu %>
You could then make this the default content_for the submenus and only specify the content if it needs to be different.
I hope this helps!
Use nested layouts to nest a specific controller's layout under the application layout, by creating a file like so:
# app/view/layouts/<controller_name>.html.erb
<% content_for( :submenu ) do %>
<%= render :partial => 'submenus/correct_submenu' %>
<% end %>
<%= render template: "layouts/application" %>
With this method, you don't have to modify a bunch of view files.

How to include a css or javascript in an erb that is outside the layout?

Sorry for the slightly noobish question, as I am writing my first rails app.
I get the idea of the layout view, but if you are using them, is there any way to include a view specific js or css file? For example, I have layouts/products.html.erb, and for products/edit.html.erb I want products_edit.css, but I don't want that css for all product views, what is the best practice to accomplish that?
If you have a generic edit.css file, I would suggest an if in your layout
<%= stylesheet_link_tag 'edit' if params[:action] == 'edit' %>
Otherwise you can use content_for with a yield to add additional tags into the head.
layout.html.erb
<head>
...
<%= yield(:header) if #content_for_header %>
</head>
products/edit.html.erb
<% content_for :header do -%>
<%= stylesheet_link_tag 'edit_product' %>
<% end -%>
You can add a stylesheet tag inside the head tag of the layout by doing something like this:
layouts/products.html.erb:
<head>
...
<%= yield :css %>
...
</head>
products/edit.html.erb
<% content_for :css do
stylesheet_link_tag 'products_edit'
end %>
You can't if your </head> tag is in your layout.
You may want a different layout for that controller action. Like this on the render:
render :action => "index", :layout => "some_other_layout
Also you can set a different default layout for a whole controller with this line in the controller class:
layout "some_other_layout"
Check the API docs, there's some complex things you can do with conditionals on that layout method.

Resources