プログラミングスクールの最終課題で、フリマアプリのクローンサイトを作成している中で、ancestryを使って、商品出品ページのカテゴリーに親・子・孫のカテゴリーのセレクトボックスを実装しましたが、それらの値を編集ページでセレクトボックスに初期値として持たせるのにやたらと苦労したので、備忘録として残しておきます。
(まだupdateの処理を実装していないので、表示だけができて値が受け渡せていない可能性もあります。)
環境
Ruby 2.5.1
Rails 5.2.3
留意点
ancestryのインストールや、データの作成の方法については解説しないので、他の方の記事を参考にしてください。
コード
ビューから
edit.html.haml
.form-group-top
// = f.label :カテゴリー, class: "label" do
カテゴリー
%span.form-requires 必須
// %br
= f.select :category, options_for_select(@category_parent_array.map{|c|[c, {}]}, @item.category.parent.parent.name), {}, {class: 'select', id: 'parent_category'}
.form_group#children
= f.select :category, options_for_select(@category_child_array.map{|c|[c[:name], c[:id], {'data-category'=>c[:id], 'id'=>c[:id]}]}, @item.category.parent.id), {}, {class: 'select', id: 'child_category'}
.form_group#grandchildren
= f.select :category, options_for_select(@category_grandchild_array.map{|c|[c[:name], c[:id], {'data-category'=>c[:id], 'id'=>c[:id]}]}, @item.category.id), {}, {class: 'select', id: 'grandchild_category'}
= f.text_field :category_id, id: 'grand_child_result_id', type: 'hidden'
- 参考サイトの「初期値設定2」を見てもらうのがわかりやすいかと思います。
- 一番下のセレクトボックス(孫カテゴリー、下から2行目)にある、
@item.category.id
が孫カテゴリーに表示させたい初期値だと考えてください。(selectedにしたいもの) - あと、2~5行目はこの解説では関係ないので気にしないでください。
- この場合、孫カテゴリーのidが最終的にitemのテーブルに格納されているのでこの書き方になります。
- この値を基準に、子と親のカテゴリーを探します。
- 子は孫にとっての親なので、
@item.category.parent.id
- 親は孫にとって、親の親なので、
@item.category.parent.parent.name
- ちなみに、一番下にあるtext_fieldはcategory_idを格納するためにわざと設置しています。
ビューには表示したくないのでtype: 'hidden'で隠しています。
親カテゴリーと子・孫カテゴリーの表記の違い
親カテゴリー
edit.html.haml
= f.select :category, options_for_select(@category_parent_array.map{|c|[c, {}]}, @item.category.parent.parent.name), {}, {class: 'select', id: 'parent_category'}
- 親カテゴリーは、配列を作る時点で、nameしか格納していないので、このような表記になります。
- 対して、子と孫カテゴリーはidとnameの情報を格納。
子カテゴリー&孫カテゴリー
edit.html.haml
.form_group#children
= f.select :category, options_for_select(@category_child_array.map{|c|[c[:name], c[:id], {'data-category'=>c[:id], 'id'=>c[:id]}]}, @item.category.parent.id), {}, {class: 'select', id: 'child_category'}
.form_group#grandchildren
= f.select :category, options_for_select(@category_grandchild_array.map{|c|[c[:name], c[:id], {'data-category'=>c[:id], 'id'=>c[:id]}]}, @item.category.id), {}, {class: 'select', id: 'grandchild_category'}
- このoptions_for_selectを突き止めるまでが苦労しました。
- ただのselectと同じ書き方でもテキスト自体の表示はできますが、非同期通信でエラーが出たので、他の方法が必要でした。
-
c[:name]
→ セレクトボックスで表示させたいもの・・・今回はcategoryのname -
c[:id]
→ 選択肢の選択後に欲しい値(value属性値にセットする値)・・・今回はcategoryのid -
{'data-category'=>c[:id], 'id'=>c[:id]}
→ 追加属性記述・・・自分の場合は、idの記述が抜けていたせいで、非同期通信をしても値が空になっていました。
コントローラー
items_controller.rb
def edit
# 親セレクトボックスの初期値(配列)
@category_parent_array = ["---"]
# categoriesテーブルから親カテゴリーのみを抽出、配列に格納
Category.where(ancestry: nil).each do |parent|
@category_parent_array << parent.name
end
# itemに紐づいていいる孫カテゴリーの親である子カテゴリが属している子カテゴリーの一覧を配列で取得
@category_child_array = @item.category.parent.parent.children
# itemに紐づいていいる孫カテゴリーが属している孫カテゴリーの一覧を配列で取得
@category_grandchild_array = @item.category.parent.children
end
ビューに渡す配列の定義
- 子・孫カテゴリーの配列は、考え方としては、ビューの初期値と似ています。
- ただし、初期値が属しているグループを全て取得する必要が出てきます。
- 例えば、初期値がTシャツ/カットソー(半袖/袖なし)でancestryカラムが1/3だった場合、同じ1/3のものを全て取得します。
- 基準が
@item.category
です。- 子カテゴリー → @item.category(孫)から見た親(子)から見た親(親)の子供達なので
@item.category.parent.parent.children
- 孫カテゴリー → @item.category(孫)から見た親(子)の子供達なので
@item.category.parent.children
- 子カテゴリー → @item.category(孫)から見た親(子)から見た親(親)の子供達なので
-
@item
はbefore_action
で以下の処理を行って定義しています。
def set_item
@item = Item.find(params[:id])
@category = :category_id_eq_any
end
処理としては以上です。
(ついでに)ancestryのデータベース
- 実際のカテゴリーのデータベーステーブルを載せるとこんなかんじになってます。
- idがどのように紐づいているかはこれを見た方がわかりやすいかと。