Is there a way I can create a sub-layout with ERB?

I have 2 pages that have almost the same content except for stuff in
the middle. I'd basically like to have a sub-layout that wraps the
content that is different - exactly the way a normal layout file
would.

How can I call into a layout inside of erb? I'd like to do something
like this:

    <%= render :sub-layout => "reusable-wrapper" %>
        <lots-of-html-for-this-page />
    <% end %>

Inside of the reusable-wrapper, I am imagining a <%= yield %> just
like the normal layouts have.

I could use partials with a 'header' and 'footer' and wrap it that
way, but then those separate documents won't be well-formed html and
it will look like junk in my editor :frowning:

Anyway to achieve this with rails? Thanks!

You might want content_for with named yields.

The example you give isn't gonna work -- render doesn't define a block and so shouldn't have an end to it. It's just <%= render :partial => "xyz" %>

Either that or put an if statement into your layout and render either one partial or the other.

<%= if something_is_true
  render :partial => "abc"
else
  render :partial => "xyz"
end
%>

That should work. If that doesn't try this:

<% if something_is_true %>
<%= render :partial => "abc" %>
<% else %>
<%= render :partial => "xyz" %>
<% end %>

Note that the if/else/end statements evaluate only (<% syntax), whereas render syntax evaluate & output (<%= syntax)

but really, read up on how content_for and named yields work, once you grok them they are so great to work with. examples:

<%# This is the layout %>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
  <title>My Website</title>
  <%= yield :script %>
</head>
<body>
  <%= yield %>
</body>
</html>

<%# This is our view %>
Please login!
<% content_for :script do %>
  <script type="text/javascript">alert('You are not authorized to view this page!')</script>
<% end %>

I have 2 pages that have almost the same content except for stuff in
the middle. I'd basically like to have a sub-layout that wraps the
content that is different - exactly the way a normal layout file
would.

You can use yield and content_for, have a look at the Rails Guide on
Layouts and Rendering.

Colin

I don't think that's going to work. You see, I don't want to put this
sub-layout in the main layout at all. It is just 2 pages out of
hundreds. It doesn't belong in the main layout.

I guess I could pass in a variable into a partial to switch it... but
that seems like the wrong solution... no? I guess it'll have to do.
Just not what I expected.

I am already using content_for in many places on this application
actually. I do grok it. I don't I can use it for this.

Those are basically your two options...

Based on just what you've told us, an if statement inside a layout to choose which partial seems like the logical way to do it.

Also note you can render using a variable
<%= render :partial => @what_to_render %>

then you can se @what_to_render in your controller if you want. it kind of all depends on where the "decisioning" logic comes from

Maybe I should just be more explicit. How do I do this? :stuck_out_tongue:

I don't see how content_for can solve this problem. Even if it can, I
can imagine ways to solve it far more easily/elegantly. I essentially
want to take a bunch of header/footer html that is really big and put
it into a custom tag - to give you an analogy of the java world.
Freemarker also lets you create macros that do the very same thing.
There has to be an equivalent. I'd be really disappointed if there
wasn't.

This is unfortunate if that's how to solve it. What if 10 pages need
to use this same sublayout... do I need to have an if condition that
is 10 if/elsif's? That's not scalable :confused:

It would really be better to have a layout that is special to these 2
pages that I can execute manually and wrap the content that is on the
page. That way the content for that page is in the view it belongs to
- not in the partial with an if/switch statement.

This is unfortunate if that’s how to solve it. What if 10 pages need
to use this same sublayout… do I need to have an if condition that
is 10 if/elsif’s? That’s not scalable :confused:

Well like I said you can set a variable – local variable or instance variable – and tell render to use that variable.

Or write a helper method and call <%= render :partial => what_to_render() %>

then put the 10 if/else’s into a the what_to_render helper method. (or use case with a default case so you only put the special cases). It is ruby after all, almost anything can be made cleaner and more elegant.

It would really be better to have a layout that is special to these 2
pages that I can execute manually and wrap the content that is on the
page. That way the content for that page is in the view it belongs to

  • not in the partial with an if/switch statement.

You can switch the layout in the controller, either on a controller-by-controller basis, or you can decide in a method which layout to use.

render “hello.erb”, :layout => “xyz”

Without really looking at your code I can’t solve you problem, but I can throw a bunch of suggestions at you as to different ways it could be done :wink:

-Jason

I think I solved it. It needs 2 more files than I think it should...
but it's better than a N-level if/switch statement.

index.html.erb:

    <% content_for :page_title, "Support" %>

    <%= render :partial => "help_document_listing", :layout =>
"help_document_layout" %>

    <%= javascript_include_tag "controllers/supportController" %>

show.html.erb:

    <% content_for :page_title, "Support: #{@help_document.question}"
%>
    <% content_for :body_id, "normalBody" %>

    <%= render :partial => "help_document_view", :layout =>
"help_document_layout" %>

    <%= javascript_include_tag "controllers/supportController" %>

_help_document_layout.html.erb:

    <div id="help">
      <h2>Have a Question You Need Answered?</h2>

      <div id="helpCategories">
        <h3>Categories</h3>

        <ul>
          <li><%= link_to "Most Popular", support_path %></li>
          <%= render :partial => "help_category", :collection =>
@help_categories %>
        </ul>
      </div>

      <%= yield %>

      <p id="nevermind">
        <b>Can't find what you're looking for?</b><br/>
        <%= link_to "Contact our support team", contact_webpage_path
%>
        and we'll personally get back to you as quickly as we can.
      </p>
    </div>

This way the partial that belongs to 'index' or 'show' gets put into
the <%= yield %>, which is exactly what I had in mind.

My only beef with this solution is that you need to put the page's
content in a partial... when it would be better to put it on the page
itself. That's unfortunate.