erbの部分テンプレート完璧に理解した
RoRのビューを書くテンプレートエンジンはslimやhalmなどがありますが、デフォルトで使われているのはerbです。
erbでは、renderメソッドで切り出したviewを再利用できます。
このrenderメソッドが厄介で、使い方がやたら多様! チュートリアルのコピペでは限界が来たので、年貢の納め時と思って勉強してみました。
参考URL:
まず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? } %>
Discussion