Apr 28

Date published: 4-28-2014

Rails version: 4.0.0

Layouts

One of the good things about using Ruby on Rails is that you can setup a basic site layout and it will automatically be applied to every page on the site. If you’ve ever worked on a site that didn’t use layouts, then you know what a timesaver this can be. Rather than having to modify every single page when you want to make a change to the header for example, you make one change in the layout file, and all the pages on your site will now use the updated header. There may be times when you want to disregard the default layout or use a different one entirely, such as when the user wants to print something and wants their information without all the extraneous elements like the header, footer, or navigation sidebar.

Ignoring the default layout

One thing you might want to do is just ignore the default application.html.erb file entirely. This could be useful when debugging the site because it strips out all the CSS and lets you see just the HTML that is being generated by the action. We tell Rails to ignore the layout in the controller, and there are several different ways to go about it. If you want every action in the controller to ignore the layout, you can do that with just one line:

class MyController < ApplicationController
  layout false

  # actions here
end

Chances are rather than wanting to ignore it for every action, you have one specific action in mind that you want to have a different layout. You can specify which actions to use with a layout using the only and except keywords. An example:

  layout false, only: [:show]
  layout false, except: [:index, :new]

You can also specify how to render the layout individually inside each action. This is useful because it means you don’t have to scroll around in your code to figure out why something isn’t being displayed the way you want it to look.

  def show
    render layout: false
  end

While removing the layout can be useful, chances are it’s not something we want to use for pages we show to our users. So how do we define a new layout and use that when rendering our pages?

Print layout

One case where we might want a different layout is on pages that the user wants to print out. We might add an action to our controller called print that displays the same information from our show action but without our header and footer. Before we make any changes, let’s look at our default layout, which might look something like this:

<!DOCTYPE html>
<html>
  <head>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title><%= yield(:title) %></title>
    <%= stylesheet_link_tag    "application", media: "all", "data-turbolinks-track" => true %>
    <%= javascript_include_tag "application", "data-turbolinks-track" => true %>
    <%= csrf_meta_tags %>
    <%= render 'layouts/shim' %>
  </head>
  <body>
    <%= render 'layouts/header' %>
    <div id="container-main" class="container">
      <% flash.each do |key, value| %>
      <% key = :warning if ![:success, :info, :warning, :danger].include?(key) %>
        <div class="alert alert-<%= key %> alert-dismissable">
          <button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>
          <%= value %>
        </div>
      <% end %>
      <%= yield %>
      <%= render 'layouts/footer' %>
      <%= debug(params) if Rails.env.development? %>
    </div>
  </body>
</html>

As you can see, we’re showing lots of extra information like the header, footer, error messages, and debug information. We don’t want any of that information on pages we’re planning to print, so let’s make a new layout to use when showing our print view. Create a new file named app/views/layouts/print.html.erb and fill it in with this:

<!DOCTYPE html>
<html>
  <head>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title><%= yield(:title) %></title>
    <%= stylesheet_link_tag    "application", media: "all", "data-turbolinks-track" => true %>
    <%= javascript_include_tag "application", "data-turbolinks-track" => true %>
    <%= csrf_meta_tags %>
    <%= render 'layouts/shim' %>
  </head>
  <body>
    <div id="container-main" class="container">
      <%= yield %>
    </div>
  </body>
</html>

Now we can make a print action in the controller to correspond with the page we’re going to show when the user wants to print.

  def print
    render :show, layout: 'print'
  end

Notice that we didn’t even have to create a new view file for the print action. We’re telling it to render exactly the same thing from the show action but use the print layout instead. This saves us from having redundant code that has to be maintained.

Leave a Reply

preload preload preload