rails + jquery + ajax + jsonを使った非同期通信の処理の流れについて

#はじめに
rails + jquery + ajax + jsonを使ってリアルタイムで投稿が行えるようにした際、どういった処理の流れが行われているのかまとめてみました。(createのみで、更新、削除の説明は省くとします。スマホで見るとかなり見づらいと思います。)

#完成形
![画面収録-2019-08-08-20.36.48.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280603/d72d954a-2785-5279-1ee8-dd14a32f8c76.gif)
このようにページ遷移せずにデータベースに投稿内容を保存しつつ、投稿を追加(取得)することを非同期通信といいます。
#実装するためには？
###railsに渡していた投稿内容の情報をjavascriptにどうやって渡すのか？
railsで定義した変数をJavaScriptに渡すためにはjson形式に変換して渡す必要があります。
(gon([公式のgithubページ](https://github.com/gazay/gon))というgemではRailsの変数を簡単にjavaScriptに渡してくれるみたい。)
しかし今回はgemを使わずに非同期通信で投稿が行えるようにします。

#処理の流れについて
viewは記述量が多くてごちゃごちゃしているので省いて画像で説明します。

##まずはルーティング
```ruby
# getであること！
get 'comments/create_comment', to: 'comments#create_comment'
```

##view
<img width="1077" alt="スクリーンショット 2019-08-08 21.21.54.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280603/59348f17-ef87-28a6-c44b-a8dd4774615d.png">

##jsファイル
```javascript
$(document).on('click', '.create_comment', function() {
    var content = $('#content_' + $(this).attr('id').replace('comment_submit_', '')).val();
// ----------------------------------------------------------------------------
    if(content == '') {
      alert('コメントを入力してください。');               // 投稿が空欄だった場合の振る舞い
      return false;    
// ----------------------------------------------------------------------------
    } else {
// ----------------------------------------------------------------------------
      $.ajax({
        url: '/comments/create_comment',
        data: { note_id: $('#note_id').val(),     // まずはここ！
                content: content                  // 何か投稿があった時の振る舞い
              },
        dataType: 'json'
      })
// ----------------------------------------------------------------------------
      .done(function(data) {
        var html = buildHTML(data);
        $('.comments').append(html);             // json形式で値を受け取った後の処理
        $('.content').val('');
      })
// ----------------------------------------------------------------------------
      .fail(function(data) {
        alert('エラーが発生しました。')               // 受け取り失敗の場合の処理
      });
// ----------------------------------------------------------------------------
    }
  });
```
まず投稿内容を記入して、view画像の緑の「作成」ボタンを押した時にjQueryのクリックイベントが発火します。
$.ajax以下の記述では、まずその投稿の内容を指定したURLにdata内の情報とともに送ります（かつjson形式で！）。

##コントローラ

```ruby 
def create_comment
# ------------------------------------------------------------------------
  @note = Note.find(params[:note_id])
  @comment = comments.new(permit_params)         # データベース内の処理
  @comment.save
# ------------------------------------------------------------------------
# ------------------------------------------------------------------------
  respond_to do |format|
    format.html { redirect_to note_path(@note) }
    format.json                         # データベースの処理が終わったあとどうするのか
  end
# ------------------------------------------------------------------------
end
```
$.ajax内のdataで受け取った値を元に```@note```を探し出し、それに基づく``` @comment ```コメントをデータベースに保存します。
そして次にデータベースに保存したあとどうするのか、ということになりますがルーティングでは``` get ```メソッドであるので、``` create_comment.~~ ```ファイルがないかと探しに行きます。
また``` $.ajax ```で``` dataType ```は``` json ```でという指定があったのでhtmlファイルを参照するのではなく、json形式のファイルを参照しようとします。それが以下の``` create_comment.json.jbuilder ```ファイルです。

#create_comment.json.jbuilderファイル

```
json.note_id           @note.id
json.note_edit_user    @note.user.full_name
json.comment_id        @comment.id
json.comment_user_name @comment.reply_user.full_name
json.comment_date      @comment.created_at.strftime('%Y/%m/%d %H:%M')
json.content           @comment.content
```
create_comment.json.jbuilderファイルではコントローラで受け取った値を全てjson形式にして値を受け取ります。例えば、

```
json.content @comment.content
```
これはjavascript側で

```
data {
  'content': '@commentの投稿内容(content)'
}
```
と渡されるので``` data.content ```でjavaScriptからRailsの変数の値を取り出せるようになります。
そして最後に先ほどのjsファイルで残っていた``` .done(function(data) ```以降の処理がなされるようになります。

##jsファイル(さっきの続き)
```js
// ----------------------------------------------------------------------------
      .done(function(data) {
        var html = buildHTML(data);
        $('.comments').append(html);             // json形式で値を受け取った後の処理
        $('.content').val('');
      })
// ----------------------------------------------------------------------------
      .fail(function(data) {
        alert('エラーが発生しました。')               // 受け取り失敗の場合の処理
      });
// ----------------------------------------------------------------------------
    }
  });
```
Railsでの変数を受け取ることができたのであとは投稿内容をhtmlに差し込むだけです。``` ${} ```内に受け取った変数を埋め込んでいます。

```js 
function buildHTML(data) {
    var comment_write_user = data.comment_user_name == data.note_edit_user ? 'editor-comment' : 'reply-user-comment'
    var html = `<div class="comment-container">
                  <div class=${ comment_write_user }>
                    <div class="comment-header">
                      <div class="user-info">
                        <i class="far fa-user"></i><span class="user-name">${ data.comment_user_name }</span><span class="post-time">${ data.comment_date }</span>
                      </div>
                      <span class="custom-btn">
                        <input class="comment_id" type="hidden" value=${ data.comment_id }>
                        <div class="i far fa-edit edit-btn"></div><div class="delete_comment i far fa-trash-alt delete-btn"></div>
                      </span>
                    </div>

                    <div class="comment comment-${ data.comment_id }">
                      <p>${ data.content }</p>
                    </div>

                    <div class="edit-comment edit-comment-${ data.comment_id }">
                      <textarea class="form-control content" placeholder="コメントを入力してください" id="content_${ data.comment_id }" name="content[${ data.content }]">
                      </textarea>
                      <div class="btn btn-success float-right update_comment" id="comment_submit_${ data.comment_id }">更新</div>
                      <div class="btn preview-btn">プレビュー</div>
                    </div>
                  </div>
                </div>`
    return html;
  }
```
そして実際にviewに追加が反映され、完成です。

#参考サイト
[Ajaxを用いて非同期通信でチャットメッセージを送る](https://qiita.com/kumasuke/items/36365bcdf30eaea65250)