Avoiding repetitive "content_for" in views - ruby-on-rails

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.

Related

Rails: How to render a layout around a collection?

Rails supports :layout option when rendering collection partials, but the layout applies to the every element in the list.
Is there a way to add a layout around the whole collection?
Details
Let's say I have a collection #promoted_stories. I want to render some HTML around the rendered collection, but only if #promoted_stories is not empty. What I want can be achieved in this way:
<% if #prometed_stories.present? %>
<div class="promoted-stories>
<%= render #promoted_stories %>
</div>
<% end %>
Can I do the same and avoid the if? I'm a fan of logic-less layouts, so I would like to avoid as much branching in my views if possible. I'd prefer if something like this was possible:
# View:
<%= render collection: #promoted_stories, collection_layout: 'promoted_stories' %>
# _promoted_stories.html.erb
<div class="promoted-stories">
<%= yield %>
</div>
While not as elegant as imagined solution, this should achieve similar results:
# Helper
def render_collection_template(template, collection)
render template: "layouts/#{template}", locals: { collection: collection } if collection.present?
end
# View
<%= render_collection_template 'promoted_stories', #promoted_stories %>
# Template
<div class="promoted-stories">
<%= render collection %>
</div>
There is now a pull request for rails implementing collection layout for collection render.

Rendering Layouts without loading its inner layout in Rails

Im trying to bring a layout in my html page by
render layout 'flatty'
thing is this loads the whole flatty layout. In flatty.html.erb it renders _header,_footer and also _sidebar.html.erb
I dont want to load _sidebar.html.erb in this particular page.
So how should i render this?
thing is this loads the whole flatty layout. In flatty.html.erb it renders _header,_footer and also _sidebar.html.erb I dont want to load _sidebar.html.erb in this particular page
Why do you want to use same layout if you have so many changes? Why not make a partial which you could render in both cases. Make a new partial, lets say _common.html.erb, render it in your flatty layout and view of the action in which you want to use it.
#flatty.html.erb
<%= render "common" %>
<%= render "sidebar" %>
#some_action_name.html.erb
<%= render "common" %>
If you still want to use same layout in both cases then you ca use rails 4 controller_name and action_name helpers in your layout and selectively render sidebar and other partials in your layout:
#flatty.html.erb
<%= if controller_name == "some_controller_name" && action_name == "some_action_name"
<%= render "sidebar" %>
<% end %>
Maybe in your controller action, you can have a flag indicating the sidebar should not be rendered. Then, in your flatty.html.erb file, check for the flag variable before you render the _sidebar.html.erb.
For example, if you have a controller action called flatty, add an instance variable, #disable_sidebar, to act as your flag.
def flatty
#disable_sidebar = true
# Your other code
render layout: 'flatty'
end
Then, in your flatty.html.erb, add a conditional before your render for your sidebar (note the ! negation in the if statement:
<% if !#disable_sidebar %>
<%= render "layouts/sidebar" %>
<% end %>
Alternatively, in your flatty.html.erb you can also check for the controller and action values in your params hash, and then don't render your sidebar if it matches that controller's action:
<% if params[:controller]!="YOUR_CONTROLLER" and !params[:action].eql? "flatty" %>
<%= render "layouts/sidebar" %>
<% end %>

Rails Partials For Resources

I have a resource called Exercises in my application. I currently have a partial called _exercise.html.erb that I use to render them. I have an outlying case where I'd like to render them in a much different way. Can I make another partial for exercises that has this other format and still be able to use <%= render #exercises %>?
If not what is the best approach? Should I out a variable in the controller that tells the partial which layout to use, this would have both layout in one file and one if to decide. Or is there some better way?
If you'd like to use business logic to determine when to show what partial for your #exercises collection you should use the to_partial_path method in the Exercise model to define that. See #4 in this post: http://blog.plataformatec.com.br/2012/01/my-five-favorite-hidden-features-in-rails-3-2/
Or, if this is more of a view-related decision (i.e. one view will always use the regular _exercises.html.erb and another view would always use e.g. _alternate_exercises.html.erb) then you can specify as such:
<%= render partial: 'alternate_exercises', collection: #exercises, as: :exercise %>
This will render the _alternate_exercises.html.erb partial once for each item in #execrises passing the item in to the partial via a local_assign called exercise.
In this case, I suppose you have two options:
1) Put the conditional code inside of _exercises.html.erb
eg.
<% if #exercise.meets_some_condition %>
you see this stuff
<% else %>
you see other stuff
<% end %>
This way, you can still make use of <%= render #exercises %>
2) Otherwise, your other option is to have separate partials and render them outside.
eg.
<% #exercises.each do |exercise| %>
<% if exercise.meets_some_condition %>
<%= render "exercises/some_condition_exercise" %>
<% else %>
<%= render "exercises/exercise" %>
<% end %>
<% end %>
This is the best approach for rendering partial. You can wrap that partial with if else statement in your code. Here is my example
rendering with form called _victim.html.erb
<%= render :partial => "victim", :locals => {:f => f }%>
rendering without form
<%= render :partial => "victim"%>

