パーシャルへの変数の渡し方
パーシャルとは、テンプレートを別出しして再利用することです。
具体的な書き方とデバッグ内容をまとめていきます。
目次
each文
boards_controller.rb
def index @boards = Board.all end
index.html.erb
<% @boards.each do |board| %> <%= render partial: 'list', locals: { board: board } %> <% end %>
_list.html.erb
<% binding.pry %>
rails consoleで確認します。
[13] pry(#<#<Class:0x00007fae67c39570>>)> board.title => "Bellatrix Lestrange" [15] pry(#<#<Class:0x00007fae67c39570>>)> board.body => "To the well-organized mind, death is but the next great adventure."
@boardsからboard変数に入れて、renderのlocalsでboard変数にboard変数を入れる形になります。
この方法は、パーシャルをeachで回した数だけ読み込んでいますので、負荷が高いです。
※今回は、seed.rbでBoardに20個データを入れているので、20回パーシャルが呼ばれることになります。
※Rendered boards/_board.html.erbが複数回行われていることが分かります。
log
Started GET "/boards" for ::1 at 2020-10-03 15:59:48 +0900 Processing by BoardsController#index as HTML User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]] ↳ vendor/bundle/ruby/2.6.0/gems/activerecord-5.2.3/lib/active_record/log_subscriber.rb:98 Rendering boards/index.html.erb within layouts/application Board Load (0.3ms) SELECT "boards".* FROM "boards" ↳ app/views/boards/index.html.erb:16 Rendered boards/_board.html.erb (1.1ms) Rendered boards/_board.html.erb (0.2ms) Rendered boards/_board.html.erb (0.2ms) Rendered boards/_board.html.erb (0.2ms) Rendered boards/_board.html.erb (0.2ms) Rendered boards/_board.html.erb (0.2ms) Rendered boards/_board.html.erb (0.2ms) Rendered boards/_board.html.erb (0.2ms) Rendered boards/_board.html.erb (0.2ms) Rendered boards/_board.html.erb (0.2ms) Rendered boards/_board.html.erb (0.2ms) Rendered boards/_board.html.erb (0.1ms) Rendered boards/_board.html.erb (0.2ms) Rendered boards/_board.html.erb (0.2ms) Rendered boards/_board.html.erb (0.3ms) Rendered boards/_board.html.erb (0.2ms) Rendered boards/_board.html.erb (0.4ms) Rendered boards/_board.html.erb (0.2ms) Rendered boards/_board.html.erb (0.2ms) Rendered boards/_board.html.erb (0.2ms) Rendered boards/index.html.erb within layouts/application (25.1ms) Rendered shared/_header.html.erb (2.1ms) Rendered shared/_flash_message.html.erb (0.4ms) Rendered shared/_footer.html.erb (0.4ms) Completed 200 OK in 71ms (Views: 68.1ms | ActiveRecord: 0.6ms)
ちなみに、renderの書き方をまとめると
Goog
<%= render partial: 'list', locals: { board: board } %> <%= render 'list', board: board %>
Bad(undefined local variable or method `board')
- × <%= render partial: 'list', board: board %> - × <%= render 'list', locals: { board: board } %>
要するに、オプションを指定して書くのか、書かないのかはっきりしろということです。
collection
パーシャルにコレクションを渡すと、コレクションのメンバごとにパーシャルがレンダリングされて挿入されます。
要するに、パーシャルは一度だけレンダリングされるので、効率的だよね、ということです。
実際に書いてみましょう。
index.html.erb
<%= render partial: 'list', collection: @boards %> undefined local variable or method `board'
:asオプションを指定してローカル変数経由でアクセスしてみます。
index.html.erb
<%= render partial: 'list', collection: @boards, as: 'board' %>
rails consoleで確認します。
[10] pry(#<#<Class:0x00007febd7b6e4d0>>)* board.title => "James Potter" [11] pry(#<#<Class:0x00007febd7b6e4d0>>)> board.body => "Just because you have the emotional range of a teaspoon doesn’t mean we all have."
log
Started GET "/boards" for ::1 at 2020-10-03 16:03:55 +0900 Processing by BoardsController#index as HTML User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]] ↳ vendor/bundle/ruby/2.6.0/gems/activerecord-5.2.3/lib/active_record/log_subscriber.rb:98 Rendering boards/index.html.erb within layouts/application Board Load (0.2ms) SELECT "boards".* FROM "boards" ↳ app/views/boards/index.html.erb:16 Rendered collection of boards/_board.html.erb [20 times] (1.9ms) Rendered boards/index.html.erb within layouts/application (8.0ms) Rendered shared/_header.html.erb (3.0ms) Rendered shared/_flash_message.html.erb (0.3ms) Rendered shared/_footer.html.erb (0.3ms) Completed 200 OK in 51ms (Views: 46.1ms | ActiveRecord: 0.8ms)
これでレンダリングの数を1回に絞ることができました。
ただ、asオプションを省略するためにはどうすればいいか調べてみると、
Railsガイドに以下の記述を見つけました。
パーシャルを呼び出す時に指定するコレクションが複数形の場合、パーシャルの個別のインスタンスから、出力するコレクションの個別のメンバにアクセスが行われます。このとき、パーシャル名に基づいた名前を持つ変数が使用されます。上の場合、パーシャルの名前はproductであり、このproductパーシャル内でproductという名前の変数を使用して、出力されるインスタンスを取得できます。
パーシャル名(list)を基にした変数(lists)を使って、アクセスするよということです。
ですので、パーシャル名と変数名は揃えた方が良さそうですね。
修正してみます。
index.html.erb
<%= render partial: 'board', collection: @boards %>
ファイル名変更
mv _list.html.erb _board.html.erb
railsコンソールで確認します。
[1] pry(#<#<Class:0x00007fdcc4321f10>>)> board.title => "James Potter" [2] pry(#<#<Class:0x00007fdcc4321f10>>)> board.body => "Just because you have the emotional range of a teaspoon doesn’t mean we all have."
log
Started GET "/boards" for ::1 at 2020-10-03 15:55:46 +0900 Processing by BoardsController#index as HTML User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]] ↳ vendor/bundle/ruby/2.6.0/gems/activerecord-5.2.3/lib/active_record/log_subscriber.rb:98 Rendering boards/index.html.erb within layouts/application Board Load (0.5ms) SELECT "boards".* FROM "boards" ↳ app/views/boards/index.html.erb:16 Rendered collection of boards/_board.html.erb [20 times] (3.4ms) Rendered boards/index.html.erb within layouts/application (13.2ms) Rendered shared/_header.html.erb (1.8ms) Rendered shared/_flash_message.html.erb (0.3ms) Rendered shared/_footer.html.erb (0.2ms) Completed 200 OK in 68ms (Views: 61.2ms | ActiveRecord: 1.4ms)
こちらもレダンリングは1回になっています。
まとめ
collectionを使用して、レンダリングをまとめましょう。
次回は、N + 1問題について記載します。