2019年にRails + Ajaxを整理してみる(サンプルアプリ＆コード付き)

Rails上でAjaxを動かす、という良くありそうな話。

ただ調べてみるとやり方が色々あって、
Rails歴半年ちょいの私には何が正しいのかさっぱり分からなかった。

という訳で整理してみる。同じ境遇にある人の助けになれば嬉しい。

jQueryとかを用いた、古式ゆかしい(らしい)やり方ですので、
Vue/React等をお使いの方々はおかえりください(涙)

間違ってる部分とかあったら、コメントいただければ幸いです。
(特に図の部分)

## この記事がよく刺さりそうな人
- Railsの基礎はわかる
- Ajaxの雰囲気はわかる
- JavaScript & jQueryも本気出せばちょっと書ける  
(決してチョットデキルではない)
- RailsでAjaxはあまりやった事がない  
もしくは「良く分からんけどまぁ動いてるからヨシ！」で乗り切った

## とりあえず結論
Rails + Ajax の実現方法は、
ざっくり以下の3パターン&その組み合わせっぽい。

**1. Rails推奨方式**
**2. フロントはJavaScriptだけでやる方式**
**3. AjaxのリクエストはRails & レスポンス以降はJavaScriptでやる方式**

## 方式別サンプルアプリ＆コード
作ったのは、フォームに文字を入力＆ボタンを押すと、Ajaxを使って文字が書き換わるアプリ。
(アプリを名乗るのはおこがましいかもしれない)

これを上の３方式のそれぞれでやってみた。
Ajaxでやる意味ある？というツッコミは無しで...

![ajax-succeed.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283780/10e7419f-1ea5-01c4-43c1-7067d4996e26.gif)

コードは本当に必要最低限なので、色々細かいツッコミはご容赦くださいm(_ _)m 
逆に手元で再現する分にはやりやすいはず。。。

