WHAT
ajaxを使って非同期通信をして、メッセージを画面遷移なしで投稿します。
ajaxとjbuilderの処理の流れがよくわからず、理解するのに大変苦労したので、忘れないうちに備忘録。
WHY
ajaxとjbuilderの処理の流れがよくわからず、コードを書くのにかなり時間がかかったため。
※間違い等あれば、ご指摘ください!!
処理
1. フォームを送信
_form.html.haml
= form_for [@group, @message], html: {class: 'msg_form'} do |f|
= f.hidden_field :user_id, value: "#{@message.user_id}"
= f.text_field :body, autofocus: true, placeholder: "type a message", class: "form__textfield"
= link_to "#" do
%i{class:"fa fa-picture-o"}
= f.submit "Submit", class: "form__submit"
ルーティング(一部抜粋)
GET /groups/:group_id/messages(.:format) messages#index
POST /groups/:group_id/messages(.:format) messages#create
2. フォーム送信イベントを中止し、js処理へ
$(function() {
// 省略
$('.msg_form').on('submit', function(e) {
e.preventDefault();
// ここでフォームのsubmitイベントを中止。
var api_url = window.location.pathname;
// ajaxでリクエストを送る際のパス取得
var formdata = new FormData($(this).get(0));
// formdataオブジェクトとして、フォームに入力した値を取得
// 以下のコードでformdataの中身を確認できる(らしい)
// for(item of formdata) console.log(item);
// ajaxを使って、json形式でリクエストを送る。
$.ajax({
url: api_url, // リクエストのパス。ここでは'/groups/23/messages'
type: 'POST', // HTTPメソッド
data: formdata, // リクエストと一緒に送るdata
contentType: false, // リクエストに含まれているdataの型はこれですよ〜の記述(リクエストヘッダにあるらしい)を変更しないよ
processData: false, // リクエストに含まれているdataの実際の型を変更しないための記述出そうだ
// 上記2つの指定はしないといけないらしい。
// 参考 : http://semooh.jp/jquery/api/ajax/jQuery.ajax/options/
dataType: 'json', //JSON形式でリクエスト送るよ
})
// ここまでで、ajaxのリクエストの指定。
// リクエストが送信されるので、ルーティングが読まれる。
// (以下省略)
// うまく行ったらこう、ダメならこう、というコードが続く。
});
});
3. コントローラー処理
2 でajaxから、HTTPメソッド'POST' で '/groups/:group_id/messages(.:format)' のリクエストが送られた。ルーティング通り messages#create (つまりmessagesコントローラーのcreateアクションが処理される。)
messages_controller.rb
class MessagesController < ApplicationController
def create
@message = Message.new(message_params) // Messageインスタンス生成
@message.user_id = current_user.id // paramsでは取れないuser_id, group_idを追加
@message.group = Group.find(params[:group_id]) // MessageインスタンスにGroupをアソシエーション
if @message.save // もしMessageインスタンスを保存できたら
respond_to do |format|
format.html {redirect_to group_messages_path(params[:group_id])}
// リクエストがHTML形式であれば、リダイレクト
// 今回のアプリケーションでは読まれることはない(はず)だが、一応記述
format.json
// リクエストがJSON形式であれば、(今回は)jbuilderを実行
// 今回のアプリケーションでは、format.jsonが読まれるべき。
end
else // もしMessageインスタンスを保存できなければ
flash[:notice] = "メッセージを入力してください"
redirect_to group_messages_path(params[:group_id])
end
end
private
def message_params
params.require(:message).permit(:body ,:image, :user_id).merge(group_id: params[:group_id])
end
end
4. jbuilder処理
ajaxによりJSON形式でリクエストが読まれ、messages_controller.rbのcreateアクションが動いた。
respond_to 以下 format.json の記述により、views/messages/create.json.jbuilderが読まれる。
create.json.jbuilder
json.name @message.user.name
json.body @message.body
json.image @message.image
json.group_id @message.group_id
json.user_id @message.group_id
json.time @message.created_at
リクエストによって送られたjson形式のdataオブジェクトに関して
nameキー には @message.user.nameがvalueとなる。
bodyキー には @message.body
...(以下省略)
つまりdataの中身は以下のようなJSON形式のハッシュに。
.key を指定することで、それぞれのvalueを取得できるようになる。
Object {name: "yukihiro", body: "ooo", image: null, group_id: 23, user_id: 23…}
jbuilderの処理が終わったので、message.jsの ajaxの後半へ進む。
5. ajax リクエストが成功/失敗した場合
$(function() {
function new_message(message) {
var new_message = $('<div class="msg">' +
'<p class="msg__username">'+ message.name +'</p>' +
'<p class="msg__time">'+ message.time + '<p>' +
'<p class ="msg__passage">' + message.body +'</p>' +
'</div>');
return new_message;
}
$('.msg_form').on('submit', function(e) {
// 省略
// さっきこのajaxでリクエスト送った
$.ajax({
url: api_url,
type: 'POST',
data: formdata,
contentType: false,
processData: false,
dataType: 'json',
})
// ajaxのリクエストが成功したら.doneが読まれる
.done(function(message){
console.log('success!'); // デバッグ用
console.log(message); // 上に同じ
var html = new_message(message); // 非同期でメッセージを追加したかったので、これ
$('.msg').append(html); // 実際に追加する
// 'chat__content'の末尾に'html'を加える
$('.form__textfield').val(''); //テキストフィールドを空にする
$('.form__submit').prop('disabled', false); //送信ボタンを有効にする
})
// ajaxのリクエストが失敗したら、.failが読まれる
.fail(function(message){
console.log('error!');
});