Rails6でcocoonを使い、callbackが発火しなかったときの対処

# 前提
　個人開発でcocoonというgemを使うことにした。入力フォームを作る際に、項目を簡単に増減できるお手軽gemだ。

https://github.com/nathanvda/cocoon

　なんやかんやあって項目を増減することはできたが、callbackを実装するところで手間取った。自身のためにもすこし記録を残しておく。

# やりたいこと

　gemはさくさく動くが、増減する項目数について制限を設けたかった。項目が0になってしまうのは捨てたいし、10個20個と追加できてしまうと見栄えが悪くなる。

　cocoonはcallbackを用意している。

> #### Callbacks (upon insert and remove of items)
On insertion or removal the following events are triggered:

> - cocoon:before-insert: called before inserting a new nested child, can be canceled
- cocoon:after-insert: called after inserting
- cocoon:before-remove: called before removing the nested child, can be canceled
- cocoon:after-remove: called after removal


[Callbacks (upon insert and remove of items)--cocoon](https://github.com/nathanvda/cocoon#callbacks-upon-insert-and-remove-of-items)

項目の増減前にそれぞれイベントをつくることができ、いいかんじに制限してくれる。

いいな！やってみよ！

と思ったけどなぜかイベントが起こらなかった。

# 調査

　いろいろと調べることにした。cocoonは一応動作はしているので、gemがインストールされていないとか、環境設定が悪いとかそんなことではなさそうだった。まずは公式のissueから探っていった。

 
https://github.com/nathanvda/cocoon/issues/275
https://github.com/nathanvda/cocoon/issues/353

　これが一番参考になりそうだった。↓

https://github.com/nathanvda/cocoon/issues/399

開発者のnathanvdaさんがいろいろと回答していた。

> from your code, I thought it could be a turbolinks problem? (using the ready event only, but in that case the click-event would also not be captured)
there is, by coincidence, not another event-handler that captures it first and stops propagation?

　railsのturbolinksにまつわる問題なのでは？callbackイベントの前に別のイベントが入って邪魔をしているのでは？

　質問者から回答があった。

> dskvr commented on 21 Dec 2016

>I feel like an idiot.
 jQuery was being included twice, I didn't confirm this was the exact because I need to move like the wind at this point, but what I speculate is that the listeners were bound on a different instance of jQuery than Cocoon was firing the events from. How I didn't notice this before I have no idea. It took an accident and some snooping in adjacent partials to notice the discrepancy. (I hit my head against the table at the cafe for a good 10 minutes.)

　カフェで10分ほど頭をゴンゴンやっていたらしい。そこではない。「ちょっと勘違いしてたわ。jQueryを二重に読み込んでいたよ。なんできづかなかったかというと、ほにゃらら...」ちょっと英語は苦手だ。

 stack over flowでもcocoonで検索してみた。「[cocoon-gem] callback」でいっぱいでてくる。

https://stackoverflow.com/search?q=%5Bcocoon-gem%5D+callback&s=951d2e01-2665-4c9e-9195-3728daca227e

　驚きなのが大体の回答欄で開発者のひとが答えていることだった。このひとすごいな。

　だいたいポイントは一緒だった。

- 単純なスペルミス
- callbackを使うには要素をdivで囲んでおく必要がある
- turbolinksの問題かもしれないので記載方法に注意する


# 修正
　回答を見ながら自分のコードを修正していった。viewファイルはそのままにした。

```erb:app/views/reports/new.html.erb
<!-- 略　-->
  <%= form_with model: @report, local: true do |f| %>
    <p>登録日:<%= f.date_field :reported_on , value:Date.today%></p>
    <div id="report_items">
      <%= f.fields_for :report_items do |item| %>
        <%= render "report_item_fields", f: item %>
      <% end %>
      <div class="links">
        <%= link_to_add_association "追加", f, :report_items %>
      </div>
    </div>
    <p>コメント:<%= f.text_field :content %></p>
    <%= f.submit "登録" %>
  <% end %>
<!-- 略　-->
```

JSファイルを修正した。

```javascript:home.js
//$(document).ready(function() { 修正前　
$(document).on("page:load turbolinks:load", function() { //修正後
//...以下略
```

　turbolinksが読み込まれた後にイベントを出すようにした。これでもうまくいかなかった。

　スペルミスはとくになかった。その証拠に

```javascript
$(document).on("page:load turbolinks:load", function() {
  $('#report_items').on('click', function() {
    console.log("click");
  });
//...略
```

　としたら動いた。クリックイベントは動いていた。

　ちょっとよくわからない。

## 真因

　さきのgithubのissueで誰かが言っていたことを思い出した。

> jQuery was being included twice

　jQueryが二重に呼び出されているかもしれない。そもそもRails6はJSをどのように読み込んでいるのか。

https://nobunobu1717.site/?p=1292#Rails6

　ここが参考になった。私はそれまでapplication.jsとは別のファイル(home.js)を作成し、そこにさきのcallbackを書いていた。


#### 変更前

```
app/javascript/packs/
 - application.js
 - home.js   
```

そしてviewで以下のように設定していた。

```erb:app/views/layouts/application.html.erb
<!-- 略-->
   <head>
<!-- 略-->
    <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
    <%= javascript_pack_tag 'home' , 'defer':'true' %>
  </head>
<!-- 略-->
```

これがいけなかったのかもしれない。参考記事の方法1のように、application.jsにhome.jsの参照を追加させてみる。

#### 変更後

```
app/javascript
  - packs
    - application.js
  - home.js （フォルダ移動）
```

```erb:app/views/layouts/application.html.erb
<!-- 略-->
   <head>
<!-- 略-->
    <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
  </head>
<!-- 略-->
```

```javascript:app/javascript/packs/application.js
require("@rails/ujs").start()
require("turbolinks").start()
require("@rails/activestorage").start()
require("channels")

require("jquery")
require("@nathanvda/cocoon")

require ('home') //追加
```


## 結果

　callbackが発火するようになった。よかった。

　問題が起こったら公式を確認する、issueをみるというのは良いアプローチなんだなあと思った。