RailsでAjaxを使ってサーバサイドと非同期通信する方法を紹介します。例として企業編集をAjaxを使って非同期で編集します。
目次
1. 編集画面(View)
Viewで企業編集画面を用意します。formで編集したい項目を囲い、submitボタンが押下されたらサーバに送られ、フロントで受け取ります。
今回はAjaxを使った非同期通信の流れを紹介するため、それ以外の処理やモデル、routes.rbやja.ymlなどの定義は適宜読み替えてください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | ... <div data-method="edit_error_message"></div> <%= form_for @company, url: edit_confirm_company_path(@company), remote: true, data: { method: :edit_confirm } do |f| %> ... <dl> <dt>企業名<span class="required">*</span></dt> <dd><%= f.text_field :company_name, class: "form_input", placeholder: "例:テスト企業" %></dd> </dl> ... <button type="submit"><%= t('view.buttons.edit') %></button> <% end %> ... |
2. サーバサイドで編集確認処理(Controller)
Viewから送られてきたパラメータを受け取り、バリデーションします(バリデーションはモデルに書いてある前提)。
バリデーションに引っかかった場合は、エラーを返します。
成功した場合は、セッションにパラメータを格納します。非同期処理なため、JSON形式で返すformat.jsonを使用します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | class CompaniesController < ApplicationController before_action :set_company, only: [:edit, :edit_confirm] COMPANY_PARAM = :company_param ... def edit_confirm respond_to do |format| @company.assign_attributes(edit_company_params) @company.validate if @company.errors.blank? session[COMPANY_PARAM] = edit_company_params format.json { render json: @company, status: :ok } else format.json { fail AsyncRetryValidationError, @company.errors } end end end ... private def set_company @company = ::Company.find(params[:id]) end def edit_company_paramss params.require(:company).permit(:company_name) end end |
3. サーバからの編集確認結果を受け取る(View・CoffeeScript)
サーバから帰ってきたデータはフロントで受け取ります。
バリデーションに成功した場合は、data-remodal-idがedit-modal-confirmの編集確認モーダルが呼び出されます。
バリデーションに失敗した場合は、Viewの<div data-method=”edit_error_message”></div>にエラーメッセージを表示させます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | ... $('[data-method="edit_confirm"]') .on 'ajax:success', (event, data, status, xhr)-> $('[data-remodal-id="edit-modal-confirm"]').remodal().open() return .on 'ajax:error', (event, xhr, status, error)-> data = $.parseJSON(xhr.responseText) element = $('[data-method="edit_error_message"]') show_error_message element, data.errors return show_error_message = (element, error_messages) -> element.addClass 'error_block' element.find('p').remove() for error_message in error_messages element.append '<p>' + error_message.message + '</P>' return ... |
バリデーションに成功した場合、以下の編集確認モーダルが表示されます。
モーダルに表示された編集ボタンが押下されたら、編集処理を行うアクションにデータを送ります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | ... <div id="remodal" class="remodal modal_common" data-remodal-id="edit-modal-confirm" data-remodal-options="hashTracking: false, closeOnOutsideClick: false"> <div class="modal_text"> <p>企業情報を編集します。よろしいですか?</p> </div> <%= form_for @company, url: edit_company_path(@company), remote: true, data: { method: :edit } do |f| %> <div class="modal_cancel"> <button type="button" data-remodal-action="cancel"><%= t('view.messages.cancel') %></button> <button type="submit"><%= t('view.messages.do_edit') %></button> </div> <% end %> </div> ... |
4. サーバサイドで編集処理(Controller)
バリエーションに成功したパラメータを先ほど格納したセッションから取り出し、編集処理を行います(実際の編集処理は割愛します)。
先ほどのCompaniesControllerに以下の処理を追記するような形です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | ... def edit respond_to do |format| edit_company = session[COMPANY_PARAM] company = ::Company::editr.new(@company, edit_company).edit if company.errors.blank? format.json { render json: company, status: :ok } else format.json { fail AsyncRetryValidationError, company.errors } end end end ... |
5. サーバからの編集結果を受け取る(View・CoffeeScript)
編集処理の結果をフロントで受け取ります。
編集処理に成功した場合は、data-remodal-idがedit-modal-completionの編集完了モーダルが呼び出されます。
1 2 3 4 5 6 7 8 9 10 11 12 | ... $('[data-method="edit"]') .on 'ajax:success', (event, data, status, xhr)-> data = $.parseJSON(xhr.responseText) $('[data-remodal-id="edit-modal-completion"]').remodal().open() return .on 'ajax:error', (event, xhr, status, error)-> Application.show_error_modal(xhr) return ... |
編集処理に失敗した場合は、エラーモダールを表示させます。エラーモーダルは他でも使用するためApplication.js.coffeeに共通化しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | ... this.Application.show_error_modal = (error, id = 'remodal-error') -> if $.isArray(error) error_messages = error else if error.responseJSON && error.responseJSON.errors error_messages = error.responseJSON.errors else error_messages = [] $element = $('[data-remodal-id="' + id + '"]') if error_messages.length > 0 $element.find('.modal_text').empty() for error_message in error_messages $element.find('.modal_text').append '<p>' + error_message.message + '</p>' $element.remodal().open() return ... |
編集に完了した場合、以下の編集完了モーダルが表示されます。
閉じるボタンを押下すると、画面がリロードされ編集された新しい情報が更新されます。
1 2 3 4 5 6 7 8 9 10 11 12 | ... <div id="remodal" class="remodal modal_common" data-remodal-id="edit-modal-completion" data-remodal-options="hashTracking: false, closeOnOutsideClick: false"> <div class="modal_text"> <p>編集が完了しました。</p> </div> <div class="modal_cancel"> <button type="button" class="btn_white btn_big" data-remodal-action="cancel", onclick="location.reload();">閉じる</button> </div> </div> ... |
編集に失敗した場合、以下のシステムエラーモーダルが表示されます。
エラーメッセージが存在しない場合は、デフォルトのメッセージを表示するようにしています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | ... <% options ||= {} options[:messages] ||= [] if options[:messages].blank? options[:messages] << 'システムエラーが発生しました。' options[:messages] << 'しばらく時間をおいてから再度お試しください。' end options[:remodal_id] ||= 'remodal-error' options[:remodal_options] ||= 'hashTracking: false, closeOnOutsideClick: false' %> <div data-remodal-id="<%= options[:remodal_id] %>" data-remodal-options="<%= options[:remodal_options] %>"> <div class="modal_text"> <% options[:messages].each do |message| %> <p><%= message %></p> <% end %> </div> <div class="modal_cancel"> <button type="button" data-remodal-action="cancel">閉じる</button> </div> </div> ... |
以上、RailsでAjaxを使った非同期処理の流れを紹介しました。