やりたい事
大項目 (セレクトボックス) を変更したら、それに紐づく小項目 ( チェックボックス ) を動的に変更したい。
宅配ピザのお店を管理するシステムで、お店の情報を登録する際、県 ( 大項目 ) を選択したら、それに紐づく配達可能な詳細エリア ( 小項目 ) が動的に変更するようなイメージ。
前提
バージョン | |
---|---|
Ruby | 2.1.1 |
Rails | 4.2.0 |
以下のようなアソシエーションを想定。
app/models/pref.rb
# 県
class Pref < ActiveRecord::Base
has_many :detail_areas
end
app/models/detail_area.rb
# 詳細エリア
class DetailArea < ActiveRecord::Base
belongs_to :pref
end
app/models/store.rb
# 店舗
class Store < ActiveRecord::Base
belongs_to :pref
end
店舗への詳細エリアの格納は
Rails 複数チェックボックスで入力された値をカンマ区切りで DB へ格納する
を参考に、カンマ区切り形式で detail_areas という string カラムへの格納を想定。
1. フォームの詳細エリア部分 ( 動的に変更したい部分 ) を切り出し、部分テンプレートを作成。
app/views/stores/_form.html.erb
<%= render 'form_detail_areas', store: @store, detail_areas: @detail_areas %>
app/views/stores/_form_detail_areas.html.erb
<div id="detail_areas_select" class="field">
<%= label_tag 'store[detail_areas]' %><br>
<%
checked = store.detail_areas.presence || []
detail_areas.each do |detail_area|
checkedval = checked.include?(detail_area.id.to_s) ? true : false
concat check_box_tag 'store[detail_areas][]', detail_area.id, checkedval, {id: 'store_detail_area_ids_' + detail_area.id.to_s}
concat detail_area.name
concat " "
end
%>
</div>
2. 都道府県の変更を受けて Ajax 通信を行う CoffeeScript を作成。
app/assets/javascripts/stores.coffee
$(document).on 'change', '#store_pref_id', ->
# form の action をもとに store_id を取得。
# 新規登録の場合は stores が設定される。
work = $(this).parents('form').attr('action').split('/');
$.ajax(
type: 'GET'
url: '/stores/detail_areas_select'
data: {
store_id: work[work.length - 1],
pref_id: $(this).val()
}
).done (data) ->
$('#detail_areas_select').html(data)
3. Ajax 通信を受けるコントローラのルーティングを設定。
config/routes.rb
resources :stores do
collection do
:detail_areas_select
end
end
4. コントローラへ実際の処理 ( ビューへ渡す値の生成 ) を追加
app/controllers/stores_controller.rb
def detail_areas_select
# ajax によるリクエストの場合のみ処理
if request.xhr?
if params[:store_id].present? && params[:store_id].to_s != 'stores'
store = Store.find(params[:store_id])
else
store = Store.new
end
detail_ares = Detail_area.select(:id, :name).where(pref_id: params[:pref_id])
render partial: '/stores/form_detail_areas', locals: { store: store, detail_areas: detail_areas}
end
end
以上。