跳至内容 跳至搜索

Action View Partials

还有一个用于在当前控制器内渲染子模板的便捷方法,该子模板依赖于单个对象(我们将这种子模板称为局部视图)。它依赖于这样的事实:局部视图应遵循以下命名约定:在文件名前加下划线 - 以将其与可以单独渲染的常规模板分开。

在 Advertiser#account 的模板中

<%= render partial: "account" %>

这将渲染 “advertiser/_account.html.erb”。

在 Advertiser#buy 的另一个模板中,我们可以有

<%= render partial: "account", locals: { account: @buyer } %>

<% @advertisements.each do |ad| %>
  <%= render partial: "ad", locals: { ad: ad } %>
<% end %>

这将首先使用传递的 `@buyer` 作为局部变量 `account` 渲染 advertiser/_account.html.erb,然后渲染 advertiser/_ad.html.erb 并将局部变量 `ad` 传递给模板进行显示。

The :as and :object options

默认情况下,ActionView::PartialRenderer 没有局部变量。:object 选项可用于将对象传递给局部视图。例如

<%= render partial: "account", object: @buyer %>

将 `@buyer` 对象提供给局部视图,该对象可作为局部变量 `account` 使用,并且等同于

<%= render partial: "account", locals: { account: @buyer } %>

使用 :as 选项,我们可以为该局部变量指定不同的名称。例如,如果我们想将其命名为 `user` 而不是 `account`,我们可以这样做

<%= render partial: "account", object: @buyer, as: 'user' %>

这等同于

<%= render partial: "account", locals: { user: @buyer } %>

渲染局部视图的变体

:variants 选项可用于渲染局部视图的不同模板变体。例如

<%= render partial: "account", variants: :mobile %>

这将渲染 _account.html+mobile.erb。此选项还接受多个变体,如下所示

<%= render partial: "account", variants: [:desktop, :mobile] %>

这将查找以下模板并渲染第一个存在的模板

  • _account.html+desktop.erb

  • _account.html+mobile.erb

  • _account.html.erb

渲染局部视图集合

局部视图的示例描述了一个熟悉的模式,即模板需要遍历一个数组并为每个元素渲染一个子模板。此模式已实现为一个接受数组并按其中元素名称的相同名称渲染局部视图的单个方法。因此,“使用局部视图”中的三行示例可以改写为一行

<%= render partial: "ad", collection: @advertisements %>

这将渲染 advertiser/_ad.html.erb 并将局部变量 `ad` 传递给模板进行显示。一个迭代对象将自动提供给模板,其名称形式为 `partial_name_iteration`。迭代对象知道当前对象在集合中的索引以及集合的总大小。迭代对象还有两个便捷方法,`first?` 和 `last?`。在本例中,模板将接收 `ad_iteration`。为了向后兼容,`partial_name_counter` 仍然存在,并映射到迭代的 `index` 方法。

渲染局部视图时可以使用 :as 选项。

您可以通过 :spacer_template 选项指定要在元素之间渲染的局部视图。以下示例将在每个广告局部视图之间渲染 advertiser/_ad_divider.html.erb

<%= render partial: "ad", collection: @advertisements, spacer_template: "ad_divider" %>

如果给定的 :collectionnil 或为空,render 将返回 nil。这将允许您使用此形式指定要显示的文本

<%= render(partial: "ad", collection: @advertisements) || "There's no ad to be displayed" %>

渲染共享局部视图

两个控制器可以共享一组局部视图并如下渲染它们

<%= render partial: "advertisement/ad", locals: { ad: @advertisement } %>

这将渲染局部视图 advertisement/_ad.html.erb,而不管它是从哪个控制器调用的。

渲染响应 to_partial_path 的对象

除了显式命名局部视图的位置外,您还可以让 PartialRenderer 完成工作,通过检查 to_partial_path 方法来选择正确的路径。

# @account.to_partial_path returns 'accounts/account', so it can be used to replace:
# <%= render partial: "accounts/account", locals: { account: @account} %>
<%= render partial: @account %>

