非同期通信によるメッセージ送信
railsにおける非同期通信によるメッセージ送信の大まかな流れ
- jsファイルに、フォームが送信されたらイベントが発火するようかく。
- イベントが発火した時にAjaxを使い、createアクションが動くようにする。
- コントローラーでHTMLとjsonの処理を分ける。
- jbuilderで送信されたメッセージをJSON形式で返す。
- jsファイルにdoneメソッドを定義し、HTMLを付け加える
- HTMLを追加した分画面をスクロールさせる。
- 非同期通信に失敗した時の処理をかく。
1〜7について具体的に述べていく。
1. jsファイルに、フォームが送信されたらイベントが発火するようにかく。
viewファイルのフォームは以下のようなものとする。
app/views/messages/index.html.erb
<%= form_for [@group, @message], id: "new_message" do |f| %>
<%= f.text_field :content, class: "form__message" %>
<%= f.submit "Send", class: "form__submit" %>
<% end %>
この時、jsファイルにフォームが送信されたらイベントが発火するように、以下のようにかく。
app/assets/javascripts/message.js
$(function() {
$('#new_message').on('submit', function(e) {
e.preventDefault();
var formData = new FormData(this);
var url = $(this).attr('action');
})
})
上の流れを追っていく。
- id が new_message であるフォームが送信されたら関数実行。
- e.preventDefault(); でイベントを止め、通常の同期通信で送信されてしまうのを止める。
- FormDataを使ってformから送信された内容を取得し、変数 formDataに代入している。
- attr メソッドを使ってform の action 属性を取得し、変数 url に代入している。
2. イベントが発火した時にAjaxを使い、createアクションが動くようにする。
下記のように、ajax通信を行い、createアクションを動かす。
app/assets/javascripts/message.js
$("#new_message").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
})
})
processDataオプションはデフォルトではtrueになっており、dataに指定したオブジェクトをクエリ文字に変換する役割がある。
contentTypeオプションはサーバーにデータのファイル形式を伝えるヘッダ。text/xmlでコンテンツタイプをXMLとして返してくる。
ajaxのリクエストがFormDataのときはどちらの値も適切な状態で送ることが可能なため、falseにすることで設定が上書きされることを防ぐ。
FormDataを使用してフォームの情報を取得した時には必ずfalseにするという認識で良い。
3. コントローラーでHTMLとjsonの処理を分ける。
以下のように、respond_to do |format| を使い、コントローラーのcreateアクション内で、formatがHTMLの時とjsonの時との処理を分ける。
app/controllers/messages_controller.rb
class MessagesController < ApplicationController
省略
def create
@message = @group.messages.new(message_params)
if @message.save
respond_to do |format|
format.html
format.json
end
else
@message = @group.message.includes(:user)
flash.now[:alert] = 'メッセージを入力してください'
render :index
end
end
省略
4. jbuilderで送信されたメッセージをJSON形式で返す。
以下のように、rubyで扱われていた変数を、json形式で扱えるように変換する。
app/veiews/messages/create.json.jbuilder
json.user_name @message.user.name
json.content @message.content
json.time @message.created_at
json.id @message.id
こうすることで、それぞれjQueryのオブジェクトとして、
message.user_name、message.content、message.time、message.id、として扱うことができる。
jbuilderファイルでは基本的にjson.KEY VALUEという形で書くことができる。
こうすることによってJavaScriptファイルに返ってきたデータをjbuilderで定義したキーとバリューの形で呼び出して使うことができる。
5. jsファイルにdoneメソッドを定義し、HTMLを付け加える
app/assets/javascripts/message.js
$(function() {
function buildHTML(message){
var html = `<div class="message">
<div class="upper-message">
<div class="upper-message__user-name">
${ message.user_name }
</div>
<div class="upper-message__date">
${ message.time }
</div>
</div>
<div class="lower-message">
<p class="lower-message__content">
${ message.content }
</p>
</div>
</div>`;
return html;}
$("#new_message").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);
$('.messages').append(html);
$('.form__message').val('');
$('.form__submit').prop('disabled', false);
})
})
})
doneメソッドはajax通信が上手くいき、dateが保存された時に実行される。
.done(function(data) の data には、サーバーから返されたデータが入っている。
この時サーバーから返ってくるデータは、jbuilderで作成したcreate.json.jbuilderのデータ。
次に、buildHTMLメソッドを引数にjbuilderで作成したデータを持たせて実行し、html という変数に代入している。
そして、<div class="message">のタグに html をappendメソッドで付け加えている。
その次に、text_fieldに入力された文字列をからにしている。
そして、一度送信したsubmitボタンが再び押せなくなっているのを解除している。
6. HTMLを追加した分画面をスクロールさせる。
scrollメソッドを定義し、doneメソッドの中でscrollメソッドを呼び出す。
app/assets/javascripts/message.js
$(function() {
function buildHTML(message){
var html = `<div class="message">
<div class="upper-message">
<div class="upper-message__user-name">
${ message.user_name }
</div>
<div class="upper-message__date">
${ message.time }
</div>
</div>
<div class="lower-message">
<p class="lower-message__content">
${ message.content }
</p>
</div>
</div>`;
return html;}
function scroll() {
$('.messages').animate({scrollTop: $('.message')[0].scrollHeight});
}
$("#new_message").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);
$('.messages').append(html);
$('.form__message').val('');
$('.form__submit').prop('disabled', false);
scroll()
})
})
})
7. 非同期通信に失敗した時の処理をかく。
ajax通信に失敗したときは、ユーザーにエラーを知らせるアラートを出すようにする。
app/assets/javascripts/message.js
$(function() {
function buildHTML(message){
var html = `<div class="message">
<div class="upper-message">
<div class="upper-message__user-name">
${ message.user_name }
</div>
<div class="upper-message__date">
${ message.time }
</div>
</div>
<div class="lower-message">
<p class="lower-message__content">
${ message.content }
</p>
</div>
</div>`;
return html;}
function scroll() {
$('.messages').animate({scrollTop: $('.message')[0].scrollHeight});
}
$("#new_message").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);
$('.messages').append(html);
$('.form__message').val('');
$('.form__submit').prop('disabled', false);
scroll()
})
.fail(function(){
alert('error')
$('.form__submit').prop('disabled', false);
})
})
})