跳到内容 跳到搜索

Action View 捕获助手

CaptureHelper 提供方法来提取生成的标记,这些标记可用于模板或布局文件的其他部分。

它提供了一种通过 capture 将代码块捕获到变量中的方法,以及一种通过 content_for 将代码块捕获以在布局中使用的方法。

此外,它还提供了一种在使用流响应时的使用方法,通过 provide。有关更多信息,请参阅 ActionController::Streaming

方法
C
P

实例公共方法

capture(*args, &block)

capture 方法将模板的一部分提取为字符串对象。然后,您可以在模板、布局或助手中的任何位置使用此对象。

capture 方法可以在 ERB 模板中使用…

<% @greeting = capture do %>
  Welcome to my shiny new web page!  The date and time is
  <%= Time.now %>
<% end %>

…以及 Builder(RXML)模板。

@timestamp = capture do
  "The current timestamp is #{Time.now}."
end

然后,您可以在任何其他地方使用该变量。例如

<html>
<head><title><%= @greeting %></title></head>
<body>
<b><%= @greeting %></b>
</body>
</html>

capture 的返回值是由代码块生成的字符串。例如

@greeting # => "Welcome to my shiny new web page! The date and time is 2018-09-06 11:09:16 -0500"
# File actionview/lib/action_view/helpers/capture_helper.rb, line 47
def capture(*args, &block)
  value = nil
  @output_buffer ||= ActionView::OutputBuffer.new
  buffer = @output_buffer.capture { value = yield(*args) }

  string = if @output_buffer.equal?(value)
    buffer
  else
    buffer.presence || value
  end

  case string
  when OutputBuffer
    string.to_s
  when ActiveSupport::SafeBuffer
    string
  when String
    ERB::Util.html_escape(string)
  end
end

content_for(name, content = nil, options = {}, &block)

调用 content_for 会将代码块中的标记存储在一个标识符中,以便以后使用。为了在其他模板、助手模块或布局中访问此存储的内容,您需要将标识符作为参数传递给 content_for

注意:yield 仍然可以用来检索存储的内容,但调用 yield 在助手模块中不起作用,而 content_for 则可以。

<% content_for :not_authorized do %>
  alert('You are not authorized to do that!')
<% end %>

然后,您可以在模板中的任何位置使用 content_for :not_authorized

<%= content_for :not_authorized if current_user.nil? %>

这等效于

<%= yield :not_authorized if current_user.nil? %>

然而,content_for 也可以在助手模块中使用。

module StorageHelper
  def stored_content
    content_for(:storage) || "Your storage is empty"
  end
end

此助手的工作原理与普通助手相同。

<%= stored_content %>

您也可以在布局中使用 yield 语法与现有的 yield 调用一起使用。例如

<%# This is the layout %>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
  <title>My Website</title>
  <%= yield :script %>
</head>
<body>
  <%= yield %>
</body>
</html>

现在,我们将创建一个视图,其中包含一个 content_for 调用,该调用创建 script 标识符。

<%# This is our view %>
Please login!

<% content_for :script do %>
  <script>alert('You are not authorized to view this page!')</script>
<% end %>

然后,在另一个视图中,您可以执行以下操作

<%= link_to 'Logout', action: 'logout', remote: true %>

<% content_for :script do %>
  <%= javascript_include_tag :defaults %>
<% end %>

这将在页面上放置 script 标记,用于您的默认 JavaScript 文件集;如果您只在几个视图中使用这些脚本,这种技术很有用。

请注意,content_for 会按顺序将传递给特定标识符的代码块串联起来(默认)。例如

<% content_for :navigation do %>
  <li><%= link_to 'Home', action: 'index' %></li>
<% end %>

在另一个地方

<% content_for :navigation do %>
  <li><%= link_to 'Login', action: 'login' %></li>
<% end %>

然后,在另一个模板或布局中,这段代码将按顺序渲染两个链接

<ul><%= content_for :navigation %></ul>

如果 flush 参数为 truecontent_for 将替换传递给它的代码块。例如

<% content_for :navigation do %>
  <li><%= link_to 'Home', action: 'index' %></li>
<% end %>

<%# Add some other content, or use a different template: %>

<% content_for :navigation, flush: true do %>
  <li><%= link_to 'Login', action: 'login' %></li>
<% end %>

然后,在另一个模板或布局中,这段代码将只渲染最后一个链接

<ul><%= content_for :navigation %></ul>

最后,可以将简单的内容作为参数传递

<% content_for :script, javascript_include_tag(:defaults) %>

警告:content_for 在缓存中被忽略。因此,您不应该将其用于将被片段缓存的元素。

# File actionview/lib/action_view/helpers/capture_helper.rb, line 172
def content_for(name, content = nil, options = {}, &block)
  if content || block_given?
    if block_given?
      options = content if content
      content = capture(&block)
    end
    if content
      options[:flush] ? @view_flow.set(name, content) : @view_flow.append(name, content)
    end
    nil
  else
    @view_flow.get(name).presence
  end
end

content_for?(name)

content_for? 检查是否已使用 content_for 捕获了任何内容。

这对于根据视图中的内容以不同的方式渲染布局的某些部分非常有用。

<%# This is the layout %>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
  <title>My Website</title>
  <%= yield :script %>
</head>
<body class="<%= content_for?(:right_col) ? 'two-column' : 'one-column' %>">
  <%= yield %>
  <%= yield :right_col %>
</body>
</html>
# File actionview/lib/action_view/helpers/capture_helper.rb, line 215
def content_for?(name)
  @view_flow.get(name).present?
end

provide(name, content = nil, &block)

content_for 相同,但在与流一起使用时,会立即刷新回布局。换句话说,如果您希望在渲染给定模板时多次连接到同一个缓冲区,您应该使用 content_for,否则,使用 provide 来告诉布局停止查找更多内容。

有关更多信息,请参阅 ActionController::Streaming

# File actionview/lib/action_view/helpers/capture_helper.rb, line 194
def provide(name, content = nil, &block)
  content = capture(&block) if block_given?
  result = @view_flow.append!(name, content) if content
  result unless content
end