【Rails】セレクトボックスの内容をAjaxを使用して動的に変更する

スポンサーリンク

Ajax を使用してセレクトボックスの内容を動的に書き換える処理を実際に試した手順でまとめておきます。

事前準備

先ずは必要なモデルとコントローラを作成します。

$ ./bin/rails g model publisher name:string address:text
$ ./bin/rails g model Book name:string publisher:references
$ ./bin/rails g controller hello index books
$ ./bin/rake db:migrate

config/routes.rbには次のパスが追加されていると思います。

Rails.application.routes.draw do
  get 'hello/index'
  get 'hello/books'
  ...

rails consoleを使用して以下のようなデータをDBに登録して下さい。

Publisher.create(name: "O'Reilly Japan, Inc.", address: "Yotsuya")
Publisher.create(name: 'Gihyo inc.', address: 'Ichigaya')
Book.create(name: 'ゼロから作るDeep Learning', publisher_id: 1)
Book.create(name: '入門 Python 3', publisher_id: 1)
Book.create(name: 'Pythonによるデータ分析入門', publisher_id: 1)
Book.create(name: '初めてのPython', publisher_id: 1)
Book.create(name: '実践 機械学習システム', publisher_id: 1)
Book.create(name: 'パーフェクトRuby', publisher_id: 2)
Book.create(name: 'パーフェクト Ruby on Rails', publisher_id: 2)
Book.create(name: 'パーフェクトJava', publisher_id: 2)
Book.create(name: 'パーフェクトJavaScript', publisher_id: 2)
Book.create(name: 'パーフェクトPython', publisher_id: 2)

Viewの実装

Publisher と Book モデルの項目をセレクトボックスで選択するだけのViewをapp/views/hello/index.html.hamlで作成します。

%h1 Hello#index
= select_tag :publisher_id, options_from_collection_for_select(Publisher.all, :id, :name)
= select_tag :book_id, options_from_collection_for_select(Book.all, :id, :name)

Rails のサーバを起動してlocalhost:3000/hello/indexにアクセスして次のように表示されることを確認して下さい。

f:id:tasukujp:20161222151719p:plain

Ajax処理の実装

app/views/hello/index.html.hamlに以下を追記してください。Publisher のセレクトボックスが変更された時に Ajax で/hello/booksに GET リクエストを実行するコードです。

:javascript
  $(function() {
    $('#publisher_id').change(function() {
      $.get({
        url: "#{hello_books_path}",
        data: { publisher_id: $('#publisher_id').has('option:selected').val() }
      });
    });
  });

app/controllers/hello_controller.rbでは受け取ったpublisher_idに紐づく Book オブジェクトを取得します。メソッドと同名の JavaScript テンプレートを作成するため render メソッドは省略しています。

class HelloController < ApplicationController
  def index; end

  def books
    @books = Book.where(publisher_id: params[:publisher_id])
  end
end

レスポンスに使用する JavaScript テンプレートapp/views/hello/books.js.erbを用意します。処理内容としては option タグを新しく生成して丸ごと書き換えているだけです。

$('#book_id').html('<%= j(options_for_select(@books.pluck(:name, :id))) %>');

ちなみにoptions_for_selectは option タグを生成する View ヘルパーですが、index.html.haml で使用していたoptions_from_collection_for_selectとは使い方が違うだけで同じ用途になります。j( ... )escape_javascript( ... )の alias です。

動作確認

実際に Publisher のセレクトボックスを変更した時に、Book のセレクトボックスが変更されていれば成功です。/hello/books?publisher_id=2へのリクエストが実行されているのも確認できると思います。

f:id:tasukujp:20161222152051p:plain

f:id:tasukujp:20161222152102p:plain

今回は JavaScript テンプレートを使用したやり方でしたが JSON を返却して JavaScript 側で HTML を組み立てたりやり方はいくつかありそうです。