Middlemanでcontent_forとyield_contentで超柔軟にlayoutを変更する方法

middleman

テンプレートからlayoutを微妙に変えられるcontent_forとyield_content

たとえば、記事ページと一覧ページでサイドバーの内容を変えたい時、どうしますか?Layout側でIf文で出し分けるという方法もありますね。
でも、Middlemanではそのような場合はcontent_foryield_contentを使うとコードの見通しが良くなります。

公式ページのこのページにある例を少し変えて説明します。

layout.erb

1
2
3
4
5
6
7
8
9
<html>
<head>
  <title>私のサイト</title>
</head>
<body>
  <%= yield %>
  <%= yield_content :sub_content %>
</body>
</html>

hello.erb

1
2
3
4
<% content_for :sub_content %>
<div>なんらかのコンテンツ</div>
<% end %>
<h1>Hello World</h1>

こうすることで、最終的に以下のようなhtmlが生成されます。

1
2
3
4
5
6
7
8
9
<html>
<head>
  <title>私のサイト</title>
</head>
<body>
  <h1>Hello World</h1>
  <div>なんらかのコンテンツ</div>
</body>
</html>

テンプレートからcontent_forでなんらかのコンテンツをセットしてあげて、レイアウト側から取り出すことができます。yield_contentのよい点は、仮にテンプレート側でcontent_forで何もセットしていない場合、エラーにならず何も出力しないことです。

この仕組みによって、テンプレートからメインコンテンツ以外(たとえばサイドバーやメニュー)を微妙に変えたい時など、レイアウト側のコードをif文などで汚さずに、テンプレート側で書くことができます。あるべきコードがあるべきところにあり見通しが良くなります。

Nested Layout(入れ子レイアウト)でも使えるcontent_forとyield_content

Middlemanではwrap_layoutというヘルパーメソッドを使うことで、レイアウトを何重にも入れ子にすることができます。このNested Layout(入れ子レイアウト)でもcontent_forとyield_contentは大活躍します。

公式ドキュメントのこのページにある例を少し変更して説明します。ただ、erbだと現状Nested Layout(入れ子レイアウト)は動かないので、slimに変換して説明します。(erbだと動かない理由はMiddlemanでwrap_layoutがドキュメント通りに動かない件をご覧ください)

入れ子の親が以下のようだとします。

layout.slim

1
2
3
4
5
6
7
html
  body
    header
      | ヘッダー
    = yield
    = yield_content :sub_content
    footer

子は以下のようだとします。

article_layout.slim

1
2
3
4
5
- content_for :sub_content
  | なんらかのコンテンツ
= wrap_layout :layout
  article
    = yield

これで、子側で設定した:sub_contentに"なんらかのコンテンツ"という文字列がセットされ、親側のレイアウトで取り出すことができます。
階層構造としては、テンプレート→子レイアウト→親レイアウトとなります。子レイアウトが何重になっても大丈夫です。

個人的に柔軟で便利だと思うのは、下の階層でcontent_forでセットしたコンテンツはそれより上ならどこの階層でもyield_contentで取り出してもOKです。なので、前述の例だと、テンプレートでセットしたものを、子レイアウトでも取り出してもいいし、親レイアウトで取り出してもOKです。

content_forとwrap_layoutの順番に注意

子レイアウトでの書き方で注意しなければならない点があります。content_forwrap_layoutの順番です。以下のように、content_forを後ろに書くと親側で取り出すことができません。(何も出力されない。)

1
2
3
4
5
= wrap_layout :layout
  article
    = yield
- content_for :sub_content
  | なんらかのコンテンツ

まとめ

いかがでしたでしょうか。レイアウト側にif文などで全てのコンテンツを書いていた方は一度、content_foryield_contentやNested Layoutを試してみてはいかがでしょうか。コードがわかりやすくなり、メンテナンスしやすくなるのは間違いないです。

※Middlemanのバージョン: 3.4.0

羊毛や小麦