partialについて解説
はじめに
こんにちは!大ちゃんの駆け出し技術ブログです。
本記事では複数のページで共通のファイルを利用するための機能であるパーシャルについて解説をします。
基礎編を学習していた時のメモを見つけたため、初学者向けに記事を作ろうかなと思ったためです。 では早速見ていきましょう。
パーシャルとは
Ruby on Railsでは度々目にするパーシャルですが、一体どのようなものでしょうか?
Rails学習の初期段階では、「複数のページで同じレイアウト部分のファイルを共通化する方法」と説明されることが多いかと思います。
例えば、簡単なタスク管理アプリがあり、中に新規タスク登録ページとタスク編集ページがあるとします。
新規タスク登録ページ
タスク編集ページ
一目瞭然ですが、レイアウトがほとんど一緒です。 わかりやすい例を見せるためにシンプル過ぎるレイアウトを使用していますが、サイトのレイアウトの統一感のために、複数のページで似たようなレイアウトになるのはよくありそうです。
実際、これら2つのページのファイルの構成も共通部分が多くなり、無駄が多いように思えます。(RubyのDRY(Don't Repeat Yourself)に反していますね、、)。form_withから下は全く同じですよね。 なお、今回は参考にしたテキストでerbではなくslimが使われていたので、そのまま使用していますmm
新規タスク登録ページのファイル
h1 タスクの新規登録 .nav.justify-content-end / tasks_path = /tasks = link_to '一覧', tasks_path, class: 'nav-link' = form_with model: @task, local: true do |f| .form-group = f.label :name = f.text_field :name, class: 'form-control', id: 'task_name' .form-group = f.label :description = f.text_area :description, rows: 5, class: 'form-control', id: 'task_name' = f.submit nil, class: 'btn btn-primary'
タスク編集ページのファイル
h1 タスクの編集 .nav.justify-content-end = link_to "一覧", tasks_path, class: 'nav-link' = form_with model: @task, local: true do |f| .form-group = f.label :name = f.text_field :name, class: 'form-control', id: 'task_name' .form-group = f.label :description = f.text_area :description, rows: 5, class: 'form-control', id: 'task_name' = f.submit nil, class: 'btn btn-primary'
パーシャルを使用して共通化
本題であるパーシャルを使った共通化をします。
まずはじめにやることは、パーシャルテンプレートとしてapp/views/tasks/_form.html.slim
というファイルを作成します。
そしての新規タスク登録ページのファイルとタスク編集ページのファイルの共通部分を貼り付けます。( 2つのファイルから共通部分は削除しておきましょう!)
また、インスタンス変数@task
をtask
に変更してください。(後ほど理由を説明します!)
= form_with model: task, local: true do |f| .form-group = f.label :name = f.text_field :name, class: 'form-control', id: 'task_name' .form-group = f.label :description = f.text_area :description, rows: 5, class: 'form-control', id: 'task_name' = f.submit nil, class: 'btn btn-primary'
原則として_form.html.slim
のように、テンプレートファイルの名前の先頭には_(アンダースコア)を付けるのが一般的です。
次に、すっきりした新規タスク登録ページのファイルとタスク編集ページのファイルに同じコード render partial: "form", locals: { task: @task }
を記載します。
h1 タスクの新規登録 .nav.justify-content-end = link_to '一覧', tasks_path, class: 'nav-link' = render partial: "form", locals: { task: @task }
h1 タスクの編集 .nav.justify-content-end = link_to "一覧", tasks_path, class: 'nav-link' = render partial: "form", locals: { task: @task }
render partial: "form", locals: { task: @task }
のオプション部分partial
、locals
は一般的には省略されますが、説明のため敢えて省略しない書き方にしてあります。省略するとrender "form", { task: @task }
と書きます。
まず、partial:
で読み込むテンプレートファイルを指定します。
今回は同じフォルダにテンプレートファイルがあるので"form"
で読み込むことができますが、別フォルダにパーシャルファイルがある場合は、フォルダも含めて読み込むファイルを指定する必要があります。
locals:
は一見わかりにくいのですがざっくり説明すると、インスタンス@task
の中身をローカル変数task
に入れてパーシャルのテンプレートファイルに渡すという意味になります。
つまり、task: @task
の部分でインスタンス変数の値が変数taskに渡される。
task = @task
そしてlocals:
によってローカル変数としてパーシャルにtask
を渡すといったイメージです。
これにより、パーシャル内でローカル変数task
が有効になり、先ほどパーシャル内で記述したtask
も有効になります。
これだけの記述で共通化は終了です。 レイアウトも変わらず、共通化を行うことでファイルの中身もすっきりしました。
パーシャルにローカル変数を使う理由
今回の実装方法に反してインスタンス変数のままでも問題なく実装できます。
/ インスタンス変数@taskのまま = form_with model: @task, local: true do |f| .form-group = f.label :name = f.text_field :name, class: 'form-control', id: 'task_name' .form-group = f.label :description = f.text_area :description, rows: 5, class: 'form-control', id: 'task_name' = f.submit nil, class: 'btn btn-primary'
h1 タスクの新規登録 .nav.justify-content-end = link_to '一覧', tasks_path, class: 'nav-link' / ローカル変数を指定しないことでパーシャルにインスタンス変数が渡される = render partial: "form"
ではなぜわざわざローカル変数に変える必要があるのでしょうか。
実はインスタンス変数をパーシャルで使ってしまうと、再利用性が低下するという問題が発生します。
パーシャルにコントローラーで作成されたインスタンス変数を使ってしまうと、コントローラーとパーシャルの関係が密結合となってしまいます。つまりコントローラー側での変更が直接パーシャルに影響してしまうのです。これは複数のビューから呼びだされるパーシャルの再現性を低下させます。
例えば、コントローラーのインスタンス変数の名前を変えた場合は、パーシャルのインスタンス変数も同じように変えなければなりません。それに加えて、そのパーシャルを使っている他のビューのコントローラーでも同じインスタンス変数の名前に変更しなければならないのです。再利用性が崩壊しています、、、
終わりに
いかがでしたでしょうか。
パーシャルは非常によく使われると思います。
今回の例のようなシンプル過ぎるレイアウトなら簡単ですが、本来であればここまでシンプルなページはそうないでしょう。しかし、それでもUIの統一性を考慮する場合、パーシャルが使われるケースは多いでしょう。ヘッダーやフッターなど、どのページにも共通する部分にパーシャルを使うことは明らかです。
是非パーシャルを使いこなしてみてください!
以上、大ちゃんの駆け出し技術ブログでした!