DRY view for sidebar - ruby-on-rails

In my quest to keep my application views as DRY as possible I've encountered a little snag. My appliation.html.erb incorporates a static sidebar menu. Each of my main controllers incorporates a secondary sidebar menu (essentially a submenu). I can take the code that renders the menu out of application.html.erb and put it in each of my views and change the secondary sidebar there, but this produces a lot repetition in my views.
I saw this SO post and looked at this page, but I was unable to get either idea to work. I was thinking that I could put something like:
<% provide(:submenu, 'layouts/sidebars/sidebar_customers_contacts') %>
at the top of each view and use that to render the associated partial by doing
<% content_for(:submenu) do %>
<%= render :partial => :submenu %>
<% end %>
from the application.html.erb but of course that didn't work.
This is my current application.html.erb:
<div class="side">
<%= render 'layouts/sidebar' %>
<%= render 'layouts/sidebars/sidebar_dashboard' %><!-- this needs to load a sidebar based on the controller that calls it. Each view of the controller will get the same sidebar. -->
</div>
<div class="main-content">
<%= yield %>
</div>
I feel like I'm making this more difficult than it really is. Is there a simple way to do this?

Rails provides a helper called controller_name which you can read more about here.
Assuming you adhere to your own naming conventions, this should work as-is. If you decide some controllers don't get a sidebar, you may need to throw in some conditionals...
application.html.erb
<div class="side">
<%= render "layouts/sidebar" %>
<%= render "layouts/sidebars/#{ controller_name }" %>
</div>
<div class="main-content">
<%= yield %>
</div>
EDIT
Sorry, my mistake was using single quotes instead of double-quotes. You cannot use #{string interpolation} within single quotes. Source

Related

Application.html.erb template inheritance

I have a situation in rails (version 4.04, ruby version 2.1) where I've been using the standard application.html.erb to define the main framework for my site, header, footer, nav bar, etc. When I got to an inner div, call it, inner-content, thats where I put a <% yield %> statement so that the sub template can take over and place its content in the correct place (for example products#show or products#index have show.html.erb and index.html.erb respectively which just the content for those actions).
The problem is I realized I was duplicated some code in those sub templates. In ever one of them (except one) I always was starting off like this:
<div class="columns large-6 medium-6 center-small">
<div class="inner_wrapper">
And I was always ending like this:
</div>
</div>
So I was thinking, I shouldn't be repeating all this code. I should move this into application.html.erb so that every template automatically gets the inner-content set up correctly.
The problem is that one action I was talking about. There is one action that has a different setup. I don't want to have to type in those extra 2 divs for every sub-layout except one. Is there a better way to do this?
One way could be to check which controller your currently using this in your application.html.erb
<% if params[:controller] == "controller name" %>
<div>
<%= yield %>
</div>
<% else %>
<div class="different div">
<%= yield %>
</div>
<% end %>
Not sure if this the best way, but its one way to do it.
Create a different layout file and call it maybe products_layout.html.erb.
Then in the controller
class ProductsController < ApplicationController
layout: 'products_layout'
....
end
Or do it on a per action
def show
render 'show', layout: 'products_layout'
end
http://guides.rubyonrails.org/layouts_and_rendering.html

how to extend child template to parent in rails

I am new to rails. I am having difficulty in understanding template inheritance. Earlier I have worked in django and seen template inheritence there. There I saw child is told about parent using "extends" command. Can anyone explain how it works here. I have gone through guidelines of ruby but it was not clear.
Thanks
It's quite simple to do in Rails.
You simply tell the template you are currently rendering to render another template.
For example layouts/application.html.erb contains something like this:
<% content_for :navigation do %>
<nav>...</nav>
<% end %>
<% content_for :content do %>
<%= yield %>
<% end %>
<%= render :template => 'layouts/main_application' %>
The important part is the render :template part that then delegates this template to also render the layouts/main_application.html.erb that in my case looks something like this:
<header>
...
</header>
<body>
<%= yield :nav %>
<%= content_for?(:content) ? yield(:content) : yield %>
</body>
What I am doing here is having a main template that does not contain the navigation (for things like login etc) and the application.html.erb adds that navigation to the :nav content placeholder.

Render rails partial multiple times on same page

