Rails上でAjaxを動かす、という良くありそうな話。
ただ調べてみるとやり方が色々あって、
Rails歴半年ちょいの私には何が正しいのかさっぱり分からなかった。
という訳で整理してみる。同じ境遇にある人の助けになれば嬉しい。
jQueryとかを用いた、古式ゆかしい(らしい)やり方ですので、
Vue/React等をお使いの方々はおかえりください(涙)
間違ってる部分とかあったら、コメントいただければ幸いです。
(特に図の部分)
この記事がよく刺さりそうな人
- Railsの基礎はわかる
- Ajaxの雰囲気はわかる
- JavaScript & jQueryも本気出せばちょっと書ける
(決してチョットデキルではない) - RailsでAjaxはあまりやった事がない
もしくは「良く分からんけどまぁ動いてるからヨシ!」で乗り切った
とりあえず結論
Rails + Ajax の実現方法は、
ざっくり以下の3パターン&その組み合わせっぽい。
1. Rails推奨方式
2. フロントはJavaScriptだけでやる方式
3. AjaxのリクエストはRails & レスポンス以降はJavaScriptでやる方式
方式別サンプルアプリ&コード
作ったのは、フォームに文字を入力&ボタンを押すと、Ajaxを使って文字が書き換わるアプリ。
(アプリを名乗るのはおこがましいかもしれない)
これを上の3方式のそれぞれでやってみた。
Ajaxでやる意味ある?というツッコミは無しで...
コードは本当に必要最低限なので、色々細かいツッコミはご容赦くださいm(_ _)m
逆に手元で再現する分にはやりやすいはず。。。
あとjQueryを使ってます。入れ方は以下参照。
Rails 5.2 jQuery 動かし方 - Qiita
共通処理
サーバ(Rails)側の処理は、3つの方式でほぼ共通。
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
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 ガイド
var user = '<%= "#{ @text }" %>'
$('#ajax-test1').text(user);
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に乗っかっている状態。
肝は以下2点
- xxx.js.erbからJavaScriptをレンダリングしてフロントに返す
- フロント側で受け取ったJavaScriptを実行
個人的にはRails側で、JavaScriptをレンダリングしている辺り、
少し気持ち悪い。。。
ただコード量は必要最低限で済むし、
Rails推奨であることからトラブルも起きにくそう。
基本はこれでいいのではないだろうか。
なお細かい処理がしたい場合には不便になることもある様子で、
何だかんだ使わないと言う話もあるらしい。
参考:https://qiita.com/ka215/items/dfa602f1ccc652cf2888
2. フロント側はJavaScriptだけでやる方式
Railsにあえて叛逆していくやり方。
#ajax-test4 Ajax: ほぼJS(jQuery)
= text_field_tag 'static[ajax_data2]'
= button_tag 'Post Ajax', id: 'btn2'
$(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のCSRF対策のために、
CSRFトークンの取得&セットを行なっている所。
具体的なやり方は以下の記事を完全リスペクトしましたm(_ _)m
https://qiita.com/a_ishidaaa/items/7c3fa339d3bea25a9ba8
ざっくり言うと、RailsではCSRFという脆弱性への対策として、
Postのリクエスト時にトークン(身分証明みたいなもの)を使っている。
ここをカバーしてあげないと、JavaScriptからPostは出来ない。
そう、Railsからの叛逆に成功したと思いきや、
実はその呪縛から逃れきれていなかったのだ。
なんかエモい。
なお、RailsのCSRFについての詳細は以下の記事等をご参照ください。
外部からPOSTできない?RailsのCSRF対策をまとめてみた - Qiita
3. AjaxのリクエストはRails & レスポンス以降はJavaScriptでやる方式
Railsへの依存を減らしつつ、CSRF対策はRailsによろしくできるやり方。
= form_with url: static_ajax_update2_path, id: 'ajax-request-3' do |f|
= f.text_field :data
= f.submit 'Post Ajax'
// Ajax: form送信はRails、受信以降はJS
$( () => {
$('#ajax-request-3').on('ajax:success', (e) => {
const result = $('#ajax-test3');
result.text(e.detail[0]);
});
});
図に表すと多分以下の感じ。
折衷案な雰囲気。
肝は、
RailsとJavaScriptの間で、
どのようにデータをやり取りされるかの理解が必要な所。
適当にやってると変なハマり方をしそう。。。
ただそこさえクリアすれば、
Railsっぽさと自由度をある程度両立できる?気がする(よく分かってない)
まとめ
- 基本的には大人しく「1. Rails推奨方式」を使った方がいい気がする。
(特に経験浅めの人) - ただ不便な場合もある(らしい)ので、
その際は「3. AjaxのリクエストはRails...」を採用、
もしくは「1. Rails推奨方式」と組み合わせて使えば良さそう。 - Railsで開発するけどなるべく依存したくないというワガママな人は、
「2. フロント側はJavaScriptだけでやる方式」を使えばいい...のか?
参考サイト
以下本記事作成に際しお世話になったサイト。見ると理解がすごく深まる。。。
Ruby on RailsのAjax処理のおさらい - Qiita
Rails 5.1+jQueryでajaxを試す (罠にハマる) - Qiita
Rails 5.2 jQuery 動かし方 - Qiita
RailsでのAjax - Qiita
jQuery.ajax()のまとめ: 小粋空間
Rails 5.2 jQuery 動かし方 - Qiita