# @posts is an array of Post instances, so every post record returns 'posts/post' on #to_partial_path,
# that's why we can replace:
# <%= render partial: "posts/post", collection: @posts %>
<%= render partial: @posts %>

渲染默认情况

如果您不打算使用 collections 或 layouts 等任何选项,您还可以使用 render 的简写默认值来渲染局部视图。例如

# Instead of <%= render partial: "account" %>
<%= render "account" %>

# Instead of <%= render partial: "account", locals: { account: @buyer } %>
<%= render "account", account: @buyer %>

# @account.to_partial_path returns 'accounts/account', so it can be used to replace:
# <%= render partial: "accounts/account", locals: { account: @account} %>
<%= render @account %>

# @posts is an array of Post instances, so every post record returns 'posts/post' on #to_partial_path,
# that's why we can replace:
# <%= render partial: "posts/post", collection: @posts %>
<%= render @posts %>

使用 layout 渲染局部视图

局部视图可以应用自己的 layout。这些 layout 与为整个 action 全局指定的 layout 不同,但它们的工作方式类似。想象一个包含两种用户类型的列表

<%# app/views/users/index.html.erb %>
Here's the administrator:
<%= render partial: "user", layout: "administrator", locals: { user: administrator } %>

Here's the editor:
<%= render partial: "user", layout: "editor", locals: { user: editor } %>

<%# app/views/users/_user.html.erb %>
Name: <%= user.name %>

<%# app/views/users/_administrator.html.erb %>
<div id="administrator">
  Budget: $<%= user.budget %>
  <%= yield %>
</div>

<%# app/views/users/_editor.html.erb %>
<div id="editor">
  Deadline: <%= user.deadline %>
  <%= yield %>
</div>

…这将返回

Here's the administrator:
<div id="administrator">
  Budget: $<%= user.budget %>
  Name: <%= user.name %>
</div>

Here's the editor:
<div id="editor">
  Deadline: <%= user.deadline %>
  Name: <%= user.name %>
</div>

如果给出了 collection,layout 将为 collection 中的每个项目渲染一次。例如,这两个片段具有相同的输出

<%# app/views/users/_user.html.erb %>
Name: <%= user.name %>

<%# app/views/users/index.html.erb %>
<%# This does not use layouts %>
<ul>
  <% users.each do |user| -%>
    <li>
      <%= render partial: "user", locals: { user: user } %>
    </li>
  <% end -%>
</ul>

<%# app/views/users/_li_layout.html.erb %>
<li>
  <%= yield %>
</li>

<%# app/views/users/index.html.erb %>
<ul>
  <%= render partial: "user", layout: "li_layout", collection: users %>
</ul>

给定两个名字分别为 Alice 和 Bob 的用户,这些片段返回

<ul>
  <li>
    Name: Alice
  </li>
  <li>
    Name: Bob
  </li>
</ul>

当前正在渲染的对象以及 object_counter 将在 layout 模板中作为局部变量可用,名称与在局部视图中可用的名称相同。

您还可以将 layout 应用于任何模板中的块

<%# app/views/users/_chief.html.erb %>
<%= render(layout: "administrator", locals: { user: chief }) do %>
  Title: <%= chief.title %>
<% end %>

…这将返回

<div id="administrator">
  Budget: $<%= user.budget %>
  Title: <%= chief.name %>
</div>

正如您所见,:locals hash 在局部视图及其 layout 之间共享。

方法
N
R

类公共方法

new(lookup_context, options)

# File actionview/lib/action_view/renderer/partial_renderer.rb, line 239
def initialize(lookup_context, options)
  super(lookup_context)
  @options = options
  @locals  = @options[:locals] || {}
  @details = extract_details(@options)
end

实例公共方法

render(partial, context, block)

# File actionview/lib/action_view/renderer/partial_renderer.rb, line 246
def render(partial, context, block)
  template = find_template(partial, template_keys(partial))

  if !block && (layout = @options[:layout])
    layout = find_template(layout.to_s, template_keys(partial))
  end

  render_partial_template(context, @locals, template, layout, block)
end