Rails 3 and partials layouts

I'm trying to render a collection of different objects in a same format for each. I want to keep it DRY, so I want to use partials and partial layouts.
Edit - brief clarification : That I need is not to display common items on all publication pages, but to display common properties/fields on each item. This is why I need partial layouts e.g. a layout for the the partial, not a layout for the page.
I have a collection of different objects :
#publications = Publications.all
# Publication is the parent class
# #publications = [ImagePost, VideoPost, TextPost, ...]
I want to render all publications in a list. Each publication have some common properties : author, date, ... I want to put this properties in a partial layout.
So in my view, to render the collection, I do :
<%= render :partial => 'publications', :locals => {:publications => #publications} %>
In the first level partial views/publications/_publications.html.erb, I loop on the item and try to render each item in its partial and with a common partial layout :
<ul class='publications_list'>
<% publications.each do |p| %>
<%= render p, :layout => 'publications/publication_layout' %>
<% end %>
</ul>
The partial layout, views/publications/_publication_layout.html.erb :
<li>
<h2><%= link_to publication.title, publication %></h2>
... Other common properties that I want to display on each item, independently of its type ...
<p><%= yield %></p>
</li>
And finally for each object type, I have a partial (e.g. image_posts/_image_post.html.erb and so) containing the code to display properly each.
My problem : I don't manage to render each publication in the common partial layout publication_layout. This layout is simply ignored by rails. Each item is correctly rendered but without this layout which include the common properties and the <li> tag.
Any suggestion about why my partial layout is ignored ?
ANSWER AND WORKAROUNDS
Thanks to #MarkGuk to have spotted this line in the doc :
Also note that explicitly specifying :partial is required when passing
additional options such as :layout.
So it's just not possible to simply render a polymorphic collection within the same partial for each item.
Workaround 1 : I first tried to compute partial path for each item, store it in the model for convenience, and so render each item in the good partial with the good layout. But I realize that this method, I can't refer to the object publication inside the layout...
<ul class='publications_list'>
<% publications.each do |p| %>
<% # p.partial = p.class.to_s.underscore.pluralize +'/'+ p.class.to_s.underscore %>
<%= render :partial => p.partial, :object => p, :as => :publication, :layout => 'publications/publication_layout' %>
<% end %>
</ul>
Workaround 2 :
Finally I used nested partials.
<ul class='publications_list'>
<% publications.each do |p| %>
<%= render :partial => 'publications/publication_layout', :object => p, :as => :publication %>
<% end %>
</ul>
and replaced yield with a render publication inside the layout.
I wonder if a nested layout might serve you better here. The guide should point you in the right direction and I found it trivially easy to get working, but as a start:
In views/layouts/application.html.erb, change yield to:
<%= content_for?(:publication_content) ? yield(:publication_content) : yield %>
Eliminate the partial views/publications/_publication.html.erb and instead create the nested layout views/layouts/publication.html.erb:
<% content_for :content do %>
# Put common items here
<%= content_for?(:content) ? yield(:content) : yield %> # this is for specifics
<% end %>
<%= render :template => 'layouts/application' %>
Then specific layouts can be nested further or specified with additional tags in the view, depending on the rest of your setup.
See comments to the question and marked answer from the author.
Documentation
Answer from the author

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 %>

Resources