QA@IT

Rails eachメソッド内でのAjax実装方法

4925 PV

環境:Ruby2.0.0-p195/Rails3.2.13

一覧上のドロップダウンリストを変更したタイミングで、
非同期Updateする処理を実装したいと考えていますが、
javascriptへの変数引渡し方法が分かりません。

現在のコード(一部抜粋)は以下の通りで、
javascript内で本来変数を指定すべき箇所(id、priority_id)に
定数(5、3)を指定すると、ドロップダウンリスト変更時に
正常にDBが更新されるところまで確認できました。

現在定数を指定している箇所を修正すれば正しく動くのではと
思っていますが、どのように修正すれば良いかご教授頂けますでしょうか。

[view]

<% @people.each do |person| %>
 <tr>
    <td>
      <%= person.name %>
    </td>
    <td>
      <%= form_for person , :remote => true do |form| %>
        <%= form.collection_select(:priority_id, Code.where(:code_type=>'priority'), :id, :description,{:include_blank=>false, :id => person.id}, {:id=>person.id, :class=>'input-mini', :onchange=>'priorityChange();'} ) %>
      <% end %>
    </td>
  </tr>
<% end %>

[javascript]

function priorityChange(){
    $.ajax({
        url: "people/update_priority",
        type: "GET",
        data: {
            id: 5
            priority_id: 3,
        },
        dataType: "html"
    });
}

[controller]

  def update_priority
    @person = Person.find(params[:id])
    @person.update_attribute(:priority_id, params[:priority_id])
  end

回答

前回の回答に誤りがありましたので訂正します。

ルーティング規則と対応するアクションを作成するところは同じですが、
ビュー内の各 select に以下の data 属性を付けてください。

  • data-remote = true
  • data-url = update_priority_person_path(person, format: 'json') の返値
  • data-method = put

jquery_ujs (unobtrusive JavaScript)が data-remote=true を設定された select 要素を探して、値が変化したときに自動的に対象URLにリクエストを行ってくれます。

作業を実際に確認できるrailsテンプレートを用意しました。

コマンドラインから次のコマンドを実行することによって最小限のアプリケーションが作成できますので、ローカルサーバを起動して http://localhost:3000/people にアクセスしてみてください。

rails new qaatit3063 -m https://gist.github.com/sakuro/5985264/raw/3c4f39bf8533bda4dcbb0e7e57313adfb2bbd63a/qaatit-3063-template.rb
編集 履歴 (1)
  • sakumo様
    頂いたrailsテンプレートで、希望通りの動作をする事が確認出来ました。
    本当に有難う御座います。
    自身のアプリに適用して動作したらまたご報告致します。
    -
  • sakuro様 無事自身のアプリに実装する事が出来ました。有難う御座います。
    また、素早いご回答、よりよい解決策の提示、テンプレート作成と
    最後までフォローして頂き大変感謝しております。大変勉強になりました。本当に有難う御座いました。
    -

本回答にはいくつか誤りがありますので別途再回答いたします

$.ajax を直に使わずとも、Rails の unobtrusive JS に任せることが出来ます。

resources :people

に、個別のpersonに対するルーティング規則を追加します。

resources :people do
  member do
    put 'update_priority'
  end
end

bundle exec rake routes すると

update_priority_person PUT    /people/:id/update_priority(.:format) people#update_priority

という規則が増えたのが確認できます。

/people/ID/update_priority.拡張子PUT すると update_priority アクションが呼ばれるようになります。

ビュー内に、更新URLにPUTするフォームを設けます。

<%= form_for @person, url: update_priority_person_path(@person, format: :json), remote: true do |f| %>
<%= f.collection_select(...) %>
<% end %>

update_priority メソッドには

{"person"=>{"priority_id"=>"..."}, "id"=>"..."}

のような params が渡されるので、既存の update を参考に実装します。

以上です。

編集 履歴 (1)
  • 現行のアプローチのままで実現したいということであれば、その旨コメントして下さい。 -
  • sakuro様
    早速のご回答有難う御座います、お返事遅くなり申し訳ございません。
    頂いた方式で実装してみたのですが、
    No route matches {:action=>"update_priority", :controller=>"people", :format=>:json, :id=>nil}となってしまいました。
    -
  • bundle exec rake routesは正しく設定されていたので、どこがおかしいかわからず、form_for内の @person を personに変更したらエラーは出なくなりましたがドロップダウン変更時に何もコールされなくなりました。 -
  • もしよろしければ、現行のアプローチのままで実現する方法をご教授願えますでしょうか。お手数おかけして申し訳ございません。 -
  • すみません。テストで書いたJavaScriptがビューファイルに残っていて、そっちが更新処理をやっていました。訂正バージョンをtemplate化しましたので、別途回答致します。 -
  • 現行アプローチのままやる方法ですが、1: priority_id はイベントの発生したselectの現在の値として取得出来る。2: person#id は hidden でフォームに埋め込んでおく。 3: selectやhiddenはonchangeハンドラの引数として渡す。 という感じで可能です。 -
ウォッチ

この質問への回答やコメントをメールでお知らせします。