a.s.panchenko
a.s.panchenko

Reputation: 1236

Partial with multiple yields in rails

I'm looking for solution to have partial with multiple yields.

In real example I have this views structure:

Basic application.erb (/views/layouts/application.erb):

<!DOCTYPE html>
<head>
    <title>Some title</title>
</head>
<body>
<div class="page">
    <%= yield %>
</div>
</body>
</html> 

Some partial to DRY my code (/views/shared/content.erb):

<div class="content">
    <div class="sidebar">
        <%= yield :sidebar %>
    </div>
    <div class="main">
        <%= yield %>
    </div>
</div>

And controller view (/views/home/index.erb):

<%= render :partial => 'layouts/header' %>    
<%= render :partial => 'shared/navigation' %>

<% # It is close to what I want to do %>
<%= render :layout => 'shared/content' do %>
    <% content_for :sidebar do %>
        <%# This is will go to application.erb, not in content.erb %>
        <%= render :partial => 'shared/menu' %>
    <% end %>

    <%= yield %>
<% end %>

<%= render :partial => 'layouts/footer' %>

So the main issue here is to have a template block with multiple yield areas and ability to pass custom html or render another partial.

Upvotes: 5

Views: 5850

Answers (3)

Xiaohui Zhang
Xiaohui Zhang

Reputation: 1125

Here is my solution:

/views/shared/_content.erb

<div class="content">
    <div class="sidebar">
        <%= yield :sidebar %>
    </div>
    <div class="main">
        <%= yield %>
    </div>
</div>

views/home/index.erb


<%= my_content_tag do %>
    <% content_for :sidebar do %>
        <%= render :partial => 'shared/menu' %>
    <% end %>

    <%= yield %>
<% end %>

app/helpers/my_tags_helper.rb

module MyTagsHelper
  def my_content_tag(&block)
    sidebar_backup = @view_flow.content.delete(:sidebar)
    x = capture(&block)
    html = render('shared/content') { x }
    @view_flow.content[:sidebar] = sidebar_backup if sidebar_backup
    html
  end
end

The key is using capture to extract content_for block and pass it into @view_flow

Upvotes: 1

a.s.panchenko
a.s.panchenko

Reputation: 1236

In my case I've found solution like this.

On my controller view (/views/home/index.erb):

<% sidebar_content = render :partial => 'shared/slider' %>
<%= render :layout => 'shared/content', :locals => {:sidebar => sidebar_content} do %>
    <%= yield %>
<% end %>

The partial with multiple areas (/views/shared/content.erb):

<div class="content">
    <div class="sidebar">
        <%= sidebar %>
    </div>
    <div class="main">
        <%= yield %>
    </div>
</div>

This solution doesn't look pretty, but it works. I hope to find something better in near future.

Upvotes: 5

Wes Foster
Wes Foster

Reputation: 8900

This question is old, however, it's still relevant when I was searching for an answer on Google.

I've come up with a solution, while still not beautiful, works very well. The idea uses Rails' capture method, which takes a block and stores its contents into a variable:

controller.html.erb

<%= render 'shared/partial', body: capture { %>
  My body content
<% }, footer: capture { %>
  My footer content
<% } %>

shared/_partial.html.erb

<div id="body"><%= body %></div>
<div id="footer"><%= footer %></div>

Hope this helps someone!

Upvotes: 15

Related Questions