erbの部分テンプレート完璧に理解した

2 min読了の目安(約2600字TECH技術記事

RoRのビューを書く方法はslimやhalmなどがありますが、デフォルトで使われているのはerbです。
erbでは、renderメソッドで切り出したviewを再利用できます。
このrenderメソッドが厄介で、使い方がやたら多様! チュートリアルのコピペでは限界が来たので、年貢の納め時と思って勉強してみました。

参考URL:
https://railsguides.jp/layouts_and_rendering.html
https://pikawaka.com/rails/partial_template#collectionオプション
https://techtechmedia.com/partial-template-rails/

まずrenderメソッドをどのファイルに書くかで、大きく二種類の使い方があります。

controllerに書き、viewファイルを丸ごと一枚描画してレスポンスする。

「controllerに書かれたactionは、本来なら最後にrenderが必要。しかし省略してもrailsが自動で補完してくれる」みたいなのをチュートリアルで見たことがあると思います。あれです。
これは部分テンプレートではないので、今回の記事では扱いません。redirect_toとの違いとか"Can only render or redirect once per action" エラーとかの情報が欲しい人は、参考URLからrailsガイドを読んでください。

viewに書き、ページ内に部分テンプレートファイルをレイアウトとして描画する。

今回使うのはこの2つ目の方法です。チュートリアルでは「パーシャル」と呼ばれているかもしれません。
viewディレクトリ内にある、アンダースコアから始まるファイルは部分テンプレートファイルです。一枚のhtml.erbファイルとしては完成しておらず、それ単体で描画する事は(多分)できません。他のhtml.erbファイル内で展開して使います。

ここで終わってほしかった。ここから部分テンプレートの使い方は更に3つほどに細分できます。

引数を使わず、部分テンプレートをそのまま引っ張ってくる。

チュートリアルで使われている_form.html.erbなどはこれに当たります。
tweetsディレクトリの中の_tweet.html.erbファイルを描画する場合、本来の書き方はこうです。

<%= render partial: 'tweets/tweet' %>

partial:は省略できます。するとこうなります。

<%= render 'tweets/tweet' %>

引数を渡し、部分テンプレートの中で変数に代入したりif分岐したりする。

localsの使い方ってチュートリアルとかに載ってたかなぁ?(記憶の彼方)
こう書くと、tweetsディレクトリの中の_tweet.html.erbファイルのtweetという変数に、今使っている@tweetという変数を代入させてから呼び出す事ができます。

<%= render partial: 'tweets/tweet', locals: { tweet: @tweet } %>

partialだのlocalsだのを省略するとこうなります。

<%= render 'tweets/tweet', tweet: @tweet %>

「こんなもん省略したうちに入るか! ツイートツイートうるさいんじゃい!」という声が聞こえてきそうですね……。
@tweetなんて変数を使っているからには_tweet.html.erbファイルを呼びたいのは当たり前だし、そんなファイルならtweetsディレクトリに入っているに決まっているし、部分テンプレートファイルの中で使われている引数名だって間違いなくtweetです。なのでここまで省略しても、railsが空気を読んで同じことをやってくれます。

<%= render @tweet %>

この一番短い書き方、後で使うのでちょっと覚えておいてください。

引数として配列を渡し、配列の要素の個数だけ部分テンプレートを繰り返し描画する。

チュートリアルで、userだかmicropostだかをずらっと並べていたのはこれです。
コレクションを渡し、コレクションのメンバの個数だけ繰り返すにはcollectionを使います。

<%= render partial: 'tweets/tweet', collection: @tweets %>

今度はpartialとcollectionだけを省略した'tweets/tweet', @tweetsみたいな書き方は(多分)できません。
その代わり、こう書いてもrailsが「ディレクトリ名」「部分テンプレートファイル名」「部分テンプレート内の変数名」の3つを気合いで推測してくれます。

<%= render @tweets %>

ここで今回のコードと、前述の「後で使うから覚えといて」のコードを見比べてください。
片や一回呼び出し、片や繰り返し呼び出しなのに、コード上では一文字違いです。
このようにrender(だけじゃくてrailsのメソッド全般)は単数形か複数形かで、挙動が大きく違うので注意しましょう。
以上がこの記事の骨子です。


以下蛇足。
今回はデータベースの内容をviewでtableに出力しようとしてハマりました。
部分テンプレートの中でtheadとtbodyを書き、view側でrender @booksとやってしまったせいでヘッダー行→データ一行目→ヘッダー行→データ二行目……と描画されてしまったのが始まりです。
renderの引数が複数形の@booksだったせいで、部分テンプレート内のヘッダー行まで何十回も描画されてしまったわけですね。
結局ヘッダー行だけ別のテンプレートに書き、booksテンプレートにはデータ行だけ書いて解決しました。

関係ありませんが、コレクションを全部並べたくない時はselectメソッドを使えば一部だけを描画できます。

<%= render @tweets.select{|t| t.post_on_today? } %>