Вложенные макеты в Rails

У меня возникли проблемы с поиском способа сделать следующее:

Скажем, в моем приложении application.html.erb у меня есть следующий

<div id="one" >
    <%= yield %>
</div>

Затем я хочу иметь другой файл макета asdf.html.erb

<div id="two">
    <%= yield %>
</div>

Я хочу, чтобы конечный результат был

<div id="one>
   <div id="two">
      <%= yield %>
   </div>
</div>

Возможно ли это? Спасибо.

Ответ 1

По умолчанию application.html.erb - ваш макет. Вы можете отобразить по умолчанию макет по умолчанию, назвав его частичным из макета вашего приложения:

# app/views/layouts/application.html.erb
<div id="one" >
    <%= render "layouts/asdf" %>
</div>

# app/views/layouts/_asdf.html.erb
<div id="two">
    <%= yield %>
</div>

В результате вы получите следующее:

<div id="one>
   <div id="two">
      <%= yield %>
   </div>
</div>

Альтернативно, если вы хотите условно отображать макеты на основе контроллера по контроллеру, вам следует рассмотреть возможность использования вложенных макеты. Из документации:

На страницах, созданных NewsController, вы хотите скрыть верхнее меню и добавить правое меню:

# app/views/layouts/news.html.erb
<% content_for :stylesheets do %>
  #top_menu {display: none}
  #right_menu {float: right; background-color: yellow; color: black}
<% end %>
<% content_for :content do %>
  <div id="right_menu">Right menu items here</div>
  <%= content_for?(:news_content) ? yield(:news_content) : yield %>
<% end %>
<%= render template: "layouts/application" %>

Представления News будут использовать новый макет, скрыв верхнее меню и добавив новое правое меню внутри div "content".

Ответ 2

Самое чистое решение, которое я нашел на самом деле, появилось из этого репо: https://github.com/rwz/nestive

Мне не нужен весь камень. Если вы похожи на меня, вот как я достиг того, что хотел:

# application_helper.rb

  # From https://github.com/rwz/nestive/blob/master/lib/nestive/layout_helper.rb
  def extends(layout, &block)
    # Make sure it a string
    layout = layout.to_s

    # If there no directory component, presume a plain layout name
    layout = "layouts/#{layout}" unless layout.include?('/')

    # Capture the content to be placed inside the extended layout
    @view_flow.get(:layout).replace capture(&block)

    render file: layout
  end

Затем вы сохраняете /layouts/application.html.erb без изменений!

И вы можете создавать другие макеты. В моем случае /layouts/public.html.erb и /layouts/devise.html.erb:

# public.html.erb
<%= extends :application do %>
  <%= render 'partials/navbar' %>
  <div class="container margin-top">
    <%= yield %>
  </div>
<% end %>

# devise.html.erb
<%= extends :public do %>
  <div class="col-sm-6 col-sm-offset-3">
    <%= yield %>
  </div>
<% end %>

Работает как шарм! Я все еще улыбаюсь, наконец нашел свое решение.

Ответ 4

Вы также можете сделать следующее, чтобы условно отобразить подмакет:

# app/views/layouts/application.html.erb
<%= controller.controller_name.include?("foo") ? render("layouts/foo") : yield %>

# app/views/layouts/_foo.html.erb
<div class="bar">
    <%= yield %>
</div>

В сценариях с единственным подмассивом я нахожу это предпочтительным для подхода вложенного макета, описанного в Rails guide, потому что выполнение не" t нужно перейти от подмашины, к основному макету, а затем вернуться к подметке. Вместо этого он протекает более естественно, начиная с основного макета, переходя к суб-макету, а затем заканчивая в представлении.

Ответ 5

Если вы ищете чистое решение, которое не связывает application.html.erb с его наследующими элементами, жемчужина (как указано в другой, которая используется для этого, но она, похоже, не работает с Rails 5. Но это еще один способ сделать это: https://mattbrictson.com/easier-nested-layouts-in-rails

# Place this in app/helpers/layouts_helper.rb
module LayoutsHelper
  def parent_layout(layout)
    @view_flow.set(:layout, output_buffer)
    output = render(:file => "layouts/#{layout}")
    self.output_buffer = ActionView::OutputBuffer.new(output)
  end
end

Тогда asdf.html.erb становится

<div id="two">
    <%= yield %>
</div>
<% parent_layout 'application' %>

Будьте предупреждены, что это зависит от внутренних компонентов Rails и может перестать работать в будущей версии. Это вряд ли произойдет в ближайшее время, поскольку он работает минимум 3 года (в зависимости от даты связанного сообщения в блоге).