Using multiple yields to insert content

Cristiano picture Cristiano · Nov 18, 2013 · Viewed 7.6k times · Source

I am trying to insert content on my page with yield but every time action removes whole content from the page. I have one main yield which is working fine:

<body>
    <%= render 'layouts/header' %>
    <div class="container">
      <%= yield %>
      <%= render 'layouts/footer' %>
    </div>
</body>

But inside that new content which is displayed on one page I have another yield:

<div class="container">
    <%= render 'admins/menu' %>
    <%= yield :admin %>
</div>

When user clicks on the menu which is rendered, new content should be displayed below that menu.

admins/_menu.html.erb

<div class="navbar">  
    <div class="navbar-inner">  
        <div class="container">  
            <ul class="nav">
                <li><%= link_to "Users", :controller => "admins", :action => "test" %></li>
                <li><%= link_to "1", ... %></li>
                <li><%= link_to "2", ... %></li>
                <li><%= link_to "3", ... %></li>
            </ul>
        </div>  
    </div>  
</div>  

Controller:

class AdminsController < ApplicationController

    def index
    end

    def test
        @users = User.paginate(page: params[:page])
    end
end

test.html.erb

<% content_for :admin do %>

<h1>All users</h1>

...

<% end %>

When I click on the option 'Users' from menu, page refreshes, menu disappears and nothing is displayed inside `body'. I want the content to be displayed below menu. How to use that second yield and accomplish this functionality?

I hope the question is not confusing. If question is confusing, please write me in comments and I will edit it immediately.

Thank you :)

Answer

marzapower picture marzapower · Nov 18, 2013

So, when you go to the index page you will get the piece of html that will be placed in the main layout, and this piece of html look like this:

<div class="container">
    <%= render 'admins/menu' %>
    <%= yield :admin %>
</div>

This code will yield :admin properly.

When you go to the test page you do not have this html code anymore (since it only belongs to the index method). So, anything you put in the content_for(:admin) block will be ignored since no-one is printing it.

What you probably want to do is creating a shared layout for all your admin pages. Follow this guide and you'll have your solution.

Solution

Edit the application.html.erb layout using this:

<%= content_for?(:content) ? yield(:content) : yield %>

instead of

<%= yield %>

Then create an admins.html.erb file inside the layouts folder to handle your admin pages' layout. Something like this:

<% content_for :content do %>
  <div class="container">
    <%= render 'admins/menu' %>
    <%= yield %>
  </div>
<% end %>
<%= render template: "layouts/application" %>

Will do fine. Then in the index.html.erb and test.html.erb just place regular HTML content, without using the content_for(:admin) block. Everything should work fine and you'll have your custom admin template, with a slightly different look from regular pages.