非同期通信によるメッセージ送信

railsにおける非同期通信によるメッセージ送信の大まかな流れ

  1. jsファイルに、フォームが送信されたらイベントが発火するようかく。
  2. イベントが発火した時にAjaxを使い、createアクションが動くようにする。
  3. コントローラーでHTMLとjsonの処理を分ける。
  4. jbuilderで送信されたメッセージをJSON形式で返す。
  5. jsファイルにdoneメソッドを定義し、HTMLを付け加える
  6. HTMLを追加した分画面をスクロールさせる。
  7. 非同期通信に失敗した時の処理をかく。

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);

    })

})

})