chat-spaceの自動更新機能

今回初投稿いたします。**おかしなところ**や**間違った解釈**をしている箇所が多々あるかと思いますが、訂正等ございましたらご指摘頂けると大変助かります。

**それでは本題です。**

今回は、先日まで作成していたchat-spaceというlineの簡易版アプリの中の**メッセージの自動更新機能**を紹介します。

今chat-spaceのこの機能でつまずいてしまっている方や、これからこの強敵に挑む方に少しでも役立てることができればと思います。(※カリキュラムが変わり、当時よりもわかりやすいものになっている可能性があります。その場合はそちらを使ってください)

#まずは自動更新の仕組みから

①javascriptを使ってすでに送信したメッセージのidのうち最も新しいメッセージのidを**リクエスト**として送る。(カスタムデータ属性を使い、最新のメッセージのidを取得する)
②railsのコントローラ(api/message\_controller.rb)のアクションでデータベースに保存されている最新のメッセージのidと①のidを比較して、①のidよりも大きいidを持っているメッセージを**レスポンス**する。
③javascriptを使って、レスポンスされたメッセージをメッセージ一覧の**最後に追加**する。


###今回編集するファイルはこちら(ファイル名は人によって違ってくると思います)
◆app/views/○○○/\_message.html.haml
◆app/assets/javascripts/message.js
◆app/controllers/api/message\_controller.rb
◆config/routes.rb
◆app/views/api/messages/index.json.jbuilder

###**ではコードと簡単な解説を**###


◆jbuilderでmessage.idを使えるように.messageにカスタムデータ属性を付与します。

```_message.html.haml
.message{data: {message: {id: message.id}}} 
  .upper-message
    .upper-message__user-name
      = message.user.name
    .upper-message__date
      =message.created_at.strftime("%Y/%m/%d %H:%M")
  .lower-message
    - if message.content.present?
      %p.lower-message__content
        = message.content
    = image_tag message.image.url, class: 'lower-message__image' if message.image.present?
```

◆自動更新の記述を行います。

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

  function buildHTML(message){
    image = ( message.image ) ? `<img class="lower-message__image" src=${message.image} >` : ""; //三項演算子(投稿したimageが{message(json).image=>message.image.url}であることがtrueなら,画像のhtmlを表示,falseなら何も表示しない)　つまり画像のurlを投稿したときはそのurlを読んで画像で表示
                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; //htmlをajaxに返す
  }


//中略(非同期通信の記述)

  $(function(){
    var reloadMessages = function(){  //自動更新の関数
      if (window.location.href.match(/\/groups\/\d+\/messages/)){ //今いるページのリンクが/groups/グループid/messagesであれば以下を実行
        var last_message_id = $('.message:last').data("message-id"); //dataメソッドで.messageにある:last(最後)のカスタムデータ属性を取得しlast_message_idに代入
        $.ajax({
          url: "api/messages", //サーバの指定(api/message_controller)
          type: 'get', //メソッド(get)
          dataType: 'json', //データの形式(json)
          data: {id: last_message_id} //飛ばすデータ(取得したlast_message_id)
        })
        .done(function(messages){ //自動更新成功の時の処理(controllerから受け取ったmessageを引数にする)
          var insertHTML = ''; //HTMLの入れ物
          messages.forEach(function(message){ //配列messagesの中身一つ一つをHTMLに変換して入れ物に足し合わせる
            insertHTML = buildHTML(message); //メッセージが入ったHTMLを取得 
            $(".messages").append(insertHTML); //メッセージを追加
            $('.messages').animate({scrollTop: $('.messages')[0].scrollHeight}, 50); //自動更新が成功した時(メッセージが送信できた時)のみ一番下までスクロールする
          })
        })
        
        .fail(function(){ //自動更新が失敗した時の処理
          alert('自動更新に失敗しました'); //アラートを出す
        });
      }
    };
    setInterval(reloadMessages, 7000); //7000ミリ秒(7秒)ごとにreloadMessages(自動更新の関数)を実行
  });
});
```

◆ルーティング,コントローラ,jbuilderの記述

```app/controllers/api/message_controller.rb
class Api::MessagesController < ApplicationController
  def index
    group = Group.find(params[:group_id]) #今のグループの情報をparamsの値を元にデータベースから取得
    last_message_id = params[:id].to_i #paramsのidの数値をlast_message_id変数として定義
    @messages = group.messages.includes(:user).where("id > #{last_message_id}") #グループが所有するメッセージの中から,last_message_idより大きいidがないかをmessageから検索して@messagesに代入
  end
end
```

```routes.rb
Rails.application.routes.draw do
  devise_for :users
  root 'groups#index'
  resources :users, only: [:index, :edit, :update]
  resources :groups, only: [:new, :create, :edit, :update] do
    resources :messages, only: [:index, :create]

    namespace :api do #apiコントローラを動かすための記述
      resources :messages, only: :index, defaults: { format: 'json' } #このルーティングはjson形式でレスポンスするように指定
    end
  end
end
```

```app/views/api/messages/index.json.jbuilder
json.array! @messages do |message|
  json.content message.content
  json.image message.image.url
  json.date message.created_at.strftime("%Y/%m/%d %H:%M")
  json.user_name message.user.name
  json.id message.id #_message.html.hamlで.messageに付与したカスタムデータ属性はここで使う
end
```

これで正常に動いてくれるはずです。何かの役に立てることができれば幸いです。
**ここのコメント、解説おかしい!!** **もっといい書き方知ってる！**という方いらっしゃればコメントいただければと思います。

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