我在Ruby on Rails中编写的Web应用程序具有大量使用的内容面板。我正在尝试编写助手来使用更清晰的语法渲染这些面板。我对编写视图助手并不是很熟悉,所以这可能很简单。但是,当尝试使用传递给块中帮助器的“html”(erb代码)时,我会得到奇怪的结果,其中块被渲染两次。
相关代码如下。这个例子正在使用ERB,因为我简化了它以避免HAML出现任何可能的问题,但我得到了与HAML相同的结果。
我想要呈现的HTML是:
<section class="panel panel-default">
<header class="panel-heading">Contacts</header>
<div class="panel-body">
<p>Test</p>
</div>
</section>
这是我的帮手:
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
这是我的观点:
<%= panel :default do |p| %>
<%= p.header 'Contacts' %>
<%= p.body do %>
<p>Test</p>
<% end %>
<% end %>
这是呈现的HTML:
<section class="panel panel-default">
<header class="panel-heading">Contacts</header>
<p>Test</p>
<div class="panel-body">
<p>Test</p>
</div>
</section>
为什么会发生这种情况以及如何解决这个问题?我可能只是误解了有关视图块的内容。
由于
修改
我能够通过使用这种身体方法使其正常运作:
def body(&block)
@helper.concat tag(:div, class: 'panel-body').html_safe
block.call
@helper.concat '</div>'.html_safe
end
其中@helper是从主辅助模块ApplicationHelper中作为self传递给PanelHelper初始值设定项的。我也在调用p.body时删除=因为我们直接写入缓冲区。
答案 0 :(得分:2)
使用
<% p.body do %>
<p>Test</p>
<% end %>
即。 <% p.body do %>
代替<%= p.body %>
会取消视图中的第一个<p>Test</p>
。
def body(&block)
content_tag :div, class: 'panel-body' do
block.call
end
end
<%= p.body do %>
<p>Test</p>
<% end %>
会给你想要的输出。
答案 1 :(得分:2)
<%= p.body do %>
<p>Test</p>
<% end %>
因此,<p>Test</p>
出现两次(种类)因为第一个实例是在yield
内调用block.call
(或在您的情况下为body
)的结果ApplicationModule助手的代码。根据{{3}},视图中的块与普通块的工作方式不同,因为yield会自动将块调用的结果插入到HTML中(我仍然不确定原因,但我想弄清楚)
但这可以通过将nil
放在body
函数的末尾来证明:
def body(&block)
content_tag(:div, block.call, class: 'panel-body')
nil
end
会导致<p>Test</p>
被放入代码中(block.call
的结果,而不是content_tag调用的结果)。
但是,将block.call
更改为"<p>Test</p>"
def body(&block)
content_tag(:div, "<p>Test</p>", class: 'panel-body')
nil
end
将导致HTML中没有任何内容。所以它是视图助手中的yield/block.call
,它会产生一些意想不到的后果。所以这就是你看到<p>Test</p>
两次的原因。
解决方案,您可以执行@PrakashMurthy建议的操作并将块传递给content_tag,如此
def body(&block)
content_tag :div, class: 'panel-body' do
block.call
end
end
这是有效的,因为你的帮助器没有屈服于块,而是将它传递给一个方法,该方法没有yield
将代码插入模板的相同行为。
您还可以使用this railscast方法,该方法获取块的结果并将其作为字符串返回。
content_tag(:div, capture(&block), class: 'panel-body')