JavaScript
jQuery
JSON
rails,
rails5,

Rails 5.1+jQueryでajaxを試す (罠にハマる)

More than 1 year has passed since last update.


Rails5.1でajaxができない?

ajaxのやり方を聞かれたのでRails4時代の知識をドヤ顔で答えていたら、すっかりやり方がかわっていて浦島太郎感が半端ないです。

(react? vue.js? なにそれ)

そもそも『rails5.1 + jQuery(rails-ujs)でajaxをやる』という路線自体が、急速に消滅に向かっている空気を感じるので、最新の情報が全く見つからなかったです。トホホ。


結論


1. jquery-ujsの代わりに、rails-ujsを使おう

5.1からjquery依存が廃止されたのに伴い、ujs関連はrails-ujsとして切り出されたようです。


Gemfile

gem 'rails-ujs'



application.js

//= require jquery

//= require rails-ujs
//= require turbolinks
//= require_tree .

turbolinksはお好みで。


2. イベント取得の方法が変わっているので要注意

上記と関連しているのですが、

  $('#hoge').on('ajax:success', function(event, data, status, xhr) { ... 

でよかったのが、

  $('#hoge').on('ajax:success', function(event) { ...

と変更になりました。データの取り出しは、 こんな感じ。

data   = event.detail[0];

status = event.detail[0];
xhr = event.detail[0];

(ajax:success ajax:error ともに共通のかたちです)


とりあえずやってみる

いくつか実装の方向はあるようですが・・


直接jsを返す

コントローラーで直接hoge.js.erb をレンダリングして返すヤツですね。

そういや標準機能ですが、いろいろあってあんまり見ないですね。


JSONを返す

よく見るやり方としては、2つあるような気がしています。


1) ajaxリクエスト送信、レスポンス受信ともにjsで書く

ボタンクリックを待ち受けておいて、



$.ajax({...}).done(...).fail(...)

みたいにするアレですね。


2) リクエスト送信はフォームにまかせ、JSONを受け取る部分をjsで書く (今回はコレ)

ちょっと楽をしたいので、送信は link_to ... remote: trueでrailsまかせ。

受信部分だけをjsで書いてみる

⇒ 罠だった。


やってみる

ブラウザでボタンを押したら、サーバーがランダムに選んだ「じゃんけん」の手を返してくる

・・・というだけの、手抜きサンプルをつくってみようと思います。

教える都合上、js、HTMLともにクラシックな記法で書いていきます。


config/routes.rb

Rails.application.routes.draw do

root to: 'rps#index'
get 'rps/new'
end

まずフォームからajaxリクエストを送信するところ。


index.html.erb

<div>

じゃんけん・・ <span id = 'shape'> <%= @shape %></span>
</div>

<br />

<%= button_to 'ぽん!', rps_new_path, method: :get, remote: true %>


ここから飛んできたリクエストを受けるサーバー側としては・・


controllers/rps_controller.rb

class RpsController < ApplicationController

def index
@shape = shape
end

def new
respond_to do |format|
format.json {
render json: {shape: shape}, status: 200
}
end
end

private

def shape
['✊', '✌️', '✋'].sample
end
end


jsにマルチバイト文字を投げるとかアレですが、手抜きサンプルということで・・・(ゲホゲホ)

こんな画面になりました。

スクリーンショット 2017-07-09 0.06.09.png

chromeの検証画面で見てみると・・・

responce.gif

ふむふむ。

押すたびにJSONが帰ってきているよね。


ハマる

さて、あとは飛んできたJSONをjsでキャッチして画面を差し替えるだけ。

たしかこんなでしたよね〜。


rps.js

function set_ajax() {

$('#ajax-btn').on('ajax:success', function(event, data, status, xhr) {
$('#shape').text(data['shape']);
});

$('#ajax-btn').on('ajax:error', function(event, xhr, status, error) {
alert("失敗!");
});
}

$(document).on('turbolinks:load', set_ajax);


ところが・・・ウンともスンとも言ってくれない。

Uncaught TypeError: Cannot read property 'shape' of undefined

んにゃ?

concole.logで、dataの中身を見てみると・・やっぱりundefinedだよこれ。

ナヌ。

jquery-ujsの公式wiki を読んでも、『引数は [event, xhr, status, error] が取れる』 ということがしっかり書いてあるんだけどな・・・。

このあとturbolinksを切ったりgemを入れ替えたりだとか、散々な長旅を経た結果、ようやくいいドキュメントを発見しました。

rails5.1からjQuery依存を捨てたことによってもろもろ変更があったらしい。

スクリーンショット 2017-07-09 0.45.41.png

つらつら読んでいくと・・・

all custom events return only one parameter: event. 

In this parameter, there is an additional attribute detail
which contains an array of extra parameters.

カスタムイベントは全て1つのパラメータeventだけを返します。パラメーターの中には、他のパラメーターを含む配列をふくむ属性detailが入っています

おおおおーこれか。

そりゃ余計な引数をつけたらundefinedになるわけだorz

というわけで、


rps.js

function set_ajax() {

{
$('#ajax-btn').on('ajax:success', function(event) {
data = event.detail[0];
console.log(data);
$('#shape').text(data['shape']);
});

$('#ajax-btn').on('ajax:error', function(event) {
alert("失敗!");
});
}

$(document).on('turbolinks:load', set_ajax);


こういう風に変えたら

Janken.gif

・・・ど、どや❗ajaxカンタンだろ? (虫の息)


感想

手抜きアプローチを選択したはずが時間がかかってしまいました。


rails-ujs依存というツラミ


  • このアプローチだとrails-ujs依存というツラミがいつまでも残るようなモヤモヤ感がのこります。仕様がまた変わるとか、サポートされなくなったりすると、影響をもろに受けそう。


じゃあどうするの?


  • モダンなjsフレームワークを使わないのであれば、自分で $.ajax()... を呼んでajaxイベントを発火させるのが最も無難なオプションでしょうか。

  • いずれにせよ、フロント周りはrailsと切り離すというのが健全なアプローチ、といういつもの話に収束するネタなのでしょうか。

巷では割りと話されている感のある話題ですが、これだという資料が見つけられなくて困りました。

みなさんはどうされていますか?