非同期通信（Ajax）を実装してみる

###はじめに
Ajaxって何？　→　非同期通信の総称とか言われてます
何ができんの？　→　例えばページをリロードせずにデータのやりとりが可能

今記事では、簡単なアプリケーションで実際に非同期通信を行えるよう実装していきます。

---

###非同期通信の流れ
非同期通信ではJavaScriptのメソッドを利用してリクエストを送るため、
フォーム要素のデフォルトアクションを無効にする必要がある。
今回のアプリで言うと【Create Todo】ボタンを押すことによって
リクエストが送られることがデフォルトアクション。

現状は下記の通り、新しいTodoが作成されると
左上のリロードマークが動いてページが更新される。
このデフォルトアクションの無効化は後述。
<a href="https://gyazo.com/fffc477bc3fee062ee895c1d73bda267"><img src="https://i.gyazo.com/fffc477bc3fee062ee895c1d73bda267.gif" alt="Image from Gyazo" width="624"/></a>




非同期通信のリクエストがJavaScriptのメソッドを利用することはわかりました。
じゃぁレスポンスは?
デフォルトアクションを無効化すると同時に、レスポンスをJSON形式で返して欲しいということをコントローラーのアクションに明記しなければいけないらしい。JSON形式のファイルはviewsフォルダに作成可能。
→ファイルの形式は○○.json.jbuilder
※JSONとはRubyのハッシュと同様、キーとバリューの組合わせでデータを表現する形式

非同期通信ここまでのまとめ
---
①リクエストはJavaScriptのメソッドを利用
②レスポンスはJSONで
③デフォルトアクションがあれば無効化

---

早速、非同期通信に取り掛かりたいところですが、その前に準備があります。4つほど。
###①respond_to メソッドでリクエストがHTML,JSONどっちのレスポンスを求めているか条件分岐

```todos_controller.rb
  # def create
  #   Todo.create(todo_params)
  #   redirect_to :root
  # end

  # ↓↓↓↓↓　respond_to メソッドで条件分岐させる　↓↓↓↓↓

  def create
    @todo = Todo.create(todo_params)
    respond_to do |format|
      format.html { redirect_to :root }
      format.json { render json: @todo}
    end
  end
```

###②Ajaxで実装していくためにjQueryのGemを導入
Gemfileに `gem 'jquery-rails'` を追記して`bundle install`

###③ディレクトリを整える

```
app
　└ assets
　    ├ config
　    ├ images
　    ├ javascripts            ←①作成
　    │     ├ application.js   ←②作成
　    │     └ todo.js          ←③作成
　    └ stylesheets
```

###④呼び出すために色々記述
`app/views/layouts/application.html.haml`に
` = javascript_include_tag 'application'`を追記し、
viewファイルから呼び出せるようにします。

続いて、`app/assets/config/manifest.js`に追記します。

```manifest.js
//= link_tree ../images
//= link_directory ../javascripts .js  ←この行を追記
//= link_directory ../stylesheets .css
```

最後に`app/assets/javascripts/application.js`に記述

```application.js
//= require jquery　   ←RailsでjQueryを使用するためのもの
//= require rails-ujs　←RailsでJavaScriptを使用するためのもの
//= require_tree .　   ←application.jsが存在するディレクトリのJavaScriptファイル全てを読み込む
```


はい　ここまでで準備完了。
まだ非同期通信実装の入り口に立っただけ...さすが初学者の山場。

実装に取り掛かかります
---

```todo.js
$(function() {
  $('.js-form').on('submit', function(e) {            
    e.preventDefault();                           
    let todo = $('.js-form__text-field').val();
  });
});
```
①まず大前提として**$(function(){...})**が必要。jQueryを使う時は絶対記述するくらいの感覚

