Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

content_for with flush parameter #4226

Merged
merged 1 commit into from

3 participants

@grentis

Hi all,
I have a problem using content_for with nested layouts. I have a template like this one

...
    <h1><%= content_for :box_title %></h1>
    <div class="box_content">
      <%= yield %>
    </div>
...

called for render multiple partials from a view

...
  <%= render partial: 'agents_box', layout: 'layouts/internal/box' %>
  <%= render partial: 'users_box', layout: 'layouts/internal/box' %>
...

_agents_box.html.erb

<% content_for_clear :box_title do %>Server Management<% end %>

_users_box.html.erb

<% content_for_clear :box_title do %>Users<% end %>

Due to the behavior of the content_for function (concatenate) the page is not displayed correctly. (the first h1 contains "Server Management" and the second one "Server ManagementUsers")

In order to solve this kind of problem I updated the content_for method to accept another parameter (flush - false by default) used to decide if the content should be concatenated or replaced.

Using this in my example the page is displayed in the right way

_agents_box.html.erb

<% content_for :box_title, true do %>Server Management<% end %>

_users_box.html.erb

<% content_for :box_title, true do %>Users<% end %>

Hope this can be useful also for other people

@josevalim
Owner

It looks good, but we probably should do something like this: flush = content if block_given? so we don't always need to pass the second parameter as nil when using the block form.

@sikachu
Collaborator

Agree with @josevalim. Would you please fix that and force-push to your branch?

@grentis

I made the changes... I hope I have done the right things with the force-push process

@josevalim josevalim merged commit 5ac0273 into rails:master
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Dec 29, 2011
  1. @grentis
This page is out of date. Refresh to see the latest.
View
37 actionpack/lib/action_view/helpers/capture_helper.rb
@@ -81,8 +81,8 @@ def capture(*args)
# <%# This is the layout %>
# <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
# <head>
- # <title>My Website</title>
- # <%= yield :script %>
+ # <title>My Website</title>
+ # <%= yield :script %>
# </head>
# <body>
# <%= yield %>
@@ -110,7 +110,7 @@ def capture(*args)
# That will place +script+ tags for your default set of JavaScript files on the page;
# this technique is useful if you'll only be using these scripts in a few views.
#
- # Note that content_for concatenates the blocks it is given for a particular
+ # Note that content_for concatenates (default) the blocks it is given for a particular
# identifier in order. For example:
#
# <% content_for :navigation do %>
@@ -127,16 +127,37 @@ def capture(*args)
#
# <ul><%= content_for :navigation %></ul>
#
+ # If the flush parameter is true content_for replaces the blocks it is given. For example:
+ #
+ # <% content_for :navigation do %>
+ # <li><%= link_to 'Home', :action => 'index' %></li>
+ # <% end %>
+ #
+ # <%# Add some other content, or use a different template: %>
+ #
+ # <% content_for :navigation, true do %>
+ # <li><%= link_to 'Login', :action => 'login' %></li>
+ # <% end %>
+ #
+ # Then, in another template or layout, this code would render only the last link:
+ #
+ # <ul><%= content_for :navigation %></ul>
+ #
# Lastly, simple content can be passed as a parameter:
#
# <% content_for :script, javascript_include_tag(:defaults) %>
#
# WARNING: content_for is ignored in caches. So you shouldn't use it
# for elements that will be fragment cached.
- def content_for(name, content = nil, &block)
+ def content_for(name, content = nil, flush = false, &block)
if content || block_given?
- content = capture(&block) if block_given?
- @view_flow.append(name, content) if content
+ if block_given?
+ flush = content if content
+ content = capture(&block)
+ end
+ if content
+ flush ? @view_flow.set(name, content) : @view_flow.append(name, content)
+ end
nil
else
@view_flow.get(name)
@@ -164,8 +185,8 @@ def provide(name, content = nil, &block)
# <%# This is the layout %>
# <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
# <head>
- # <title>My Website</title>
- # <%= yield :script %>
+ # <title>My Website</title>
+ # <%= yield :script %>
# </head>
# <body class="<%= content_for?(:right_col) ? 'one-column' : 'two-column' %>">
# <%= yield %>
View
55 actionpack/test/template/capture_helper_test.rb
@@ -53,6 +53,13 @@ def test_content_for_with_multiple_calls
assert_equal 'foobar', content_for(:title)
end
+ def test_content_for_with_multiple_calls_and_flush
+ assert ! content_for?(:title)
+ content_for :title, 'foo'
+ content_for :title, 'bar', true
+ assert_equal 'bar', content_for(:title)
+ end
+
def test_content_for_with_block
assert ! content_for?(:title)
content_for :title do
@@ -63,6 +70,39 @@ def test_content_for_with_block
assert_equal 'foobar', content_for(:title)
end
+ def test_content_for_with_block_and_multiple_calls_with_flush
+ assert ! content_for?(:title)
+ content_for :title do
+ 'foo'
+ end
+ content_for :title, true do
+ 'bar'
+ end
+ assert_equal 'bar', content_for(:title)
+ end
+
+ def test_content_for_with_block_and_multiple_calls_with_flush_nil_content
+ assert ! content_for?(:title)
+ content_for :title do
+ 'foo'
+ end
+ content_for :title, nil, true do
+ 'bar'
+ end
+ assert_equal 'bar', content_for(:title)
+ end
+
+ def test_content_for_with_block_and_multiple_calls_without_flush
+ assert ! content_for?(:title)
+ content_for :title do
+ 'foo'
+ end
+ content_for :title, false do
+ 'bar'
+ end
+ assert_equal 'foobar', content_for(:title)
+ end
+
def test_content_for_with_whitespace_block
assert ! content_for?(:title)
content_for :title, 'foo'
@@ -74,12 +114,27 @@ def test_content_for_with_whitespace_block
assert_equal 'foobar', content_for(:title)
end
+ def test_content_for_with_whitespace_block_and_flush
+ assert ! content_for?(:title)
+ content_for :title, 'foo'
+ content_for :title, true do
+ output_buffer << " \n "
+ nil
+ end
+ content_for :title, 'bar', true
+ assert_equal 'bar', content_for(:title)
+ end
+
def test_content_for_returns_nil_when_writing
assert ! content_for?(:title)
assert_equal nil, content_for(:title, 'foo')
assert_equal nil, content_for(:title) { output_buffer << 'bar'; nil }
assert_equal nil, content_for(:title) { output_buffer << " \n "; nil }
assert_equal 'foobar', content_for(:title)
+ assert_equal nil, content_for(:title, 'foo', true)
+ assert_equal nil, content_for(:title, true) { output_buffer << 'bar'; nil }
+ assert_equal nil, content_for(:title, true) { output_buffer << " \n "; nil }
+ assert_equal 'bar', content_for(:title)
end
def test_content_for_question_mark
Something went wrong with that request. Please try again.