Multiple content_for on the same page
Asked Answered
B

5

9

I have large block of HTML in my application that I would like to move into a shared template and then use content_for with yields to insert the necessary content. However, if I use it more than once in the same layout file the content_for just appends to the previous making that idea not work so well. Is there a solution to this?

<div class="block side">
    <div class="block_head">
        <div class="bheadl"></div>
        <div class="bheadr"></div>
        <h2><%= yield :block_head %></h2>
    </div>
    <div class="block_content">
        <%= yield :block_content %>
    </div>
    <div class="bendl"></div>
    <div class="bendr"></div>
</div>

and I use the following code to set the content for the block

    <%= overwrite_content_for :block_head do -%>
        My Block
    <% end -%>
    <%= overwrite_content_for :block_content do -%>
        <p>My Block Content</p>
    <% end -%>
    <%= render :file => "shared/_blockside" %>

The problem is if I use this multiple times on the same layout the content from the original block is appended to the secondary block

I have tried creating a custom helper method to get around it, however it does not return any content

  def overwrite_content_for(name, content = nil, &block)
    @_content_for[name] = ""
    content_for(name, content &block)
  end

I may also be going about this completely wrong and if there are any better methods for getting content to work like this I would like to know. Thanks.

Became answered 12/4, 2011 at 6:39 Comment(0)
D
2

You should define your overwrite_content_for as the following (if I understand your question correctly):

  def overwrite_content_for(name, content = nil, &block)
    content = capture(&block) if block_given?
    @_content_for[name] = content if content
    @_content_for[name] unless content
  end

Note, that in case your block yields nil, then the old content will be retained. However, the whole idea doesn't sound good, as you're obviously doing some rendering (or at least object instantiation) twice.

Dying answered 17/4, 2011 at 18:18 Comment(4)
This did the trick thanks. I do understand the issue of rendering twice but is seems that using content_for and layouts is preferable to that pasting in the large block of HTML everywhere I need it to show up.Became
Can't you get away with plain partials?Dying
Well in some cases I am passing a large amount of HTML content into the :block_content yield. Not sure how I would accomplish that with partials.Became
Well, basically if this HTML is static, then it probably should go into partial, and if it's dynamic, then to helper.Dying
P
18

In Rails 4, you can pass the :flush parameter to override content.

<%= content_for :block_head, 'hello world', :flush => true %>

or, if you want to pass a block,

<%= content_for :block_head, :flush => true do %>
  hello world
<% end %>

cf. this helper's source code for more details

Pathy answered 19/12, 2013 at 18:5 Comment(1)
If you DON'T pass :flush, you can also use content_for to append multiple snippets in one area. But you have to not use <%= yield %>, and use content_for to output as well. apidock.com/rails/v4.2.1/ActionView/Helpers/CaptureHelper/…Adolpho
D
2

You should define your overwrite_content_for as the following (if I understand your question correctly):

  def overwrite_content_for(name, content = nil, &block)
    content = capture(&block) if block_given?
    @_content_for[name] = content if content
    @_content_for[name] unless content
  end

Note, that in case your block yields nil, then the old content will be retained. However, the whole idea doesn't sound good, as you're obviously doing some rendering (or at least object instantiation) twice.

Dying answered 17/4, 2011 at 18:18 Comment(4)
This did the trick thanks. I do understand the issue of rendering twice but is seems that using content_for and layouts is preferable to that pasting in the large block of HTML everywhere I need it to show up.Became
Can't you get away with plain partials?Dying
Well in some cases I am passing a large amount of HTML content into the :block_content yield. Not sure how I would accomplish that with partials.Became
Well, basically if this HTML is static, then it probably should go into partial, and if it's dynamic, then to helper.Dying
F
2

You can always just pass the content directly and not rely on a block:

<% content_for :replaced_not_appended %>
Fallal answered 30/10, 2012 at 18:34 Comment(0)
R
1

I am not sure if I really understand your question - here is one approach in code that works:

view:

<% content_for :one do %>
  Test one
<% end %>

<% content_for :two do %>
  Test two
<% end %>

<p>Hi</p>

application.html.erb

<%= yield :one %>
<%= yield %>
<%= yield :two %>

Railsguides: http://guides.rubyonrails.org/layouts_and_rendering.html#using-content_for

Rudderhead answered 12/4, 2011 at 6:52 Comment(1)
Why the downvote? The original question has been edited after my answer. It contains much more detail now...Rudderhead
C
-1

You can use named content_for and yield blocks like this:

View:

<% content_for :heading do %>
  <h1>Title of post</h1>
<% end %>

<p>Body text</p>

<% content_for :footer do %>
  <cite>By Author Name</cite>
<% end %>

Then in a layout:

<%= yield :heading %>
<%= yield %>
<%= yield :footer %>

You can of course define those in any order.

Docs: http://api.rubyonrails.org/classes/ActionView/Helpers/CaptureHelper.html

Condyloid answered 12/4, 2011 at 6:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.