LoginSignup
26
34

More than 5 years have passed since last update.

Rails Ajax で要素を動的に変更する

Posted at

やりたい事

大項目 (セレクトボックス) を変更したら、それに紐づく小項目 ( チェックボックス ) を動的に変更したい。

宅配ピザのお店を管理するシステムで、お店の情報を登録する際、県 ( 大項目 ) を選択したら、それに紐づく配達可能な詳細エリア ( 小項目 ) が動的に変更するようなイメージ。

前提

バージョン
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


以上。

26
34
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
26
34