非同期通信を利用したアプリを作成しましたが
データが2重送信される事案が発生して、何時間も時間を無駄にしたので、その備忘録として。
1 rails newからGemfileの設定
$ rails new ajax-sample
gem 'jquery-rails'
gem 'jbuilder', '~> 2.5'
この二つを用意して
$ bundle install
これで、準備完了です。
2 MVCファイルの準備
今回は、コメントを入力して、表示するアプリを作成してみます。
$ rails g model Comment text:string
$ rails db:migrate
$ rails g controller Comments index
Rails.application.routes.draw do
root to: 'comments#index'
resources :comments, only: [:create]
end
class Comment < ApplicationRecord
validates :text, presence: true
end
class CommentsController < ApplicationController
def index
@comments = Comment.all
end
def create
binding.pry
@comment = Comment.create(comment_params)
respond_to do |format|
format.json
end
end
private
def comment_params
params.require(:comment).permit(:text)
end
end
Prefix Verb URI Pattern Controller#Action
root GET / comments#index
comments POST /comments(.:format) comments#create
<h1>Enter comment</h1>
<%= form_with model: Comment.new,url: :comments, class: "form" do |f| %>
<%= f.label "comment" %>
<%= f.text_field :text, class: "text" %>
<%= f.submit class: "button" %>
<% end %>
<ul>
<% @comments.each do |comment| %>
<li><%= comment.id %> <%= comment.text %></li>
<% end %>
</ul>
このままでは、コメントを作成するたびに、リダイレクトが発生して、読み込み待ちがおきてしまうため、非同期通信を導入してみましょう。
3 非同期通信の設定
非同期通信の流れとしては
送信ボタンをクリック→通常createアクションにデータが送信されるところをストップさせて、jsonデータのやりとりのみにする→データベースからjsonを引っ張ってきて、HTMLに追加する
といった流れです。
まずはapp/assets/javascriptsのapplication.jsに以下の記述があるか確認して、なければ追加します
//= require jquery
//= require rails-ujs
次に、app/assets/javascriptsにcomment.jsを作成します。
$(function() {
$('.form').on('submit', function(){
var formData = new FormData(this);
var href = $('.form').attr('action');
$.ajax({
type: 'POST',
url: href,
data: formData,
dataType: 'json',
processData: false,
contentType: false
})
これで、投稿したデータをjsonファイルとして、createアクションに送ることができます。
[5] pry(#<CommentsController>)> params
=> <ActionController::Parameters {"utf8"=>"✓", "authenticity_token"=>"fYbMbCkLGkIudntFybf8hgGaVwGm68ZDyaFD0R5O+5EeJ+9iSB65a2NYhJcBgbcISCpmRV036KMdsPQRJ1J1Nw==", "comment"=>{"text"=>"Adele"}, "controller"=>"comments", "action"=>"create"} permitted: false>
[6] pry(#<CommentsController>)> params[:comment]
=> <ActionController::Parameters {"text"=>"Adele"} permitted: false>
formDataを取得して、createコントローラーにjsonファイルとして、データを送信しています。
先ほど作成した、comments_controller.rbでは、jsonファイルを受け取った時には、jsファイルにデータを送るように設定しています。
def create
binding.pry
@comment = Comment.create(comment_params)
respond_to do |format|
format.json
end
end
次は、jbuilderをインストールしたので、インスタンス変数をjsファイルで使えるようにします。
app/viewsにcreate.json.jbuilderファイルを作成します。(jsonファイルを送ったアクション名を記述し、コントローラー名と同じviewフォルダに作成します。)
json.text @comment.text
json.id @comment.id
これで、jsファイルでは、@comment.textをcomment.textとして、記述することが可能になりました。
最後に、comment.jsのajax以降を以下のように記述します。
$(function() {
$('.form').on('submit', function(){
var formData = new FormData(this);
var href = $('.form').attr('action');
$.ajax({
type: 'POST',
url: href,
data: formData,
dataType: 'json',
processData: false,
contentType: false
})
.done(function(comment){
var html = `<li>${comment.id} ${comment.text}</li>`
$('ul').append(html);
})
.fail(function(){
alert("Enter text !!!");
$('.text').val('');
})
.always(function(){
$('.form')[0].reset();
$('.button').removeAttr('data-disabele-with');
})
return false; //.on('submit')の時はreturn false;でデータの2重送信を防ぐこと!!
});
});
これで、簡単な非同期通信を作成することができました。
ちなみに、送信ボタンをクリックしてイベントを発生させる場合(.on('click')の場合)は
$('.button').on('click', function(e){
e.preventDefault();
以上の記述でOKであり、return falseは必要ありません。
作者はここで数時間の時間を費やしてしまいました。。。。