Ruby
Rails

Railsのlayout中の要素をページ毎に変更したい場合のプラクティス

More than 1 year has passed since last update.

Rails で開発をしている際、 layout ファイル中の要素を特定のページでのみ変更したくなることがしばしばある。

例えば、次のような app/views/layouts/application.html.haml があり、ページによってサイドメニューの内容を変えたいというユースケースを考える。

%html
  %head
    -# ...
  %body
    .side-menu
      -# この部分をページによって変更したい
      = render 'side_menu'
    %main
      = yields

ダサい例: pathによる分岐

次のようにページの URL によって分離する方法をよく見かける。

- if request.path == '/'
  = render 'root_side_menu'
- else
  = render 'side_menu'

このようなやり方でしか解決できない場合もあることは認めるが、しかしクソダサい方法だと思う。

共通部分であるレイアウト側が特定部分である URL や View のことを気にしなければならない感じが嫌だし、対応しなければならない URL が増えるたびに条件式を変更しなければならないのも嫌だ。View の Template ファイル中ではこの分岐に全く触れることが出来ないのも、ロジックが分散している感じがして嫌だ。

格好いい例: content_for を使う

もっと宣言的に書こう。 yieldcontent_for を使うほうがずっといい。

まず、 layout ファイルを次のように変更しよう。

%html
  %head
    -# ...
  %body
    .side-menu
      = yield :side_menu
    %main
      = yield

次に、View の Template に content_for で表示したい内容を渡す。

-# root の view の Template
= content_for :side_menu do
  = render 'root_side_menu'
-# 他の部分の Template
= content_for :side_menu do
  = render 'side_menu'

Rails の View の yield に Symbol を渡すと、その Symbol 名を引数に持つ content_for を探して、content_for のブロックを yield の部分に差し込む。
content_for の宣言がViewファイル間で重複するのが気になる場合はHelper メソッドに逃がすなどすればいい。