I have a partial that I'm rendering twice on the same page, but in two different locations (one is shown during standard layout, one is shown during mobile/tablet layout).
The partial is rendered exactly the same in both places, so I'd like to speed it up by storing it as a variable if possible; the partial makes an API call each time, and the 2nd call is completely unnecessary since it's a duplicate of the first API call.
Is there any way to store the HTML from the returned partial as a variable and then use that for both renders?
Edit: I'm hoping to do this without caching, as it is a very simple need and I'm looking to keep the codebase lean and readable. Is it possible to store the partial as a string variable and then reference that twice?
<% content_for :example do %>
<%= render :your_partial %>
<%end%>
then call <%= yield :example %> or <%= content_for :example %> wherever you want your partial called.
One option would be to use fragment caching. After you wrap the partial with a cache block, the second call should show the cached version of the first. For example:
<% cache do %>
<%= render(:partial => 'my_partial') %>
<% end %>
... later in the same view ...
<% cache do %>
<%= render(:partial => 'my_partial') %>
<% end %>
To store the result of the render to a string, you could try the render_to_string method of AbstractController. The arguments are the same as for render.
partial_string = render_to_string(:partial => 'my_partial')
I'm adding an answer to this old question because it topped Google for a search I just made.
There's another way to do this now (for quite a while), the capture helper.
<% reuse_my_partial = capture do %>
<%= render partial: "your_partial" %>
<% end %>
<div class="visible-on-desktop"
<%= reuse_my_partial %>
</div>
<div class="visible-on-mobile"
<%= reuse_my_partial %>
</div>
This is simpler and slightly safer than using content_for because there is no global storage involved that something else might modify.
The rails docs linked to use instance #vars instead of local vars because they want it to be available to their layout template. That's a detail you do not need to worry about, because you're using it in the same template file.

How can I use the same layout multiple times on a page?

TL; DR: How can I use the same layout multiple times on a page? All attempts to render partials with recurring layouts go into the elements where the first time the layout is used.
Quick Info:
Using
Rails 2.3.14
Ruby 1.8.7
My partials use content_for, which is pretty handy with layouts.
Basically, my layout looks like this:
<div class="header">
<%= yield :modal_header %>
</div>
<div class="body">
<%= yield :modal_body or yield %>
</div>
Generally, this is how I use my modal layout:
<% content_for :modal_header do %>
header text / elements
<% end %>
<% content_for :modal_body do %>
body info / settings for object or warning message, etc
<% end %>
This is how I render modals on my page:
<%= render :partial => "section_options", :layout => "modal" %>
and that works really well
BUT
when, on the same page, I try to render another modal:
<%= render :partial => "section_content_options", :layout => "modal" %>
This is what happens to the modal at the top of my page (occurs first in the HTML document)
<div class="header">
-- header from second partial --
-- header from first partial --
</div>
<div class="body">
-- body from second partial --
-- body from first partial --
</div>
and then later in the page, where the content from the second partial is supposed to be:
everything is rendered correctly ...
For the sake of this example:
<div class="header">
section content options
</div>
<div class="body">
section contents options ... options
lil confirm / cancel buttons
</div>
Is there a way to fix this? This behavior really messes with javascript bindings as well. so. yup.
Is there a bug with layouts in rails 2.3.14? do I have to upgrade to rails 3 to get rid of this problem?
Modals should be rendered using partials, not layouts. Layouts are designed as a way of defining the overall layout of the entire site, not specific parts. Something like a modal (which by design is specific) should be a partial that's rendered either through a condition or JavaScript.

How would I go about adding a simple menu to a Rails Application? (How to use yield :menu)

I want to add a menu to my First Ever Rails Application. Nothing too complicated.
I've worked out that I should maybe reference it from application.html.erb, but after that I'm stuck.
Here's what I've got so far (It's not much)
<%= render :partial => "menu" %>
If I'm rendering a partial call "menu" in application.html.erb, where do I put the menu file, and what do I call it? Does it need to go in the controller of the view?
Can I call this partial from whichever layout subfolder I'm in?
Part II. If I want to show different content according to the view I'm in - how do I do this?
<body>
<p>[<%= yield :menu %>]</p>
<%= yield %>
</body>
</html>
I'm just learning Rails, so sorry about the stupid questions. Also, I'm interested in not only a solution, but also an idea of best practices.
In basic terms you are looking for the content_for helper. You put this inside your views which will then populate named blocks in partials or layouts such as :menu. You may elect, if you wish, to use partials to actually define the content for the content_for regions.
In a view:
<% content_for :menu do %>
<ul>
<li> ... </li>
<li> ... </li>
</ul>
<% end %>
or as:
<% content_for :menu do %>
<%= render :partial => "some_menu_content" %>
<% end %>
In the layout or partial:
<div id="menu">
<%= yield :menu%>
</div>
Watch this screencast from the Railscasts series for more information. It's old but still applicable

Resources