LoginSignup
178
179

More than 5 years have passed since last update.

Application Layoutを整理する3つの方法

Last updated at Posted at 2012-12-30

サービスを運営していると、

  • Admin以下のページだけちょっとヘッダーを変えたい
  • ABテストのために2種類のレイアウトを試してみたい
  • キャンペーン用にある期間だけ別のロゴを使いたい
  • ユーザー登録ページだけ簡易レイアウトにしたい

などなどの理由で、app/views/layouts/application.html.erbと大体同じだけど、ちょっと違うというレイアウトが欲しくなります。ただ、コピペでいろんなレイアウトを作ってしまうと後の変更が困難になります。Layoutでもデフォルト値や継承を使って再利用性の高いものを作りましょう。

方法1 Default content_for

あるyieldに対応するcontent_forがセットされなかった時のデフォルト値を定める方法です。

yieldは普通の文字列を返す関数で、値がセットされていなければ""が返ります。なのでempty?を使って値がセットされているか調べることができます。

app/views/layouts/application.html.erb
<header>
  <% if yield(:header).empty? %>
    <%= render 'header' %>
  <% else %>
    <%= yield :header %>
  <% end %>
</header>

方法2 Template Inheritance

Controllerの継承関係がViewのTemplateでも使えるという話です。

とりあえず以下のRails Castを見るのがいいと思います。
http://railscasts.com/episodes/269-template-inheritance

要約すると、共有したいpartial templateはapp/views/shared等ではなくapp/views/application下に置きます。例えば_header.rbという名前のpartialだとすると、以下のようになります。

app/views/layouts/application.html.erb
<%= render 'header' %>
app/views/application/_header.html.erb
<div class="head">Normal Page</div>

ここでproducts以下のページだけheader部分の内容を変えたいとしたら、以下のようにproductsディレクトリの下に同名の_header.html.erbを作り内容を上書きます。

app/views/products/_header.html.erb
<div class="head">Production Page</div>

お分かりかと思いますが、これは、以下のようにProductsControllerがApplicationControllerを継承している事を利用しています。

app/controllers/products_controller.rb
class ProductsController < ApplicationController

この方法は綺麗ですが、LayoutをControllerごとにしか変えられないという問題があります。

方法3 Parent Layout

layoutのテンプレートを親layoutを使って設定する方法です。各yieldの値が設定済みのlayoutを作れます。

基本的には以下のリンクを参照するといいです。
http://m.onkey.org/nested-layouts-in-rails-3

ただこれはrails3.0の方法を示していて、rails3.2では動きません。これを、http://stackoverflow.com/questions/9185344/ を参照して直したのが以下です。

app/helpers/application_helper.rb
module ApplicationHelper
  def parent_layout(layout)
    @view_flow.append(:layout, self.output_buffer)
    self.output_buffer = render(file: "layouts/#{layout}")
  end
end

これを使って、

app/views/layouts/admin.html.erb
<h1>Admin Panel</h1>
<div class="admin_navigation">Home | Posts | Users | Assets</div>
<%= yield %>

<% content_for :header do %>
<%= link_to image_tag('logo.png'), admin_root_path %>
<% end %>

<% parent_layout 'application' %>

のようにすれば、layout/application.html.erbをベースにlayout/admin.html.erbを作れます。

3つを併用した使用例

これらの方法はどれかを選ぶというよりかは併用すると良いです。

app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
<head>
  <title>App Title</title>
  <%= stylesheet_link_tag    "application", :media => "all" %>
  <%= javascript_include_tag "application" %>
  <%= csrf_meta_tags %>
</head>
<body>
  <div id="wrapper">
    <header>
      <% if yield(:header).empty? %>
        <%= render 'header' %>
      <% else %>
        <%= yield :header %>
      <% end %>
    </header>
    <article>
      <%= yield %>
    </article>
    <footer>
      <% if yield(:footer).empty? %>
        <%= render 'footer' %>
      <% else %>
        <%= yield :footer %>
      <% end %>
    </footer>
  </div>
</body>
</html>
app/views/application/_header.html.erb
<%= link_to image_tag('logo.png'), root_path %>
app/views/admin/base/_header.html.erb
<%= link_to image_tag('logo.png'), admin_root_path %>
app/views/application/_footer.html.erb
&copy; <%= Date.today.year %> Company Name. All rights reserved.
app/layout/no_header_footer.html.erb
<% content_for :header do %> <% end %>
<% content_for :footer do %> <% end %>
<% parent_layout 'application' %>
178
179
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
178
179