I have multiple partials that I'm rendering, the idea being that the partials render in a row, and there's a different bit of content. So for the output like this:
<div class="container">
<div class="row" data-page='1'>
<h1>Page 1 Title</h1>
</div>
<div class="row" data-page='2'>
<h1>Page 2 Title</h1>
</div>
<div class="row" data-page='3'>
<h1>Page 3 Title</h1>
</div>
</div>
I am writing a code like this:
<div class="container">
<% (1..3).each do |p| %>
<div class="row" data-page="<%= p %>">
<%= render "partials/o#{p}" %>
<h1><%= yield :title %></h1>
</div>
<% end %>
</div>
<!-- o1 partial -->
<% content_for :title do %>
Page 1 Title
<% end %>
<!-- o2 partial -->
<% content_for :title do %>
Page 2 Title
<% end %>
<!-- o3 partial -->
<% content_for :title do %>
Page 3 Title
<% end %>
The problem with the above code is that on page 2 and page 3, it has contents from the previous pages as well. Like this:
<div class="container">
<div class="row" data-page='1'>
<h1>Page 1 Title</h1>
</div>
<div class="row" data-page='2'>
<h1>Page 1 Title
Page 2 Title</h1>
</div>
<div class="row" data-page='3'>
<h1>Page 1 Title
Page 2 Title
Page 3 Title</h1>
</div>
</div>
How can I avoid this and get back to the desired output?
You have completely misunderstood how the captures helper works.
Each yield
block essentially waits for the entire view to finish rendering so that its sure that all content has been provided. You can think of each named yield as a buffer. In your case even if you have multiple calls to yield they all point to the :title
buffer.
content_for
writes content to the buffer when called with 2+ arguments (content_for(:foo, 'bar')
or outputs the buffer when called with one argument. Note that content_for
concatenates the content by design:
<%= yield :poem %>
<% content_for :poem, "Roses are red" %>
<% content_for :poem, " violets are blue" %>
# => "Roses are red violets are blue"
This can be altered by using the flush: true
option which replaces anything previously captured.
<%= yield :poem %>
<% content_for :poem, "This gets flushed..." %>
<% content_for :poem, "Roses are red violets are blue", flush: true %>
# => "Roses are red violets are blue"
You can also use provide
which streams straight into yield block.
But for this case I would wonder if your not just using it improperly where a simple local variable would do the job with less hackyness.