②２行目の**$('.js-form').on('submit', function(e)**
まずは前半 **$('.js-form')** は下記の２行目のクラス名の部分

```index.html.haml
.contents
  = form_with model: @todo, html: { class: 'form js-form' }, local: true do |f|
    = f.text_field :content, class: 'form__text-field js-form__text-field'
    = f.submit class: 'form__submit js-submit'
  %ul.todos
    = render @todo
```

今回はクラス名ですが、**$(#'〇〇')**とidを使用することも可能。
後半、**submit**イベントを設定　(**.js-form**が送信されたときに実行されるイベント)
**function(e) の e** 今回のコードで言うと**submit**イベント発火時の情報を取得してるみたい。
イメージ的には  **$('.js-form').on('submit',** までの情報が入ってる感じかな

③そして３行目の **e.preventDefault( );**
初めに言ってたデフォルトアクションを無効化させる魔法のコード

④４行目は変数**todo**に、**f.text_field**で入力された値を代入
**val()**がvalue値を取得しています

---

さらに`todo.js`に記述していきます

```todo.js
$(function() {
  $('.js-form').on('submit', function(e) {            
    e.preventDefault();                           
    let todo = $('.js-form__text-field').val();
// ↓↓↓↓↓　ここから続き　↓↓↓↓↓
    $.ajax({
      type: 'POST',
      url: '/todos.json',
      data: {
        todo: {
          content: todo
        }
      },
      dataType: 'json'
    })
  });
});
```

⑤**$.ajax**は非同期通信を行うための記述

⑥**$.ajax**以下の`type` `url` `data` `dataType` は**ajaxメソッドのオプション**

| オプション          | 説明                              | 
|:-----------------|:----------------------------------|
| type             |HTTP通信の種類で、GETとPOSTがある       |
| url              | リクエスト送信先のurl                  |
| data             | サーバに送信する値                    |
| dataType         |サーバから返されるデータの型 （json一択では？）|

ややこしいのが**data**の部分で、横書きすると↓
{ todo: { content: todo（テキストフィールドに入力された値）} }

まとめると
通信方法**POST**で、**'/todos.json'**というURLに
**{ todo: { content: todo（テキストフィールドに入力された値）} }**を送信しますよ。
サーバからは**json**で値を返してね。ってこと。

---

さらにさらに`todo.js`に記述していきます

```todo.js
$(function() {
  $('.js-form').on('submit', function(e) {            
    e.preventDefault();                           
    let todo = $('.js-form__text-field').val();
    $.ajax({
      type: 'POST',
      url: '/todos.json',
      data: {
        todo: {
          content: todo
        }
      },
      dataType: 'json'
    })
// ↓↓↓↓↓　ここから続き　↓↓↓↓↓
    .done(function(data) {
    })
    .fail(function() {
      alert('error');
    });
  });
});
```

非同期通信を**done（成功）**と**fail（失敗）**で処理を分けてます

⑦**done(function(data)**の**data**（引数名は別になんでも良い）は
リクエストによって返ってきたレスポンスが代入される
レスポンスは確かJSONでって言ってたよな...てことで`todos_controller.rb`を見ると

```todos_controller.rb
def create
  @todo = Todo.create(todo_params)
  respond_to do |format|
    format.html { redirect_to :root }
    format.json { render json: @todo}   ← ココ
  end
end
```

上記のようにjsonの場合は作成したTodoをjson形式にして返す記述がしてある
この@todoをどうするかですが、今記事の初めのほうに言っていた
『JSON形式のファイルはviewsフォルダに作成可能。ファイル形式は○○.json.jbuilder』を実行

⑨viewsフォルダの中のtodoフォルダに**jbuilderファイル**を作成

```
app
　└ views
　    ├ todos                 
　    │   ├ _todo.html.haml   
　    │   ├ index.html.haml   
　    │   └ create.json.jbuilder   ←作成 ※createはアクション名
```

⑩`create.json.jbuilder`に記述

```create.json.jbuilder
json.content @todo.content
```
@**todo.content**の内容が**json.content**で送られますよという意味

じゃ**@todo.content**ってどこから出てきたのって話
ターミナルでコンソールを起動して、@todo = Todo.firstを実行すると

```
=> #<Todo id: 1, content: "test1", ...省略>
```
データベースのtodosテーブルにある、1つ目の投稿情報全てが出てきました。

続けて、**@todo.content**　を実行すると

```
rb(main):002:0> @todo.content
=> "test1"
```
todosテーブルにある、1つ目の投稿のcontentカラム情報が表示される。

話を元に戻すと、今回はこの**"test1"**って内容が**json.content**で送られてますよってこと
**@todo.content**はどこから出てきた → コンソールで確認するとわかりやすい

⑪jbuilderでブラウザに表示させるHTMLを作成
以下の`test1` `test2` `test3`はそれぞれ
【Create Todo】ボタンが押されるたびに生成されている
[![Image from Gyazo](https://i.gyazo.com/622205b7d0930dc1ed5d3b14e18bd582.png)](https://gyazo.com/622205b7d0930dc1ed5d3b14e18bd582)

検証ツールで確認すると``
<li class="todo">
　〇〇
</li>``
というまとまりになっている。このまとまりをbuildTodoで作成

```todo.js
$(function() {
      // ↓↓↓↓↓　追記①　↓↓↓↓↓
  function buildTodo(form){
    let html = `<li class="todo">
                  ${form.content}
                </li>
                `
    return html
  }
      // ↑↑↑↑↑　追記　　↑↑↑↑↑
  $('.js-form').on('submit', function(e) {
    e.preventDefault();
    let todo = $('.js-form__text-field').val();
    $.ajax({
      type: 'POST',
      url: '/todos.json',
      data: {
        todo: {
          content: todo
        }
      },
      dataType: 'json'
    })

    .done(function(data) {
      // ↓↓↓↓↓　追記①　↓↓↓↓↓
      let html = buildTodo(data)
      // ↑↑↑↑↑　追記　　↑↑↑↑↑

      // ↓↓↓↓↓　追記③　↓↓↓↓↓
      $('.todos').append(html)
      // ↑↑↑↑↑　追記　　↑↑↑↑↑

    })
    .fail(function() {
      alert('error');
    });
  });
});
```
追記①では、描画内容をbuildTodoで生成している。
htmlという変数に、先ほど検証ツールで確認したまとまり(DOM)を代入
〇〇の部分にはbuildTodo(form)の引数.content ※引数はなんでも良い

追記②は、非同期通信が成功したときHTML情報を描画するという記述
buildTodoを呼び出している

追記③では、ajaxで非同期取得したHTMLをappendで反映させてます
$('.todos')のクラス名はどこからきているかと言うと↓
[![Image from Gyazo](https://i.gyazo.com/7d30c3fe5270000e3af6bd5291a7dc60.gif)](https://gyazo.com/7d30c3fe5270000e3af6bd5291a7dc60)
それぞれのli要素を囲っているul要素のクラス**".todos"**です。
このクラス名を指定することでul要素内に新たにli要素が生成されていきます。

---

さて、現状はここまできています。↓
[![Image from Gyazo](https://i.gyazo.com/992e54a2c007ed0069a9409f0b742d20.gif)](https://gyazo.com/992e54a2c007ed0069a9409f0b742d20)
非同期通信はできていて、リロードなしでも投稿内容が更新されている。
ここで2つ気になることが出てきました。

1.前回の投稿内容が残ったまま
2.【Create Todo】ボタンが一度しか押せない

これら2点を解消していきます。

まず1つ目
前回の投稿内容を投稿と同時に消えるようにする

```todo.js
$(function() {
  function buildTodo(form){
    let html = `<li class="todo">
                  ${form.content}
                </li>
                `
    return html
  }
  $('.js-form').on('submit', function(e) {
    e.preventDefault();
    let todo = $('.js-form__text-field').val();
    $.ajax({
      type: 'POST',
      url: '/todos.json',
      data: {
        todo: {
          content: todo
        }
      },
      dataType: 'json'
    })

    .done(function(data) {
      let html = buildTodo(data)
      $('.todos').append(html)
      // ↓↓↓↓↓　追記　↓↓↓↓↓
      $('#todo_content').val("");
      // ↑↑↑↑↑　追記　↑↑↑↑↑
    })
    .fail(function() {
      alert('error');
    });
  });
});
```

**$('#todo_content').val("")**を追記
val("")とすることで値を空にできます。先ほどのval()と似ているので注意
**#todo_content**はどこから？
[![Image from Gyazo](https://i.gyazo.com/bf9f26d7804743c1537f0f789e27274b.png)](https://gyazo.com/bf9f26d7804743c1537f0f789e27274b)
検証ツールで確認 → 上記の青枠のところを見てみると**id="todo_content**とあります。
なのでここのidを引っ張ってきたわけです。別にクラスでも良いと思いますが。

続いて2つ目
【Create Todo】ボタンをリロードせずに押せるようにする

```todo.js
$(function() {
  function buildTodo(form){
    let html = `<li class="todo">
                  ${form.content}
                </li>
                `
    return html
  }
  $('.js-form').on('submit', function(e) {
    e.preventDefault();
    let todo = $('.js-form__text-field').val();
    $.ajax({
      type: 'POST',
      url: '/todos.json',
      data: {
        todo: {
          content: todo
        }
      },
      dataType: 'json'
    })

    .done(function(data) {
      let html = buildTodo(data)
      $('.todos').append(html)
      $('#todo_content').val("");
      // ↓↓↓↓↓　追記　↓↓↓↓↓
      $(".form__submit").prop('disabled', false);
      // ↑↑↑↑↑　追記　↑↑↑↑↑
    })
    .fail(function() {
      alert('error');
    });
  });
});
```

**$(".form__submit").prop('disabled', false);**を追記
.form__submitは【Create Todo】ボタンのクラス名を使っています。

**prop()**は、引数にプロパティを指定することでプロパティの値を取得できます。
**disabled**は無効化させる属性ですので、それを**false**で無効化しないようにします。

ここでいう無効化とは、ボタンが押せなくなるということ。それが**false**なので有効化です。

---

これでようやく完成です↓
[![Image from Gyazo](https://i.gyazo.com/ba6788e3984d7ade09abf450d0d7f0d2.gif)](https://gyazo.com/ba6788e3984d7ade09abf450d0d7f0d2)

①リロードされずにブラウザが更新されている
②前回の投稿内容が消えている
③ボタンを連続で押すことができる

全部達成できました。

---

###まとめ
非同期通信は一度コードを書いてしまえば、
テンプレとして使えるところがたくさんある印象です。

めちゃめちゃ細かくコードを読み解いていったので記事が長くなってしまいました..
ご意見、アドバイス等々、なんでもお待ちしております。

ここまで読んでいただきありがとうございます。