Edited at

Railsのpartialでcollectionを渡すときにはindexの変数が自動的に作られる

オブジェクトの配列を元にパーシャルを表示するときに、それがいま何番目かをカウントしたいときがあると思います。

Railsにおいて配列からパーシャルを表示したい場合には、= render 'partial_name', collection: @objectsを使う方がよいとされます。1

しかし、この方法ではindexの変数を渡すことができないように見えます。

そのため、collection:を使わずにeachを回してrenderを実行したくなりますが、その必要はありません。

実は、Railsのrender partial: ..., collection: ...を実行するとカウント変数が作られていて、表示したpartialの数をカウントしてくれています。2

そのカウント変数は、パーシャル内で#{variable_name}_counterという名前で作成されています。

(適宜、variable_nameを利用している変数に読み替えてください。)


コード例


users/index.html.slim

ul.user-list

= render partial: 'shared/user', collection: @users


shared/users.html.slim

/ この変数名はパーシャルの名前ではなく、`collection:`に渡した変数名を元に作成されます。

/ そして、indexは0からはじまるので注意
li.user-list-item
| (#{user_counter + 1}) #{user.full_name} # => (1) 田中 太郎


おまけ

要素が複数の場合はカウントを表示したいけれど、一つの場合は表示したくない、という状況があったりしますよね。

カウント変数はパーシャルが一つのときには定義されないため、以下のように実装するといいんじゃないかなと思います。


shared/_opening_job_position.html.slim

ul.job-list

- if @opening_job_postions.one?
= render partial: 'shared/opening_job_position', locals: { : @opening_job_positions.first }
- else
= render partial: 'shared/opening_job_position', collection: @opening_job_positions


shared/_opening_job_position.html.slim

li.job-list-item

/ 生成されるのは変数であるため、Object#tryでチェックすることはできないので注意。
- if defined?(opening_job_position_counter)
| (#{opening_job_position_counter + 1})
| #{opening_job_position.name}

「こうした方がよいでしょ」みたいなツッコミ・提案をお待ちしております。





  1. eachを使って= render 'partial_name', locals: { object: @object }をループするのは、パフォーマンスが悪くなるためできる限り避けたほうが良いです。 



  2. render partial: ..., collection: ...というふうにcollection:を渡した場合にのみにカウント変数が作られます。