Rails で ajax を始めるときに理解しづらい点と、jQueryを使わず Vue.js を使う方法の整理

Rails 5.2.4.1で動かしています。6系でもたぶん一緒

基本的なことは Rails ガイドを参照していきます。
https://railsguides.jp/working_with_javascript_in_rails.html

# respond_to に、format.js が必要

```ruby:abc_controller.rb
def update
  :
  respond_to do |format|
    if ovf.update(params[:id])  then
      format.html { redirect_to polymorphic_url([ovf]), notice: '更新されました。' }
      format.js
      format.json { render :edit, status: :created, location: @ovf }
    else
      :
    end
  end
end
```

Railsガイドによると
> format.jsがrespond_toブロックの中にある点にご注目ください。これによって、コントローラがAjaxリクエストに応答できるようになります。

とのこと。


# Controllerが (1) jsを返す方法と、(2)JSONを返す方法がある

## (1) update.js.erb が必要
Railsガイドによると format.js に対応する事に続けて、次の記述がある。

> 続いて、対応するapp/views/users/create.js.erbビューファイルを作成します。実際のJavaScriptはこのビューで生成され、クライアントに送信されてそこで実行されます。

上記の Controller では update アクションから呼んでいるので、 update.js.erb が必要となる。これはJSを返す方法にあたる。Controller が、format.js で指定された JS ファイルを読み込んで、それをクライアント（ブラウザ）へ送って、JSが実行される。

Railsガイドでは「create.js.erbビューファイルを作成します」とあるが、partial も指定できる。

```ruby 
  format.js {render partial: "reload" }
```

これで
create.js.erb の代わりに _reload.js.erb が読み込まれる。partial なのでアンダースコアに注意。

## (2) app/assets/javascripts/application.js にAjaxを書く
application.js ではなく、分割した別ファイルにする場合も同様。
Railsガイドには記載がない。

この方法の場合、(1) に書いたJSの記述は app/assets/javascripts 配下の.js ファイルに移動する。CoffeeScript でもOK。

Controller は JSON を返すように変更する。

````ruby 
 format.js{ render json: {ovf: @ovf}, status: :ok }
```
これで update.js.erb や partial で生成されたJSではなく、json で返すように変わる。

このJSONを使うように、app/assets/javascripts で JavaScript を書いておく必要がある。どう書くかについては、Railsガイド3.5 rails-ujsのイベントハンドラに記載がある。

### 3.5 rails-ujsのイベントハンドラ
Railsガイドの例では、次のように記載がある。

```js
document.body.addEventListener('ajax:success', function(event) {
  var detail = event.detail;
  var data = detail[0], status = detail[1], xhr = detail[2];
})
```

上記によって、event.detail[0] から、Controller が返した JSON を取得できる。

turbolinks を使っている場合は、Railsガイド 5.2 ページ変更イベント に記載があるように、 turbolinks:load を使う。

```js
function set_ajax() {
  document.body.addEventListener('ajax:success', function(event) {
    var detail = event.detail;
    var data = detail[0], status = detail[1], xhr = detail[2];
    alert("Success!" + data + status + xhr);
  });
  document.body.addEventListener('ajax:error', function(event) {
    alert("fail");
  });
}
document.addEventListener("turbolinks:load", set_ajax );

```


### 疑問：format.json はなんだ？
Controller には、次のような記述もある。scaffold で生成した場合についてくる。

```ruby 
 format.json { render :edit, status: :created, location: @ovf }
```

view から data-remote: true した場合は、 format.js{} が反応しているので、 format.json{} は使われていない。

リクエスト時にJSONフォーマットを指定してくるAPIや、独自のJSがJSONでリクエストしている場合は必要。なければ不要なので消しておく。

cf. 
[Let'sプログラミング JSON/XML形式で出力する](https://www.javadrive.jp/rails/controller/index7.html) では、
`http://localhost:3000/movies/index.json` のようにJSON形式を明示的にリクエストした場合に、 format.json が使われると記載されている。

#その他
## escape_javascript は j で書ける

```erb
<%= escape_javascript(render: @user) %>
```

の代わりに

```erb
<%= j(render: @user) %>
```
となる。

## form_with 

オプション local: true を指定しない。デフォルトでは入らなくなっているので、そのままでよい。

```haml 
= form_with(model: @ovf, url: url, method: method ) do |f|
  = f.submit(I18n.t('..button'), data: { "disable-with": "updating" })
```

## $('#something') の記述を避ける
jQuery をやめようと思うので、 `$(document)` や `$('#something')` を避ける。参考になるドキュメントが一気に減ってしまう。


# Vue でバインディングする
こちらを参考にした。

Rails + Ajax で Vue.js のバインディングを利用する
https://qiita.com/NaokiIshimura/items/c7c1330743757925e385

```haml:app/views/../scripts/_vue_test.html.haml
%p#script_update {{ message }}

:javascript
  var script = new Vue({
    el: '#script_update',
    data: {
      message: 'default message should be ID',
    },
  });
```

%p#script_update には、"default message should be ID" が表示されている。
Controller で生成した ovf を local 変数(同名のovf)として_reload.js.erb へ渡し、値をVueで定義した script.message へ入れる。

```ruby:controller#update
 format.js {render partial: "reload", locals: {ovf: ovf },status: :ok }
```


```erb:app/views/../_reload.js.erb
script.message = <%= ovf.script %> ;
```

この方法がVueにレンダリングを任せられるので、楽そうな印象。

- data-remote: true を使って、Rails 標準のままrequestできる。
- Rails が js を返せる限りは動く。
    - addEventListener が不要
- ただし、JSON を受け取る必要もない代わりに、View 内の Vue に密結合する。
    - Vueがシンプルなうちは、Controller から view を意識している程度ではある

## Vue で XHR
VueそのものはXHRを扱えない。他のライブラリと組み合わせる必要がある。

jQuery ではない方法では axios がある。Rails 標準の data-remote: true を使う場合は、追加のXHRは不要。
https://github.com/axios/axios

### Rails の data-remote:true を動かす
Vue methods から submit するときは、data-remote が効かない。代わりにRails.fire を使うとよい。（が、公式ドキュメントが分からない）

```js
  checkbox_and_so_on: function(event){
    // event.target.form.submit()
    Rails.fire(event.target.form, 'submit')
  }
```

cf. rails-ujs と form_with の使い方
https://www.bokukoko.info/entry/2017/12/22/194327


## Vue で押さえておきたいこと
今からVue.jsを始める人のための「知るのを後回しにしてよい」n個のこと
https://qiita.com/fruitriin/items/3249bb24d60932bb42ee