ruby-on-rails - 为什么 View 中的 block 被渲染两次?

标签 ruby-on-rails haml erb helpers

我在 Ruby on Rails 中编写的一个 Web 应用程序有很多使用的内容面板。我正在尝试编写帮助程序以使用更简洁的语法呈现这些面板。我对编写 View 助手不是很熟悉,所以这可能很简单。但是,当尝试使用传递给 block 中的助手的“html”(erb 代码)时,我得到了奇怪的结果,其中 block 被渲染了两次。

相关代码如下。此示例使用 ERB,因为我对其进行了简化以避免任何可能的 HAML 问题,但我使用 HAML 得到了相同的结果。

我想要呈现的 HTML 是:

<section class="panel panel-default">
  <header class="panel-heading">Contacts</header>

  <div class="panel-body">
    <p>Test</p>
  </div>
</section>

这是我的 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

这是我的看法:
<%= 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">
    &lt;p&gt;Test&lt;/p&gt;
  </div>
</section>

任何想法为什么会发生这种情况以及如何解决它?我可能只是对 View block 有误解。

谢谢

编辑

我可以通过使用这个 body 方法让它发挥作用:
def body(&block)
  @helper.concat tag(:div, class: 'panel-body').html_safe
  block.call
  @helper.concat '</div>'.html_safe
end

其中@helper 是从主帮助程序模块ApplicationHelper 传入PanelHelper 初始化程序的self。此外,我在调用 p.body 时删除了 =,因为我们直接写入缓冲区。

最佳答案

<%= p.body do %>
  <p>Test</p>
<% end %>

所以,<p>Test</p>出现两次(有点),因为第一个实例是调用 yield 的结果(或在您的情况下为 block.call )在 body 内ApplicationModule 助手的代码。根据this railscast , View 中的 block 与普通 block 的工作方式不同,因为 yield auto 将 block 调用的结果插入到 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在一个有一些意想不到的后果的 View 助手中。所以这就是为什么你看到 <p>Test</p>两次。

解决方案,您可以执行@PrakashMurthy 建议的操作并将 block 传递给 content_tag 像这样
def body(&block)
  content_tag :div, class: 'panel-body' do
    block.call
  end
end

这是有效的,因为您的助手没有屈服于该 block ,而是将其传递给一个与 yield 行为不同的方法。将代码插入模板。
您也可以使用 capture 方法,它获取 block 的结果并将其作为字符串返回。
content_tag(:div, capture(&block), class: 'panel-body')

关于ruby-on-rails - 为什么 View 中的 block 被渲染两次?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27746311/

相关文章:

ruby-on-rails - 在 Rails 中编写可继承属性与基本赋值

ruby-on-rails - ruby /rails : only pass parameter if condition is true

ruby-on-rails - Rails - 在编译和渲染模板之前以编程方式获取模板。

ruby-on-rails - 使用 erb 渲染一个变量

sql - 搜索表中特定列的方法

ruby-on-rails - ruby on rails 无法在本地连接到 postgresql

ruby-on-rails - 关系 belongs_to 上的 activerecord where 子句

html - 西纳特拉和 HAML : auto-escape/convert unsafe HTML characters for a whole template?

javascript - 如何让rails变量进入onclick函数调用

ruby - HAML Ruby - 条件