How do I create a sub-layout to remove duplication from page code?
I am building an application where seven of the pages are static, look
almost the same and share the same controller, StaticPagesController In
StaticPagesController I have just defined 7 empty actions that correspond
to the names of the views in the static_pages directory.
All of the static pages will be based on the general application layout
(layouts/application.html.erb, comprised of header and footer) and they
will all contain a "hero unit" (a big banner with a picture) at the top
and, below it, two columns. The first column will contain a paragraph of
text that will be different on each page, and on the second column I will
render a collection of ActiveModel objects (a different sub-set of objects
will be shown on each page, but I'd like to be able to write the rendering
code only once).
Question
What I would like to achieve is a layout that inherits the header and
footer from layouts/application.html.erb and allows me to only write the
code that will change between each page and avoid duplicating the
structure, the hero rendering and the collection rendering.
My (probably wrong) solution
I have found a way to do this that works, but after reading the Rails
guide about nested layouts I'm a bit confused and I'm not really sure if
it's the right way to do it. Here is how I did it:
I created layouts/static_page.html.erb with the following code:
<%= render partial: 'shared/hero', locals: { big: true, big_text:
hero_big_text } %>
<div class="container">
<div class="row">
<div class="col-sm-6">
<%= yield :introduction %>
<%= render 'hook_button' %>
</div>
<div class="col-sm-6 bo-offers-col">
The @collection will be rendered here
</div>
</div>
</div>
The first line renders the hero passing a few locals to it, including one
of them which has to be defined in the views. The rest of the code creates
the two columns and yields the introduction code in the first one and is
supposed to render the @collection object (which will be defined in the
controller) in the second one.
Here is what one of the views contains:
<%# Page title %>
<% provide :title, "Page title" %>
<%# Specify the content %>
<% content_for :hero, yield(:title) %>
<% content_for :introduction do %>
<h2>Page subtitle</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris
congue fermentum quam a lacinia.</p>
<p>Maecenas at sapien ut risus congue sodales eget quis enim. Etiam
pulvinar nulla non libero lobortis vulputate. Aliquam convallis sapien
nec velit sagittis molestie. Morbi porta pulvinar varius. Donec nec
velit at nisi ultrices commodo viverra eu enim.</p>
<% end %>
<%# Render the stuff %>
<%= render template: 'layouts/static_page', locals: { hero_big_text: false
} %>
First of all I provide a title which will be used both as the page <title>
and as the text inside the hero. I use content_for to also provide the
content of the first column.
Finally, I use <%= render template: 'layouts/static_page', locals: {
hero_big_text: false } %> to render the template I defined earlier,
passing the local I use to decide which type of hero to display (used in
the hero partial).
This solution works, but I'm unsure whether it's semantically correct or
if there are any better ways to do this. I haven't managed to find any
documents or questions addressing this exact issue and I'd like to know if
there are any better ways to do it.
No comments:
Post a Comment