【実現出来ること】
- 親子関係にあるセレクトボックスがあるとき、親のセレクトボックスの値で、子のセレクトボックスの選択肢を動的に制御する
- railsの"remote"の仕組みを利用する
【やり方】
環境
- rails 5
モデル・テーブルデータ
-
usersテーブルはid, color_id, vegetable_idカラムを持つ
id name color_id vegetable_id -
colorsテーブルはid, nameカラムを持つ
id name 1 緑 2 黄 3 青 -
vegetablesテーブルはid, name, color_idカラムを持つ
id name color_id 1 キャベツ 1 2 ほうれん草 1 3 小松菜 1 4 バナナ 2 5 レモン 2 6 トウモロコシ 2 7 人参 3 8 トマト 3 9 唐辛子 3
コード
- ルーティング
config/routes.rb
Rails.application.routes.draw do
resources :users
# selectからremote: trueでpostされるデータを受け取り、getで描画するための準備
match 'dynamic_vegetable', to: 'users#dynamic_vegetable', via: [:get, :post]
end
- view
app/views/users/new.html.erb
<%= form_for(@user, url: {action: :create}) do |f| %>
<!-- remote: trueを追加することでajaxでparams[:user][color_id]をpostできる -->
<div>
<%= f.select :color_id, options_from_collection_for_select( @color, :id, :name),
{prompt: '野菜の色を選択'}, {data: {remote: true, url: url_for(action: :dynamic_vegetable)}} %>
</div>
<!-- 色を選択するとここに動的にセレクトボックスが描画される -->
<div id="vegetable-select">
野菜
</div>
<%= f.submit '登録', name: 'commit' %>
<% end %>
- コントローラ
app/controllers/users_controller.rb
def new
@user = User.new
@color = Color.all
end
def dynamic_vegetable
# vegetablesをcolor_idで絞り込んで取得する。
@vegetable = Vegetable.where(color_id: params[:user][:color_id])
end
- セレクトボックスを動的に描画するためのview
app/views/users/dynamic_vegetable.js.erb
// 受け取った@vegetableを元にselectボックスを描画する
$('#vegetable-select').html(
"<%= j(select :user, :vegetable_id, options_from_collection_for_select(@vegetable, :id, :name)) %>"
);
解説
- viewでselectにremote: trueを設定する。これにより、選択した時にparams[:user][:color_id]が指定のurlにpostされる。このparamsを後のコントローラのアクションで使うことが出来る。
- ajaxのpostでを受け取ってアクションに流すためのルーティングを追加する。
- 「 match 'dynamic_vegetable', to: 'users#dynamic_vegetable', via: [:get, :post]」
- コントローラに、postでうけとったparamsのcolor_idに基づいて@vegetableを取得するアクションを追加する
- js.erbファイルを用意して、@vegetableに基づいてセレクトボックスを描画するコードを書く。セレクタで指定した箇所のみが部分的に更新され描画される。
メモ
- これぐらいのデータ数であれば、Color.all, Vegetable.allを一度のリクエストで全部JSONで渡し、ローカルでjavascriptを書いた方が早い
- データ数が多量(1000以上?)なら、ajaxで都度リクエストした方がよい。
- remote使わずに、javascriptでajax書いて、JSONを返すアクションを書いて実装することは可能だけど、個人的には、今回紹介した様に、remoteで書いた方がコードの見通しが良くなる気がするので好き。