##非同期通信(Ajax)
画面をリロードしなくても情報が反映されるWebアプリケーションの機能です。例えばTwitterだと更新しなくても情報がされますね。
非同期通信はAjax(エイジャックス)と呼ばれます。
Ajaxでは、レスポンスのデータにJSONという形式が使われることが多いです。
##JSON
データ交換を行うためのデータ記述形式の一種です。Rubyのハッシュと同様、キーとバリューの組み合わせでデータを表現する形式です。
【例】
{user_name: "testさん", created_at: "2020-03-17T10:35:13.000+09:00", content: "これがJSONの形です", image_url: null, id: 5}
非同期通信ではJavaScriptを利用
同期通信では、フォームのinputタイプがsubmitであるボタンを押すことでリクエストを行うことができました。ボタンを押すとリクエストが送られるという挙動は、HTMLであらかじめ定められているものです。
##デフォルトアクション
HTMLの要素を操作した際に定められている挙動です。aタグのようにクリックされると、リンク先のページを開く、という挙動です。
対して、今回の非同期通信ではJavaScriptのメソッドを利用してリクエストを送ります。そのため、フォーム要素のデフォルトアクションを無効にする必要があります。
リクエストに対してのレスポンスはJSON形式で返してほしい旨をリクエストに含めます。
##コントローラでJSON形式のデータを用意する
同期通信の際は特に指定せずともHTML形式のデータを返すようRailsが動いてくれます。
非同期通信をする際は、リクエストにJSON形式で返してほしい旨の情報が含まれているため、その場合の対処をコントローラのアクションに明記する必要があります。
##レスポンスするためのJSON形式のデータを準備
同期通信ではviewsディレクトリの中に○○.html.erbという形式でHTMLのファイルを準備して置くことでレスポンスとしてHTMLを返します。
非同期通信の場合、JSONのデータをレスポンスとして返す必要があります。同期通信の際と同様viewsフォルダの中にJSON形式のファイルを作成します。この時のファイル名は、○○.json.jbuilderという形式になります。
##JavaScriptでレスポンスを受け取り、HTMLを操作してToDoを追加
非同期通信では、ページがリロードされることはありません。代わりに、レスポンスとして帰ってきたJSONのデータを利用してHTMLを操作します。
JSONのデータはユーザーが投稿したToDoのデータなので、これをToDoリストの一番後ろに付け加えるようJavaScriptを書きます。
##respond_to
「リクエストがHTMLのレスポンスを求めているのか、それともJSONのレスポンスを求めているのか」を条件に条件分岐してくれます。
【例】
respond_to do |format|
format.html { render ... } # この中はHTMLリクエストの場合に呼ばれる
format.json { render ... } # この中はJSONリクエストの場合に呼ばれる
end
HTMLを返す場合は、該当するビューを呼びその中でデータを生成しますが、JSONを返す場合はRubyのハッシュをrenderメソッドの引数として渡すだけでJSONに変換されます。そのため、以下のようにコントローラーから直接データを返すことができます。
【例】renderというメソッドに{json: { id: @user.id, name: @user.name }}というハッシュを引数として渡しています。
respond_to do |format|
format.json {
render json: { id: @user.id, name: @user.name }
}
end
JSONでレスポンスできるように
【例】
def create
@todo = Todo.create(todo_params)
respond_to do |format|
format.html { redirect_to :root }
format.json { render json: @todo}
end
end
respond_to doを使用し、リクエストされたformatによって処理を分けるようにします。今回はhtmlと非同期通信のためのjsonを扱うようにしました。フォーマットがjsonの時、この後jsファイル側で作成したtodo(@todo)を使用するためにrenderメソッドを使用し、作成したtodoをjson形式で返すようにします。
##JavaScriptを記載して送信時に要素を取得
今回は、todo.jsというファイルをassets/javascripts以下に作成します。
Ruby on Railsでは、アプリケーション作成時にjquery-railsというgemをインストールし、assetsディレクトリ以下のapplicaton.jsファイルで//= require jqueryこのように記述することでjQueryを読み込んでいます。
【例】todo.jsにTodoの一覧ページからフォームが送信された時に、フォームに入力された値をコンソールに出力します。
$(function() {
$('.js-form').on('submit', function(e) {
e.preventDefault();
var todo = $('.js-form__text-field').val();
console.log(todo);
});
});
submitイベントを使い、フォーム(js-form)が送信された時に処理が実行されるようにイベントを設定します。
フォームが送信された時に、デフォルトだとフォームを送信するための通信がされてしまうので、preventDefault()を使用してデフォルトのイベントを止めます。
##非同期通信でリクエスト
処理の流れ
1.フォームの送信が行われた時にAjaxによる非同期通信を始める
フォームに入力された値を取得する
Ajaxを行う記述をする
2.TodosコントローラのcreateアクションにてTodoの保存を行う
3.処理後にjsonを返す
4.非同期通信の終了後に受け取ったjsonを利用してHTMLを構築する
5.4で構築したHTMLをViewに差し込む
【例】フォームの送信が行われた時にAjaxによる非同期通信を始めるフォームの送信が行われた時にAjaxによる非同期通信を始める
$(function() {
$('.js-form').on('submit', function(e) {
e.preventDefault();
var todo = $('.js-form__text-field').val();
$.ajax({ #追記〜
type: 'POST',
url: '/todos.json',
data: {
todo: {
content: todo
}
},
dataType: 'json'
}) #〜追記
});
});
##$.ajax
jQueryで非同期通信を行うための記述です。.ajaxの部分がメソッドの呼び出しとなっています。
ajaxメソッドには、Rubyのハッシュのようなキーバリュー形式で引数を渡します。JavaScriptでは、このような形式のオブジェクトはオブジェクト型と呼ばれます。読みやすいように改行して縦に並べていますが、以下のようなキーとバリューの組み合わせです。
{type: 'POST', url: '/todos.json', data: { todo: { content: todo } }, dataType: 'json' }
dataというキーに対してのバリューはオブジェクト型であり、さらにそのバリューもオブジェクト型になるという、入れ子構造となっています。
Ajaxで非同期通信に必要なオプションを設定しており、それぞれの意味は以下のようになります。
type、、、HTTP通信の種類を記述する。通信方法は、GETとPOSTの2種類がある。
url、、、リクエストを送信する先のURLを記述する。
data、、、サーバに送信する値を記述する。
dataType、、、サーバから返されるデータの型を指定する。
通信方法はPOSTで、'/todos.json'というURLに{ todo: { content: todo(テキストフィールドに入力された値)} }を送信する。サーバから値を返す際は、jsonになります。
$(function() {
$('.js-form').on('submit', function(e) {
e.preventDefault();
var todo = $('.js-form__text-field').val();
$.ajax({
type: 'POST',
url: '/todos.json',
data: {
todo: {
content: todo
}
},
dataType: 'json'
})
.done(function(data) { #追記〜
var html = $('<li class="todo">').append(data.content);
$('.todos').append(html);
$('.js-form__text-field').val('');
})
.fail(function() {
alert('error');
}); #〜追記
});
});
##doneとfail
ajaxメソッドの後につけることで、非同期通信が成功した際/失敗した際に行う処理を書くことができます。両方とも、ajaxメソッドとセットとなるメソッドです。doneは通信が成功したときに、failは通信が失敗したときに動きます。
##data
doneのうしろにあるdataという変数には、リクエストによって返ってきたレスポンスが代入されます。この場合のレスポンスは、非同期通信によって作成したTodoにあたります。
def create
@todo = Todo.create(todo_params)
respond_to do |format|
format.html { redirect_to :root }
format.json { render json: @todo} #追記
end
end
doneにもfailにも、メソッドの引数としてやってほしい処理を記述します。
今回はdoneのあとに、取得したJSONからli要素を作成しtodo一覧のリストに追加し、フォームに入力された値を空にする処理を記述しています。failのあとにはエラーが起きたことを示すアラートを表示する処理を記述しています。
最後に、htmlを生成するvar html = $('
$(function() {
function buildHTML(todo) { #追記〜
var html = $('<li class="todo">').append(todo.content);
return html;
} #〜追記
$('.js-form').on('submit', function(e) {
e.preventDefault();
var todo = $('.js-form__text-field').val();
$.ajax({
type: 'POST',
url: '/todos.json',
data: {
todo: {
content: todo
}
},
dataType: 'json'
})
.done(function(data) {
var html = buildHTML(data); #追記
$('.todos').append(html);
$('.js-form__text-field').val('');
})
.fail(function() {
alert('error');
});
});
});