あとjQueryを使ってます。入れ方は以下参照。
[Rails 5.2 jQuery 動かし方 - Qiita](https://qiita.com/NoharaMasato/items/911218d67cd052d874fd)


### 共通処理
サーバ(Rails)側の処理は、3つの方式でほぼ共通。

```ruby:routes.rb
Rails.application.routes.draw do
  get 'static/top'
  post 'static/ajax_update', to: 'static#ajax_update'
  post 'static/ajax_update2', to: 'static#ajax_update2'
end
```

```ruby:static_controller.rb
class StaticController < ApplicationController
  def top
  end

  # 1. Rails推奨方式 で使用
  def ajax_update
    @text = params[:data]
    render
  end

  # 2. フロント側はJavaScriptだけでやる方式 
  # 3. AjaxのリクエストはRails & レスポンス以降はJavaScriptでやる方式
  # で使用
  def ajax_update2
    @text = params[:data]
    render plain: @text
  end
end
```

### 1. Rails推奨方式
Railsガイドに書かれたやり方
[Rails で JavaScript を使用する - Rails ガイド](https://railsguides.jp/working_with_javascript_in_rails.html)

```erb:ajax_update.js.erb
var user = '<%= "#{ @text }" %>'
$('#ajax-test1').text(user);
```

```slim:top.html.slim
h1 Static#top
p Find me in app/views/static/top.html.slim

#ajax-test1 Ajax: Rails依存

#ajax-request1
  = form_with url: static_ajax_update_path do |f|
    = f.text_field :data
    = f.submit 'Post Ajax'
```

図に表すと多分以下の感じ。
もうガッツリRailsに乗っかっている状態。

![rails-ajax.001.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283780/a70f78f1-b0ba-d447-02fd-f5b8b54276b1.jpeg)


肝は以下2点

- xxx.js.erbからJavaScriptをレンダリングしてフロントに返す
- フロント側で受け取ったJavaScriptを実行

個人的にはRails側で、JavaScriptをレンダリングしている辺り、
少し気持ち悪い。。。

ただコード量は必要最低限で済むし、
Rails推奨であることからトラブルも起きにくそう。
基本はこれでいいのではないだろうか。

なお細かい処理がしたい場合には不便になることもある様子で、
何だかんだ使わないと言う話もあるらしい。
参考：https://qiita.com/ka215/items/dfa602f1ccc652cf2888


### 2. フロント側はJavaScriptだけでやる方式
Railsにあえて叛逆していくやり方。

```slim:top.html.slim
#ajax-test4 Ajax: ほぼJS(jQuery)
= text_field_tag 'static[ajax_data2]'
= button_tag 'Post Ajax', id: 'btn2'
```

```js:ajax_request_response.js
$(document).ready( () => {
  $('#btn2').on('click', (e) => {
    e.preventDefault();

    const param = $('#static_ajax_data2').val();

    // CSRFトークンを取得＆セット
    $.ajaxPrefilter( (options, originalOptions, jqXHR) => {
        if (!options.crossDomain) {
          const token = $('meta[name="csrf-token"]').attr('content');
          if (token) {
               return jqXHR.setRequestHeader('X-CSRF-Token', token);
           }
        }
    });

   $.ajax({
      url: `/static/ajax_update2`,
      type: 'POST',
      data: {
        data: param
      }
    })
    .done( (data, textStatus, jqXHR) => {
      var result = $('#ajax-test4');
      result.text(data);
    });
  });
});
```

図に表すと多分以下の感じ。
Ajaxに関しては、Railsには頼らないという強い意思が見える。

![rails-ajax.002.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283780/1168e763-ae14-2eb8-a2eb-198c8a401886.jpeg)


肝は、
RailsのCSRF対策のために、
CSRFトークンの取得＆セットを行なっている所。

具体的なやり方は以下の記事を完全リスペクトしましたm(_ _)m
https://qiita.com/a_ishidaaa/items/7c3fa339d3bea25a9ba8

ざっくり言うと、RailsではCSRFという脆弱性への対策として、
Postのリクエスト時にトークン(身分証明みたいなもの)を使っている。
ここをカバーしてあげないと、JavaScriptからPostは出来ない。

そう、Railsからの叛逆に成功したと思いきや、
実はその呪縛から逃れきれていなかったのだ。
なんかエモい。

なお、RailsのCSRFについての詳細は以下の記事等をご参照ください。
[外部からPOSTできない？RailsのCSRF対策をまとめてみた - Qiita](https://qiita.com/KumatoraTiger/items/cc6a1107374cce500e6d)

### 3. AjaxのリクエストはRails & レスポンス以降はJavaScriptでやる方式
Railsへの依存を減らしつつ、CSRF対策はRailsによろしくできるやり方。

```slim:top.html.slim
= form_with url: static_ajax_update2_path, id: 'ajax-request-3' do |f|
  = f.text_field :data
  = f.submit 'Post Ajax'
```

```js:ajax_response.js
// Ajax: form送信はRails、受信以降はJS
$( () => {
  $('#ajax-request-3').on('ajax:success', (e) => {
    const result = $('#ajax-test3');
    result.text(e.detail[0]);
  });
});
```

図に表すと多分以下の感じ。
折衷案な雰囲気。

![rails-ajax.003.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283780/b3b11f17-838c-d9da-35f7-abb65d4551b9.jpeg)


肝は、
RailsとJavaScriptの間で、
どのようにデータをやり取りされるかの理解が必要な所。

適当にやってると変なハマり方をしそう。。。

ただそこさえクリアすれば、
Railsっぽさと自由度をある程度両立できる？気がする(よく分かってない)

## まとめ
- 基本的には大人しく「1. Rails推奨方式」を使った方がいい気がする。  
(特に経験浅めの人)
- ただ不便な場合もある(らしい)ので、  
その際は「3. AjaxのリクエストはRails...」を採用、  
もしくは「1. Rails推奨方式」と組み合わせて使えば良さそう。
- Railsで開発するけどなるべく依存したくないというワガママな人は、  
「2. フロント側はJavaScriptだけでやる方式」を使えばいい...のか？

## 参考サイト
以下本記事作成に際しお世話になったサイト。見ると理解がすごく深まる。。。
[Ruby on RailsのAjax処理のおさらい - Qiita](https://qiita.com/ka215/items/dfa602f1ccc652cf2888)
[Rails 5.1+jQueryでajaxを試す (罠にハマる) - Qiita](https://qiita.com/fursich/items/c2ab11acad918000a8ea)
[Rails 5.2 jQuery 動かし方 - Qiita](https://qiita.com/NoharaMasato/items/911218d67cd052d874fd)
[RailsでのAjax - Qiita](https://qiita.com/a_ishidaaa/items/7c3fa339d3bea25a9ba8)
[jQuery.ajax()のまとめ: 小粋空間](http://www.koikikukan.com/archives/2012/10/02-005555.php)
[Rails 5.2 jQuery 動かし方 - Qiita](https://qiita.com/NoharaMasato/items/911218d67cd052d874fd)