はじめに
Ajaxって何? → 非同期通信の総称とか言われてます
何ができんの? → 例えばページをリロードせずにデータのやりとりが可能
今記事では、簡単なアプリケーションで実際に非同期通信を行えるよう実装していきます。
非同期通信の流れ
非同期通信ではJavaScriptのメソッドを利用してリクエストを送るため、
フォーム要素のデフォルトアクションを無効にする必要がある。
今回のアプリで言うと【Create Todo】ボタンを押すことによって
リクエストが送られることがデフォルトアクション。
現状は下記の通り、新しいTodoが作成されると
左上のリロードマークが動いてページが更新される。
このデフォルトアクションの無効化は後述。

非同期通信のリクエストがJavaScriptのメソッドを利用することはわかりました。
じゃぁレスポンスは?
デフォルトアクションを無効化すると同時に、レスポンスをJSON形式で返して欲しいということをコントローラーのアクションに明記しなければいけないらしい。JSON形式のファイルはviewsフォルダに作成可能。
→ファイルの形式は○○.json.jbuilder
※JSONとはRubyのハッシュと同様、キーとバリューの組合わせでデータを表現する形式
非同期通信ここまでのまとめ
①リクエストはJavaScriptのメソッドを利用
②レスポンスはJSONで
③デフォルトアクションがあれば無効化
早速、非同期通信に取り掛かりたいところですが、その前に準備があります。4つほど。
①respond_to メソッドでリクエストがHTML,JSONどっちのレスポンスを求めているか条件分岐
# def create
# Todo.create(todo_params)
# redirect_to :root
# end
# ↓↓↓↓↓ respond_to メソッドで条件分岐させる ↓↓↓↓↓
def create
@todo = Todo.create(todo_params)
respond_to do |format|
format.html { redirect_to :root }
format.json { render json: @todo}
end
end
②Ajaxで実装していくためにjQueryのGemを導入
Gemfileに gem 'jquery-rails' を追記してbundle install
③ディレクトリを整える
app
└ assets
├ config
├ images
├ javascripts ←①作成
│ ├ application.js ←②作成
│ └ todo.js ←③作成
└ stylesheets
④呼び出すために色々記述
app/views/layouts/application.html.hamlに
= javascript_include_tag 'application'を追記し、
viewファイルから呼び出せるようにします。
続いて、app/assets/config/manifest.jsに追記します。
//= link_tree ../images
//= link_directory ../javascripts .js ←この行を追記
//= link_directory ../stylesheets .css
最後にapp/assets/javascripts/application.jsに記述
//= require jquery ←RailsでjQueryを使用するためのもの
//= require rails-ujs ←RailsでJavaScriptを使用するためのもの
//= require_tree . ←application.jsが存在するディレクトリのJavaScriptファイル全てを読み込む
はい ここまでで準備完了。
まだ非同期通信実装の入り口に立っただけ...さすが初学者の山場。
実装に取り掛かかります
$(function() {
$('.js-form').on('submit', function(e) {
e.preventDefault();
let todo = $('.js-form__text-field').val();
});
});
①まず大前提として**$(function(){...})**が必要。jQueryを使う時は絶対記述するくらいの感覚
②2行目の**$('.js-form').on('submit', function(e)**
まずは前半 $('.js-form') は下記の2行目のクラス名の部分
.contents
= form_with model: @todo, html: { class: 'form js-form' }, local: true do |f|
= f.text_field :content, class: 'form__text-field js-form__text-field'
= f.submit class: 'form__submit js-submit'
%ul.todos
= render @todo
今回はクラス名ですが、$(#'〇〇')とidを使用することも可能。
後半、submitイベントを設定 (.js-formが送信されたときに実行されるイベント)
function(e) の e 今回のコードで言うとsubmitイベント発火時の情報を取得してるみたい。
イメージ的には $('.js-form').on('submit', までの情報が入ってる感じかな
③そして3行目の e.preventDefault( );
初めに言ってたデフォルトアクションを無効化させる魔法のコード
④4行目は変数todoに、f.text_fieldで入力された値を代入
**val()**がvalue値を取得しています
さらにtodo.jsに記述していきます
$(function() {
$('.js-form').on('submit', function(e) {
e.preventDefault();
let todo = $('.js-form__text-field').val();
// ↓↓↓↓↓ ここから続き ↓↓↓↓↓
$.ajax({
type: 'POST',
url: '/todos.json',
data: {
todo: {
content: todo
}
},
dataType: 'json'
})
});
});
⑤**$.ajax**は非同期通信を行うための記述
⑥**$.ajax以下のtype url data dataType はajaxメソッドのオプション**
| オプション | 説明 |
|---|---|
| type | HTTP通信の種類で、GETとPOSTがある |
| url | リクエスト送信先のurl |
| data | サーバに送信する値 |
| dataType | サーバから返されるデータの型 (json一択では?) |
ややこしいのがdataの部分で、横書きすると↓
{ todo: { content: todo(テキストフィールドに入力された値)} }
まとめると
通信方法POSTで、**'/todos.json'**というURLに
{ todo: { content: todo(テキストフィールドに入力された値)} }を送信しますよ。
サーバからはjsonで値を返してね。ってこと。
さらにさらにtodo.jsに記述していきます
$(function() {
$('.js-form').on('submit', function(e) {
e.preventDefault();
let todo = $('.js-form__text-field').val();
$.ajax({
type: 'POST',
url: '/todos.json',
data: {
todo: {
content: todo
}
},
dataType: 'json'
})
// ↓↓↓↓↓ ここから続き ↓↓↓↓↓
.done(function(data) {
})
.fail(function() {
alert('error');
});
});
});
非同期通信を**done(成功)とfail(失敗)**で処理を分けてます
⑦done(function(data)のdata(引数名は別になんでも良い)は
リクエストによって返ってきたレスポンスが代入される
レスポンスは確かJSONでって言ってたよな...てことでtodos_controller.rbを見ると
def create
@todo = Todo.create(todo_params)
respond_to do |format|
format.html { redirect_to :root }
format.json { render json: @todo} ← ココ
end
end
上記のようにjsonの場合は作成したTodoをjson形式にして返す記述がしてある
この@todoをどうするかですが、今記事の初めのほうに言っていた
『JSON形式のファイルはviewsフォルダに作成可能。ファイル形式は○○.json.jbuilder』を実行
⑨viewsフォルダの中のtodoフォルダにjbuilderファイルを作成
app
└ views
├ todos
│ ├ _todo.html.haml
│ ├ index.html.haml
│ └ create.json.jbuilder ←作成 ※createはアクション名
⑩create.json.jbuilderに記述
json.content @todo.content
@todo.contentの内容がjson.contentで送られますよという意味
じゃ**@todo.content**ってどこから出てきたのって話
ターミナルでコンソールを起動して、@todo = Todo.firstを実行すると
=> #<Todo id: 1, content: "test1", ...省略>
データベースのtodosテーブルにある、1つ目の投稿情報全てが出てきました。
続けて、@todo.content を実行すると
rb(main):002:0> @todo.content
=> "test1"
todosテーブルにある、1つ目の投稿のcontentカラム情報が表示される。
話を元に戻すと、今回はこの**"test1"って内容がjson.content**で送られてますよってこと
@todo.contentはどこから出てきた → コンソールで確認するとわかりやすい
⑪jbuilderでブラウザに表示させるHTMLを作成
以下のtest1 test2 test3はそれぞれ
【Create Todo】ボタンが押されるたびに生成されている

検証ツールで確認すると``
〇〇 `` というまとまりになっている。このまとまりをbuildTodoで作成$(function() {
// ↓↓↓↓↓ 追記① ↓↓↓↓↓
function buildTodo(form){
let html = `<li class="todo">
${form.content}
</li>
`
return html
}
// ↑↑↑↑↑ 追記 ↑↑↑↑↑
$('.js-form').on('submit', function(e) {
e.preventDefault();
let todo = $('.js-form__text-field').val();
$.ajax({
type: 'POST',
url: '/todos.json',
data: {
todo: {
content: todo
}
},
dataType: 'json'
})
.done(function(data) {
// ↓↓↓↓↓ 追記① ↓↓↓↓↓
let html = buildTodo(data)
// ↑↑↑↑↑ 追記 ↑↑↑↑↑
// ↓↓↓↓↓ 追記③ ↓↓↓↓↓
$('.todos').append(html)
// ↑↑↑↑↑ 追記 ↑↑↑↑↑
})
.fail(function() {
alert('error');
});
});
});
追記①では、描画内容をbuildTodoで生成している。
htmlという変数に、先ほど検証ツールで確認したまとまり(DOM)を代入
〇〇の部分にはbuildTodo(form)の引数.content ※引数はなんでも良い
追記②は、非同期通信が成功したときHTML情報を描画するという記述
buildTodoを呼び出している
追記③では、ajaxで非同期取得したHTMLをappendで反映させてます
$('.todos')のクラス名はどこからきているかと言うと↓

それぞれのli要素を囲っているul要素のクラス**".todos"**です。
このクラス名を指定することでul要素内に新たにli要素が生成されていきます。
さて、現状はここまできています。↓

非同期通信はできていて、リロードなしでも投稿内容が更新されている。
ここで2つ気になることが出てきました。
1.前回の投稿内容が残ったまま
2.【Create Todo】ボタンが一度しか押せない
これら2点を解消していきます。
まず1つ目
前回の投稿内容を投稿と同時に消えるようにする
$(function() {
function buildTodo(form){
let html = `<li class="todo">
${form.content}
</li>
`
return html
}
$('.js-form').on('submit', function(e) {
e.preventDefault();
let todo = $('.js-form__text-field').val();
$.ajax({
type: 'POST',
url: '/todos.json',
data: {
todo: {
content: todo
}
},
dataType: 'json'
})
.done(function(data) {
let html = buildTodo(data)
$('.todos').append(html)
// ↓↓↓↓↓ 追記 ↓↓↓↓↓
$('#todo_content').val("");
// ↑↑↑↑↑ 追記 ↑↑↑↑↑
})
.fail(function() {
alert('error');
});
});
});
$('#todo_content').val("")を追記
val("")とすることで値を空にできます。先ほどのval()と似ているので注意
#todo_contentはどこから?

検証ツールで確認 → 上記の青枠のところを見てみるとid="todo_contentとあります。
なのでここのidを引っ張ってきたわけです。別にクラスでも良いと思いますが。
続いて2つ目
【Create Todo】ボタンをリロードせずに押せるようにする
$(function() {
function buildTodo(form){
let html = `<li class="todo">
${form.content}
</li>
`
return html
}
$('.js-form').on('submit', function(e) {
e.preventDefault();
let todo = $('.js-form__text-field').val();
$.ajax({
type: 'POST',
url: '/todos.json',
data: {
todo: {
content: todo
}
},
dataType: 'json'
})
.done(function(data) {
let html = buildTodo(data)
$('.todos').append(html)
$('#todo_content').val("");
// ↓↓↓↓↓ 追記 ↓↓↓↓↓
$(".form__submit").prop('disabled', false);
// ↑↑↑↑↑ 追記 ↑↑↑↑↑
})
.fail(function() {
alert('error');
});
});
});
**$(".form__submit").prop('disabled', false);**を追記
.form__submitは【Create Todo】ボタンのクラス名を使っています。
prop()は、引数にプロパティを指定することでプロパティの値を取得できます。
disabledは無効化させる属性ですので、それをfalseで無効化しないようにします。
ここでいう無効化とは、ボタンが押せなくなるということ。それがfalseなので有効化です。
①リロードされずにブラウザが更新されている
②前回の投稿内容が消えている
③ボタンを連続で押すことができる
全部達成できました。
まとめ
非同期通信は一度コードを書いてしまえば、
テンプレとして使えるところがたくさんある印象です。
めちゃめちゃ細かくコードを読み解いていったので記事が長くなってしまいました..
ご意見、アドバイス等々、なんでもお待ちしております。
ここまで読んでいただきありがとうございます。
