Spec-Zone.ru › Ruby on Rails 8.1

модуль ActionView::Layouts

Подключенные модули:
ActionView::Rendering

Макеты 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».

Открытые методы экземпляра

action_has_layout? () Показать исходный код
# 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.

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API