A web app I'm writing in Ruby on Rails has content panels that are used a lot. I'm attempting to write helpers to render these panels with a little cleaner syntax. I'm not very familiar with writing view helpers, so this may be simple. However, when trying to use the "html" (erb code) passed to a helper in a block, I get weird results where the block is rendered twice.
The relevant code is below. This example is using ERB because I simplified it to avoid any possible issues with HAML, but I was getting the same result with HAML.
The HTML that I want to be rendered is:
<section class="panel panel-default">
<header class="panel-heading">Contacts</header>
<div class="panel-body">
<p>Test</p>
</div>
</section>
This is my helper:
module ApplicationHelper
def panel(type = :default, &block)
tag = content_tag(:section, class: "panel panel-#{type.to_s}") do
block.call PanelHelper.new
end
tag
end
class PanelHelper
include ActionView::Helpers::TagHelper
include ActionView::Context
include ActionView::Helpers::CaptureHelper
def header(text = nil, &block)
if block_given?
tag = content_tag(:header, block.call, class: 'panel-heading')
elsif text
tag = content_tag(:header, text, class: 'panel-heading')
else
raise ArgumentError, 'Either a block must be given, or a value must be provided for the text parameter.'
end
tag
end
def body(&block)
content_tag(:div, block.call, class: 'panel-body')
end
end
end
This is my view:
<%= panel :default do |p| %>
<%= p.header 'Contacts' %>
<%= p.body do %>
<p>Test</p>
<% end %>
<% end %>
This is the HTML that is rendered:
<section class="panel panel-default">
<header class="panel-heading">Contacts</header>
<p>Test</p>
<div class="panel-body">
<p>Test</p>
</div>
</section>
Any ideas why this is happening and how to fix it? I'm probably just misunderstanding something about the view blocks.
Thanks
EDIT
I am able to get it functioning by using this body method:
def body(&block)
@helper.concat tag(:div, class: 'panel-body').html_safe
block.call
@helper.concat '</div>'.html_safe
end
where @helper is the passed in the PanelHelper initializer as self from the main helper module ApplicationHelper. Also I remove the = when calling p.body because we're writing directly to the buffer.
Test
".html_safe end end in for the body method, I do get the expected output (though obviously it's not dynamic)... So I need to figure out how to get the contents of the block without it being inserted immediately into the page. – Max Schmeling Jan 03 '15 at 07:11