跳至内容 跳至搜索

Action View 布局

Layouts 颠倒了在许多模板中包含共享头部和尾部的常见模式,以隔离重复设置中的更改。包含模式的页面如下所示:

<%= render "application/header" %>
Hello World
<%= render "application/footer" %>

这种方法是将公共结构与变化的内容隔离的好方法,但它很冗长,如果您想更改这两个包含的结构,您将不得不更改所有模板。

使用布局,您可以颠倒过来,让公共结构知道在哪里插入变化的内容。这意味着头部和尾部只在一个地方提到,如下所示:

// The header part of this layout
<%= yield %>
// The footer part of this layout

然后,您将拥有类似以下内容的内容页面:

hello world

在渲染时,内容页面将被计算出来,然后插入到布局中,如下所示:

// The header part of this layout
hello world
// The footer part of this layout

访问共享变量

Layouts 可以访问在内容页面中指定的变量,反之亦然。这允许您使用在渲染前不会实现的引用来创建布局。

<h1><%= @page_title %></h1>
<%= yield %>

…以及在渲染时实现这些引用的内容页面。

<% @page_title = "Welcome" %>
Off-world colonies offers you a chance to start a new life

渲染后的结果是:

<h1>Welcome</h1>
Off-world colonies offers you a chance to start a new life

布局分配

您可以声明式地指定一个布局(使用 `layout` 类方法),或者给它一个与您的控制器同名的布局,并将其放在 `app/views/layouts` 目录下。如果子类没有指定布局,它将通过普通的 Ruby 继承来继承其布局。

例如,如果您有一个 `PostsController` 和一个名为 `app/views/layouts/posts.html.erb` 的模板,该模板将用于 `PostsController` 中的所有操作以及继承自 `PostsController` 的控制器。

如果您使用模块,例如 `Weblog::PostsController`,您将需要一个名为 `app/views/layouts/weblog/posts.html.erb` 的模板。

由于您所有的控制器都继承自 `ApplicationController`,如果未指定或提供其他布局,它们将使用 `app/views/layouts/application.html.erb`。

继承示例

class BankController < ActionController::Base
  # bank.html.erb exists

class ExchangeController < BankController
  # exchange.html.erb exists

class CurrencyController < BankController

class InformationController < BankController
  layout "information"

class TellerController < InformationController
  # teller.html.erb exists

class EmployeeController < InformationController
  # employee.html.erb exists
  layout nil

class VaultController < BankController
  layout :access_level_layout

class TillController < BankController
  layout false

在这些示例中,我们有三种隐式查找场景:

  • BankController 使用“bank”布局。

  • ExchangeController 使用“exchange”布局。

  • CurrencyController 从 `BankController` 继承布局。

然而,当显式设置布局时,显式设置的布局优先。

  • InformationController 使用“information”布局,这是显式设置的。

  • TellerController 也使用“information”布局,因为父级显式设置了它。

  • EmployeeController 使用“employee”布局,因为它将布局设置为 `nil`,从而重置了父级配置。

  • VaultController 通过调用 `access_level_layout` 方法动态选择布局。

  • TillController 完全不使用布局。

布局类型

Layouts 本质上就是普通的模板,但是这个模板的名称不必是静态指定的。有时您想根据运行时信息(例如用户是否已登录)来交替使用布局。这可以通过将方法引用指定为符号或使用内联方法(作为 proc)来完成。

方法引用是变量布局的首选方法,用法如下:

class WeblogController < ActionController::Base
  layout :writers_and_readers

  def index
    # fetching posts
  end

  private
    def writers_and_readers
      logged_in? ? "writer_layout" : "reader_layout"
    end
end

现在,当处理对 `index` 操作的新请求时,布局将根据访问者是否登录而变化。

如果您想使用内联方法,例如 proc,请执行以下操作:

class WeblogController < ActionController::Base
  layout proc { |controller| controller.logged_in? ? "writer_layout" : "reader_layout" }
end

如果未给 proc 提供参数,它将在当前控制器的上下文中进行评估。

class WeblogController < ActionController::Base
  layout proc { logged_in? ? "writer_layout" : "reader_layout" }
end

当然,指定布局最常见的方式仍然是作为纯模板名称:

class WeblogController < ActionController::Base
  layout "weblog_standard"
end

模板将始终在 `app/views/layouts/` 文件夹中查找。但您也可以直接指向 `layouts` 文件夹。`layout "layouts/demo"` 等同于 `layout "demo"`。

将布局设置为 `nil` 会强制在文件系统中查找,如果不存在则回退到父行为。将布局设置为 `nil` 有助于重新启用模板查找,从而覆盖父级中先前设置的配置。

class ApplicationController < ActionController::Base
  layout "application"
end

class PostsController < ApplicationController
  # Will use "application" layout
end

class CommentsController < ApplicationController
  # Will search for "comments" layout and fall back to "application" layout
  layout nil
end

条件布局

如果您有一个默认应用于控制器所有操作的布局,您仍然可以选择渲染给定操作或一组操作而不使用布局,或者将布局限制为单个操作或一组操作。`:only` 和 `:except` 选项可以传递给 `layout` 调用。例如:

class WeblogController < ActionController::Base
  layout "weblog_standard", except: :rss

  # ...

end

这将为 `WeblogController` 分配“weblog_standard”作为布局,用于所有操作,除了 `rss` 操作,该操作将直接渲染,而不会包装布局。

`:only` 和 `:except` 条件都可以接受任意数量的方法引用,因此 `except: [ :rss, :text_only ]` 与 `except: :rss` 都是有效的。

在 action render 调用中使用不同的布局

如果您的绝大多数操作都使用相同的布局,那么定义一个控制器范围的布局是有意义的,如上所述。有时会有例外,一个操作想使用与控制器其余部分不同的布局。您可以通过将 `:layout` 选项传递给 `render` 调用来实现此目的。例如:

class WeblogController < ActionController::Base
  layout "weblog_standard"

  def help
    render action: "help", layout: "help"
  end
end

这将覆盖控制器范围的“weblog_standard”布局,并将使用“help”布局渲染 `help` 操作,而不是。

命名空间
方法
A
包含的模块

实例公共方法

action_has_layout?()

控制一个操作是否应该使用布局进行渲染。如果您想禁用当前操作的任何 `layout` 设置,以便它在没有布局的情况下进行渲染,那么要么在控制器中覆盖此方法以返回 `false`,要么在渲染之前将 `action_has_layout` 属性设置为 `false`。

# File actionview/lib/action_view/layouts.rb, line 372
def action_has_layout?
  @_action_has_layout
end