chat-space メッセージ送信機能の非同期通信化

2回目の投稿になります。前回投稿した**自動更新機能の実装**に引き続き、**おかしなところ**や**間違った解釈**をしている箇所があるかと思います。何かありましたら指摘いただけると助かります。

**では本題**

今回紹介するのは**メッセージ送信機能の非同期通信化**
こちらもchat-spaceに実装した機能です。

#始めに手順です#
①.フォームが送信されたら、**イベントが発火**するようにする。
②.①のイベントが発火したときに**Ajaxを使用して、messagesコントローラのcreateアクション**が動くようにする。
③.messagesコントローラのcreateアクションでメッセージを保存し、respond_toを使用してHTMLとJSONの場合で**処理を分ける**。
④.jbuilderを使用して、**作成したメッセージをJSON形式で返す**。
⑤.返ってきたJSONを**doneメソッドで受取り、HTMLを作成**する。
⑥.⑤で作成した**HTMLをメッセージ画面の一番下に追加**する。
⑦.メッセージを送信したとき、メッセージ画面を**最下部にスクロール**する。
⑧.連続で送信ボタンを押せるようにする。
⑨.非同期に失敗した場合の処理を準備する。

#今回編集するファイルはこちら#
◆message.js
◆app/controllers/messages_controller.rb
◆app/views/messages/create.json.jbuilder





#では1つずつ紹介していきます#
###①.フォームが送信されたら、**イベントが発火**するようにする。###
```message.js
$(function(){
  $('#new_message').on('submit', function(e){
    e.preventDefault()
  });
});
```

e.preventDefault()の記述でデフォルトのイベントを止めることができます。(フォームが送信された時にリダイレクトしないようにします)

###②.①のイベントが発火したときに**Ajaxを使用して、messagesコントローラのcreateアクション**が動くようにする。###
```message.js
$(function(){

  $('#new_message').on('submit', function(e){
    e.preventDefault();
    var formData = new FormData(this);//formdata変数の定義
    var url = $(this).attr('action')//url変数の定義
    $.ajax({
      url: url,//パス
      type: "POST",//HTTPメソッド
      data: formData,
      dataType: 'json',
      processData: false,
      contentType: false
    })
  });

});
```
$(this)=>form要素自体
attrメソッド==引数に指定した属性の値を取得することができます。引数に'action'を指定しているので、form要素のaction属性の値が取得できます。

###③.messagesコントローラのcreateアクションでメッセージを保存し、respond_toを使用してHTMLとJSONの場合で**処理を分ける**。###
```app/controllers/messages_controller.rb
def create
    @message = @group.messages.new(message_params)
    if @message.save #投稿できたとき
      respond_to do |format|
        format.html { redirect_to group_messages_path(@group), notice: 'メッセージが送信されました' } #HTML形式のリクエストに対してのレスポンス
        format.json #json形式のリクエストに対してのレスポンス(この後create.json.jbuilderに遷移される)
      end 
    else
      @messages = @group.messages.includes(:user)
      flash.now[:alert] = 'メッセージを入力してください。'
      render :index
    end
  end
```

###④.jbuilderを使用して、**作成したメッセージをJSON形式で返す**。###
```app/views/messages/create.json.jbuilder
json.user_name @message.user.name
json.created_at @message.created_at.strftime("%Y年%m月%d日 %H時%M分")
json.content @message.content
json.image @message.image_url
```
必要な情報をjson形式に置き換えます。

###⑤.返ってきたJSONを**doneメソッドで受取り、HTMLを作成**する。(HTMLの名前は人によって異なると思います。ご自身のHTMLに合わせて修正をお願いします。)###
```message.js
$(function(){

  function buildHTML(message){
    image = ( message.image ) ? `<img class="lower-message__image" src=${message.image} >` : ""; //三項演算子
                var html = //変数htmlの定義
                ` <div class="message" data-message-id="${message.id}">
                <div class="upper-message">
                  <div class="upper-message__user-name">
                    ${message.user_name}
                  </div>
                  <div class="upper-message__date">
                    ${message.date}
                  </div>
                </div>
                <div class="lower-message">
                  <p class="lower-message__content">
                  ${message.content}
                  </p>
                  ${image}
                </div>
              </div>`
          return html; //return文
      };
    }
  $('#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){ //非同期通信の結果として返ってくるデータが引数(data)に入る
         var html = buildHTML(data); //return文の戻り先で、完成したHTMLを受け取る
       })
     })
  });
```

###⑥.⑤で作成した**HTMLをメッセージ画面の一番下に追加**する。###
```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
    })
    .done(function(data){
      var html = buildHTML(data);
      $('.messages').append(html);
      $("form")[0].reset(); //form内を空にする記述
```
受け取ったHTMLを**appendメソッド**で.messages(クラス)の一番最後に追加する。

###⑦.メッセージを送信したとき、メッセージ画面を**最下部にスクロール**する。###
```message.js
 $('.messages').animate({scrollTop: $('.messages')[0].scrollHeight}, 50);
```
続きに記述します。末尾の50はアニメーションのスピードです。この場合50ミリ秒（0.05秒）で動きます。

###⑧.連続で送信ボタンを押せるようにする。###
```message.js
$('input').prop('disabled', false);
```
この記述を追加します。仕様によって一度投稿をすると送信ボタンが押せなくなってしまうため、この設定をキャンセルします。

###⑨.非同期に失敗した場合の処理を準備する。###
```message.js
.fail(function() {
        alert("メッセージ送信に失敗しました");
    });
  });
```
この記述を追加します。

#これで終了です。
問題なく動いてくれると思います。こちらも何かの役に立てることができれば幸いです。
また何か情報を発信することができたらいいなと思います
では最後に今回編集したコードを記述しておきます。

```message.js
$(function(){

  function buildHTML(message){
    image = ( message.image ) ? `<img class="lower-message__image" src=${message.image} >` : ""; //三項演算子
                var html =  
                ` <div class="message" data-message-id="${message.id}">
                <div class="upper-message">
                  <div class="upper-message__user-name">
                    ${message.user_name}
                  </div>
                  <div class="upper-message__date">
                    ${message.date}
                  </div>
                </div>
                <div class="lower-message">
                  <p class="lower-message__content">
                  ${message.content}
                  </p>
                  ${image}
                </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")[0].reset();
      $('input').prop('disabled', false);
      $('.messages').animate({scrollTop: $('.messages')[0].scrollHeight}, 50);
    })
      .fail(function() {
        alert("メッセージ送信に失敗しました");
    });
  });

//自動更新の記述(中略)
  
});
```
```app/controllers/messages_controller.rb
#前略
def create
    @message = @group.messages.new(message_params)
    if @message.save #投稿できたとき
      respond_to do |format|
        format.html { redirect_to group_messages_path(@group), notice: 'メッセージが送信されました' }
        format.json
      end 
    else
      @messages = @group.messages.includes(:user)
      flash.now[:alert] = 'メッセージを入力してください。'
      render :index
    end
  end
#後略
```

```app/views/messages/create.json.jbuilder
json.user_name @message.user.name
json.created_at @message.created_at.strftime("%Y年%m月%d日 %H時%M分")
json.content @message.content
json.image @message.image_url
```

それでは今回はこのあたりで(^^)／~~~