Вложенные макеты в 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 %>
Работает как шарм! Я все еще улыбаюсь, наконец нашел свое решение.
Ответ 3
Переименуйте asdf.html.erb
в _asdf.html.erb
и перепишите application.html.erb
на следующее:
<div id="one">
<%= render 'asdf' %>
</div>
Подробнее о частицах здесь: http://guides.rubyonrails.org/layouts_and_rendering.html#using-partials
Ответ 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 года (в зависимости от даты связанного сообщения в блоге).