Multiple Level Nested Layout in Rails 3

2020-05-23 06:30发布

问题:

I have an application with a global application layout file application.html.haml. I then have multiple "controller stacks": for our main site, our admin portal, and our business site. For each of these, controllers are within a module and all inherit from the same BaseController. Each stack has it's own layout file. Within the stack, some controllers have layout files as well.

I would like all views (unless otherwise specified) to render inside multiple levels of nested layouts : application, "stack", "controller".

For example, for the Site::BlogController#show action, I'd like rails to render:

/site/blog/show.html.haml inside /layouts/site/blog.html.haml inside /layouts/site.html.haml inside /layouts/application.html.haml

I am having difficulty understanding how to insert /layouts/site.html.haml into the stack. It appears as though automatically, rails will render the action inside the controller layout inside the application layout, however, I can't see how to "insert" layouts into the render stack.

Any help is greatly appreciated, however, I have read all the rails guides to no avail, so a link to http://guides.rubyonrails.org/layouts_and_rendering.html#using-nested-layouts will not really be helpful.

回答1:

I reread the link i posted ( http://guides.rubyonrails.org/layouts_and_rendering.html#using-nested-layouts ) and realized I missed a key detail.

<%= render :file => 'layouts/application' %>

so, in Site::BaseController I have a call to layout 'site' and in /layouts/site.html.haml I have

= content_for :footer do
   -#Content for footer
= render :file => 'layouts/application'

Then in Site::BlogController which extends Site::BaseController I have layout 'site/blog' and in /layouts/site/blog.html.haml I have

=content_for :header do
  %h1 HELLO WORLD!

= render :file => 'layouts/site'

This then renders the layouts nested as described in the question. Sorry for missing this in my question. I should've read closer.



回答2:

if you create a helper like this:

# renders a given haml block inside a layout
def inside_layout(layout = 'application', &block)
  render :inline => capture_haml(&block), :layout => "layouts/#{layout}"
end

then you can define sublayout like this:

= inside_layout do
  # nested layout html here
  = yield

these layouts can be used like normal layouts.

more: http://www.requests.ch/blog/2013/10/30/combine-restful-rails-with-nested-layouts/



回答3:

I've done similar, but only used 1 level of sublayouts. Can easily be tweaked to allow multiple levels.

In controllers/application_controller.rb:

def sub_layout
  nil
end

In controller (for example blog_controller.rb):

def sub_layout
  "blog"
end

In layouts/application.html.erb rather than <%=yield%>:

<%= controller.sub_layout ? (render :partial => "/layouts/#{controller.sub_layout}") : yield %>

Make a partial layouts/_blog.html.erb:

...code
  <%=yield%>
...code

Repeat for other controller & sub layouts.

EDIT: If you need to do this on a per-action basis:

def sub_layout
  {
    'index' => 'blog',
    'new' => 'other_sub_layout',
    'edit' => 'asdf'
  }[action_name]
end


回答4:

I guess the simplest way to do it is by adding this line of code in the parent of a nested layout:

((render "layouts/#{controller_name}" rescue nil)|| yield )

you could add as many nested layouts as you want by only changing the path directory of the next layout to be rendered.

note: make sure your nested layout is named _layoutname.whatever and that your nested layout has a yield inside



回答5:

You can create a partial with a yield in it.

_my_sub_layout.html.erb:

<h3>I'm a sub layout and here's my content:</h3>
<%= yield %>

In some other view or even in your main layout application.html.erb render the partial as a layout:

<%= render layout: 'my_sub_layout' do %>
  <p>I'm the sub layout's content</p>
<% end %>