跳至内容 跳至搜索

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

现在,当处理索引操作的新请求时,布局将根据访问者是否登录而有所不同。

如果您想使用内联方法,例如 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 的所有操作(除 rss 操作外)分配“weblog_standard”作为布局,rss 操作将直接渲染,而不会在渲染的视图周围包装布局。

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

在操作渲染调用中使用不同的布局

如果您的大部分操作使用相同的布局,则定义如上所述的控制器范围布局非常有意义。 有时您会遇到一些例外,其中一个操作希望使用与控制器其余部分不同的布局。 您可以通过将 :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