модуль ActionView::Layouts
Макеты 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
Условные макеты
Если макет по умолчанию применяется ко всем действиям контроллера, вы всё равно можете отрендерить отдельное действие или набор действий без макета либо ограничить использование макета одним действием или набором действий. В вызове layout можно указать параметры :only и :except. Например:
class WeblogController < ActionController::Base layout "weblog_standard", except: :rss # ... end
Для всех действий WeblogController будет назначен макет «weblog_standard», кроме действия rss, которое будет отрендерено напрямую, без обёртки из макета.
В условиях :only и :except можно указать произвольное количество ссылок на методы, поэтому допустимо except: [ :rss, :text_only ], как и except: :rss.
Использование другого макета при вызове render в действии
Если в большинстве действий используется один и тот же макет, вполне логично задать общий для контроллера макет, как описано выше. Иногда бывают исключения, когда для одного действия требуется макет, отличный от остальных действий контроллера. Для этого можно передать параметр :layout в вызов render. Например:
class WeblogController < ActionController::Base
layout "weblog_standard"
def help
render action: "help", layout: "help"
end
end
Это переопределит общий для контроллера макет «weblog_standard», и для действия help будет использоваться макет «help».
Открытые методы экземпляра
# File actionview/lib/action_view/layouts.rb, line 372 def action_has_layout? @_action_has_layout end
Определяет, следует ли рендерить действие с использованием макета. Чтобы отключить любые параметры layout для текущего действия и отрендерить его без макета, переопределите этот метод в контроллере так, чтобы он возвращал false для этого действия, либо задайте атрибут action_has_layout равным false перед рендерингом.
© 2004–2021 David Heinemeier Hansson
Licensed under the MIT License.