pictweetでコメント機能を非同期通信
作成する機能を把握する
今回作成する機能は以下の画像のような機能。
いままでのpictweetのコメント送信機能と異なるのはajaxを使って非同期通信で行っている点である。
作業ディレクトリを以前に完成させたpictweetに移動
1 2 3 |
pictweetをテキストエディタで開く
完成しているものでない場合、この後の作業しても正しく動かないことがあるので、必ず完成しているものを選択する。
Gemfileの記述を確認
Gemfileにjquery-rails
が記載されているか確認。
1 2 3 4 5 6 7 8 |
...
(中略)
...
# Use jquery as the JavaScript library
gem 'jquery-rails'
...
(中略)
...
|
記載されていない場合は最下部に記載し、bundle install
する。
application.jsの記述を確認
application.jsに//= require jquery
//= require rails-ujs
の記載がされているか確認する。
記載がない場合は、上記を参考に記載する。
必要なクラス名とIDを与える
後のajaxの非同期通信を実装するために、事前にコメントのフォームにクラス名を与えてく。app/views/tweets/show.html.erbの投稿フォームを以下のように編集し、先に進む。
show.html.erbの中身が、form_with、か、form_tagのどちらを使用しているのか注意して進める。
form_tagの場合の修正点
下記のコードのとおり、以下3点を追記。正しく追記できないと、非同期通信の実装ができない。
- new_commentというid
- textboxというclass
- form__submitというclass
1 2 3 4 |
form_withの場合の修正点
下記のコードのとおり、以下3点を追記。正しく追記できないと、非同期通信の実装ができない。
- new_commentというid
- textboxというclass
- form__submitというclass
1 2 3 4 |
<%= form_with(model: [@tweet, @comment], local: true, id: "new_comment") do |form| %>
<%= form.text_area :text, placeholder: "コメントする" , rows: "2", class: "textbox" %>
<%= form.submit "SEND", class: "form__submit" %>
<% end %>
|
コメント機能を非同期通信化する
コメント機能の非同期通信では、respond_toやAjaxの他に新しくjbuilderというものを使う。
コメント機能実装のステップ
- jQueryを記述するためのファイルを作成する
- フォームが送信されたら、イベントが発火するようにする
- 非同期通信でコメントが保存されるようにする
- respond_toを使用してHTMLとJSONの場合で処理を分ける
- jbuilderを使用して、作成したメッセージをJSON形式で返す
- 返ってきたJSONを
doneメソッド
で受取り、HTMLを作成する - エラー時の処理を行う
1. jQueryを記述するためのファイルを作成する
jQueryを記述しながらAjaxによる非同期通信の実現を目指す。Ruby on Rails内では、JavaScriptファイルをassets/javascripts
以下に作成する。
comment.jsというファイルをassets/javascripts以下に作成する
- app
- assets
- javascripts
- comment.js
- javascripts
- assets
2. フォームが送信されたら、イベントが発火するようにする
submitボタンを押した時に、イベントが発火するようにする。
jQueryを記述
先ほど作成したcomment.jsに、コメントフォームが送信された時フォームに入力された値を受け取れるように以下の記述をする。
1 2 3 4 5 6 |
$(function(){
$('#new_comment').on('submit', function(e){
e.preventDefault();
var formData = new FormData(this);
})
})
|
ポイント① フォームが送信された時、というイベントを設定したい場合は、form要素を取得してonメソッドを使う
要素を検証するとわかるが、今回のアプリの画面ではform要素のid属性
が#new_comment
となっている。フォームの送信についてonメソッドでイベントをセッティングする際は、form要素自体に設定するようにする。
ポイント② 最初から設定されているフォームの動作はキャンセルする
フォームが送信される時、何も設定していない状態(デフォルトの状態)だとフォームを送信するための通信が行われるため、preventDefault()を使用してデフォルトのイベントを止める。
FormData
フォームのデータの送信に使用することができる。その他にも、キーのついたデータを伝送するためにフォームとは独立して使用することもできる。今回はコメントフォームがあるので、そのフォームの情報を取得するのに使う。
上の図のように、new FormData(フォーム要素)
とすることでFormDataを作成できる。
今回FormDataオブジェクトの引数はthisとなっているが、イベントで設定したfunction内でthisを利用した場合は、イベントが発生したノード要素を指す。今回の場合は、new_commentというIDがついたフォームの情報を取得している。
フォームの値を確認する
1 2 3 4 5 6 7 |
$(function(){
$('#new_comment').on('submit', function(e){
e.preventDefault();
console.log(this)
var formData = new FormData(this);
})
})
|
編集ができたら、検証の画面でコンソールを開き、thisの中身がなにか確認してみる。
以下の画像の様に、コンソール上にて、フォームに入力した値が出力されていれば正解!
JavaScriptではこのようにしてconsole.logを用いてデバッグを行う。
他にもdebuggerというデバッグ方法もある。
https://postd.cc/javascript-debugging-tips-and-tricks/
3. 非同期通信でコメントが保存されるようにする
フォームに入力されたデータを取得したら、必要なAjax関数のオプションを揃えて非同期通信を行う。
実装の流れは以下。
- フォームの送信が行われた時に、Ajaxによる非同期通信でcreateアクションを動かす
- createアクション内でコメントを保存し、respond_toを使用してHTMLとJSONの場合で処理を分ける
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
5行目でフォームの送信先のurlを定義している。$(this)は、thisで取得できる要素をjQueryオブジェクト化している。
attrメソッド
要素が持つ指定属性の値を返す。
要素が指定属性を持っていない場合、関数はundefinedを返す。
今回はイベントが発生した要素のaction属性の値を取得しており、今回のaction属性にはフォームの送信先のurlの値が入っている。
これでリクエストを送信する先のURLを定義することができた。
続いて5~12行目がAjaxで非同期通信に必要なオプションを設定する。
processDataオプション
デフォルトではtrueになっており、dataに指定したオブジェクトをクエリ文字列(例: msg.txt?b1=%E3%81%8B&b2=%E3%81%8D )に変換する役割がある。
クエリ文字列とは、WebブラウザなどがWebサーバに送信するデータをURLの末尾に特定の形式で表記したものの事。
contentTypeオプション
サーバにデータのファイル形式を伝えるヘッダ。こちらはデフォルトでは「text/xml」でコンテンツタイプをXMLとして返してくる。
ajaxのリクエストがFormDataのときはどちらの値も適切な状態で送ることが可能なため、falseにすることで設定が上書きされることを防ぐ。FormDataをつかってフォームの情報を取得した時には必ずfalseにするという認識で構わない。
他にもAjaxリクエストを送信するオプションは多くある。
http://js.studio-kingdom.com/jquery/ajax/ajax
4. コメントを保存し、respond_toを使用してHTMLとJSONの場合で処理を分ける
非同期通信によって、comment#create
を動かすことに成功し、正しくAjaxからdata
を送れていれば、そのまま保存することが可能。
保存ができていることを確認したら、respond_to
を使用してHTMLとJSONの場合で処理を書いていく。
1 2 3 4 5 6 7 8 9 10 11 12 |
form_tagで実装した場合はcomment_paramsの記述が異なるが修正の必要はない。
ローカル変数comment
は、スコープの関係でこの後のjbuilder側で使用できないので、インスタンス変数@comment
に編集する。
respond_to doを使用し、リクエストされたformatによって処理を分けるようにします。フォーマットがjsonの時の説明をします。todoリストのアプリではformat.jsonに引数としてインスタンスを渡しましたが、今回はjbuilderを使ってJavaScriptに返すデータを作成します。
jbuilder
rails newコマンドでアプリケーションを作成した際にgemfileにデフォルトで記述されているgemで、入力データをJSON形式で出力するテンプレートエンジン。
今回はJbuilderを利用するので、コントローラ内でrender json: ○○を行わない。
5. jbuilderを使用して、作成したメッセージをJSON形式で返す
respond_toで処理を分けたら、jbuilder
を使用してJavaScriptファイルに返すデータを作成する。
jbuilderは、viewと同じように該当するアクションと同じ名前にする必要がある。
commentのcreateアクションに対応するjbuilderのファイルになるので、作成するのはviews/comments/create.json.jbuilder
になる。
jbuilderのファイルを作成して、JavaScriptで必要な情報を渡すようにする。
1 2 3 |
jbuilderファイルでは基本的にjson.KEY VALUEという形で書くことができる。
こうすることによってJavaScriptファイルに返ってきたデータをjbuilderで定義したキーとバリューの形で呼び出して使うことができる。
複雑なjsonの生成も見やすく書くことができるので積極的にjbuilderを使っていく。
6. 返ってきたJSONをdoneメソッド
で受取り、HTMLを作成する
非同期通信の結果として返ってくるデータは、done(function(data) { 処理 })の関数の引数で受け取る。
この引数を元にHTMLを組み立てる。
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 |
$(function(){
function buildHTML(comment){
var html = `<p>
<strong>
<a href=/users/${comment.user_id}>${comment.user_name}</a>
:
</strong>
${comment.text}
</p>`
return html;
}
$('#new_comment').on('submit', function(e){
e.preventDefault();
var formData = new FormData(this);
var url = $(this).attr('action');
$.ajax({
url: url,
type: "POST",
data: formData,
dataType: 'json',
processData: false,
contentType: false
})
.done(function(data){
var html = buildHTML(data);
$('.comments').append(html);
$('.textbox').val('');
$('.form__submit').prop('disabled', false);
})
})
});
|
24-29行目は、非同期通信に成功した時の記述である。function(data)となっているが、非同期通信に成功した時の即時関数の第一引数には、サーバから返されたデータが入っている。この場合のサーバから返ってくるデータというのは、jbuilderで作成したcreate.json.jbuilderのデータである。
28行目の$('.form__submit').prop('disabled', false);
は、 htmlの仕様でsubmitボタンを一度押したらdisabled属性
というボタンが押せなく属性が追加される。
そのため、disabled属性
をfalseにする記述を追加している。
2-11行目ではHTMLを追加している。簡単な記述で実現できるのは、テンプレートリテラル記法
を使用しているから。
テンプレートリテラル記法
ダブルクオートやシングルクオートの代わりにバックティック文字
で囲むことで、複数行文字列や文字列内挿入機能を使用できる。
テンプレートリテラル記法を使用することで、わかりやすくHTML要素を作成できる!
buildHTMLの引数として渡されたcommentはサーバから返されたデータであるjbuilderのデータであるため、ファイル内で定義したキーとバリューの形式で使用することができる。
7.エラー時の処理を行う
最後に通信に失敗した場合の処理を実装する。
アラートを表示できれば成功。
1 2 3 4 5 6 7 8 9 |
.done(function(data){
var html = buildHTML(data);
$('.comments').append(html);
$('.textbox').val('');
$('.form__submit').prop('disabled', false);
})
.fail(function(){
alert('error');
})
|
サーバーエラーの場合、このfailの関数が呼ばれる。
以上でコメント機能の非同期通信化の実装は終了!