ancestryというGemを使うと階層カテゴリを簡単に作ることができます。今回はancestryを使いつつ、動的なセレクトボックスを作る方法をご紹介します。
今回使用したコードはGithubからも見れるようにしておきました。
ancestry-sandbox
前提
- ruby-version: 2.6.3
- rails-version: 6.0.0
準備
ancestryをinstall
まずはGemのinstallから行います。テストデータを作成するためのseed-fu
のinstallもここで行います。
# Gemfile
gem 'ancestry'
gem 'seed-fu'
$ bundle install
migrationを実行
例えばcategory
モデルで階層化を行いたい場合は、以下を実行します。
$ rails g migration add_ancestry_to_category ancestry:string:index
$ rake db:migrate
class Category < ActiveRecord::Base
has_ancestry
end
テストデータを挿入
動作確認をするためのテストデータを作ります。(ここは冗長なコードになってしまっている気がするので、改善方法ご教授いただきたいです🙇♂️)
parents = %w[ 家電 ファッション 本 ]
electronics_child = %w[ 洗濯機 炊飯器 ]
fashion_child = %w[ アウター トップス ]
book_child = %w[ 自己啓発 技術書 ]
parents.each do |parent|
Category.seed do |c|
c.name = parent
end
end
electronics_child.each do |child|
Category.find_by!(name: '家電').children.create(name: child)
end
fashion_child.each do |child|
Category.find_by!(name: 'ファッション').children.create(name: child)
end
book_child.each do |child|
Category.find_by!(name: '本').children.create(name: child)
end
上記で使っているchildren
がancestryをinstallしたことで使えるようになったメソッドで、上記の例では家電>洗濯機, ファッション>アウターなどのカテゴリが生成されています。
jQueryをinstall
Ajaxで動的セレクトボックスを作るときにjQueryを使用するので、セットアップを行います。
$ yarn add jquery
const { environment } = require('@rails/webpacker')
const webpack = require('webpack');
environment.plugins.prepend('Provide',
new webpack.ProvidePlugin({
$: 'jquery/src/jquery',
jQuery: 'jquery/src/jquery'
})
)
module.exports = environment
動的セレクトボックスを作る
ここからが本題で動的セレクトボックスを作っていきます。親のカテゴリを選択すると、それに紐づく子供のカテゴリを表示するということです。
セレクトボックスを表示
親カテゴリとデフォルトで表示させる子カテゴリを変数代入し、最初のセレクトボックスで表示させます。
before_action :set_categories, only: %w[edit new]
private
def set_categories
@parent_categories = Category.roots
@default_child_categories = @parent_categories.first.children
end
<div class="actions">
<select id="parent_category">
<% @parent_categories.each do |c| %>
<option value="<%= c.id %>"><%= c.name %></option>
<% end %>
</select>
<select id="product_category_ids" name="product[category_ids]">
<% @default_child_categories.each do |c| %>
<option><%= c.name %></option>
<% end %>
</select>
</div>
ここまでで一旦セレクトボックスを2つ並べるところまでは完了しました。続いて、親カテゴリが変更されたときにAjaxを利用して子カテゴリが動的に切り替わる実装をしていきます。
ルーティングの設定
まずはルーティングにAjax用のパスを指定します。
get :dynamic_select_category, to: 'products#dynamic_select_category'
Ajax処理を記述
次にAjax処理を追記します。下記で、dynamic_select_category_path
にセレクトボックスで選択されたvalueをデータとして持たせてAjax送信を行っています。
<script type="text/javascript">
$(function() {
$('#parent_category').change(function() {
$.ajax({
url: "<%= dynamic_select_category_path %>",
type: 'GET',
data: { category_id: $('#parent_category').has('option:selected').val() }
});
});
});
</script>
子カテゴリを動的に表示
def dynamic_select_category
@category = Category.find(params[:category_id])
end
$('#product_category_ids').html('<%= j(options_for_select(@category.children.pluck(:name, :id))) %>');
コントローラ側では受け取ったcategory_id
からカテゴリを特定し、js.erbの方では#product_category_ids
の書き換えを行なっています。
ここでも上述したchildren
メソッドで子カテゴリを表示するようにしています。