Say you have several static but similarly designed web pages, but you don’t want to copy and paste all the same HTML again and again.

Here is an example of two views, both static pages, the use nested Rails views to do slightly different rendered from one another.

One page is called bar. It has a sidebar. The other is called baz – it does not.

Give you application.html.erb a fancy content_for yield.

application.html.erb

<!DOCTYPE html>
<html>

<%= render 'layouts/header' %>

<body>

<%= render 'layouts/navbar' %>
<div class='container-fluid'>

  <%= render 'layouts/alerts' %>
<div class="row-fluid">
<div class="col-md-9">
<div id="content"><%= content_for?(:content) ? yield(:content) : yield %></div>
</div>
<div class="col-md-3">
      <%= render 'shared/site_sidebar' %></div>
</div>
</div>
</body>
</html>

This is how rails let’s you dynamically define content that can be later filled in.

Setup your routes

routes.rb

  get '/bar',   to: 'static_pages#bar'
  get '/baz',   to: 'static_pages#baz'

Setup your controller to use your custom static page layout

static_pages_controller.rb

class StaticPagesController < ApplicationController

  def bar
    render layout: "static_page"
  end

  def baz
    render layout: "static_page"
  end

end

Define your custom static page layout

static_page.html.erb

<% content_for :content do %>
    <%= content_for?(:static_page) ? yield(:static_page) : yield %>
<% end %>
<%= render template: "layouts/application" %>

There is some magic here. This is using Rails content_for as a means us letting you define content for this layout sometime later. We are going to set two different kinds of content here shortly – one for bar, one for baz.

But basically this magical line

    <%= content_for?(:static_page) ? yield(:static_page) : yield %>

Says: If there is content for an area defined for static_page, yield it. Else do a regular yield.

Define your two slightly different static pages

Now comes the fun part. Here we define two pages. Bar has a side bar, and baz hides it. But they both share the same layout.

bar.erb

<% provide(:title, 'Bar Page') %>

<% content_for :static_page do %>
<h1>What is Bar?</h1>
<% end %>

baz.erb

<% provide(:title, 'Baz Page') %>

<% content_for :stylesheets do %>
    #sidebar {display: none}
<% end %>

<% content_for :static_page do %>
<h1>What is Baz?</h1>
<% end %>

This reason this works is that in my header layout, I defined a yield on the style sheet.

<style><%= yield :stylesheets %></style>

Here is the full source for that

_header.html.erb

<head>
  <title><%= yield(:title) %></title>
<style><%= yield :stylesheets %></style>

  <%= csrf_meta_tags %>

  <%= stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track': 'reload' %>
  <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
</head>

In baz, where we don’t want to show the sidebar, we can hide it by yielding some CSS into the style sheet like this.

baz.erb

<% content_for :stylesheets do %>
    #sidebar {display: none}
<% end %>

So now when baz renders, no side bar.

screen-shot-2016-12-04-at-9-55-48-am

But when bar renders, we get the full sidebar.

Screen Shot 2016-12-04 at 9.55.57 AM.png

I found this whole process of setting this up very confusing. So don’t feel bad if you don’t get it. There aren’t many examples out there.

But if you are determined not to repeat code over and over again like I felt, hopefully this will help.

Cheers – Jonathan

Links that kind of help

http://guides.rubyonrails.org/layouts_and_rendering.html#using-nested-layouts

Advertisements