Closed shuhei-T closed 2 years ago
document.addEventListener('DOMContentLoaded', function() {
let $addFieldBtn = $('.js-add-log_feeds-field-btn');
let counter = 1;
let $cocoonField = $('#activity-logs');
let $headerFeedName = $('#js-feed-name');
let $feedAmount = 5;
console.log($cocoonField) // 追加
リスナーをセットする際に$('#activity-logs')
がまだ存在しない気がするので、リスナーがセットできていない気がしますね。(カレンダーページでは ajax で#activity-logs
を作っているので)
DOMContentLoaded
は、HTMLが読み込まれたあとで一度だけ実行されるイベントだった気がします。
上記の場所にconsole.log
を仕込んで、リスナーがセットできているか確かめてみましょうか。
// app/javascript/packs/calendar/event.js
dateClick: function(info){
// クリックした日付の情報を取得
const year = info.date.getFullYear();
const month = (info.date.getMonth() + 1);
const day = info.date.getDate();
// ajaxでevents/newを着火させ、htmlを受け取る
$.ajax({
type: 'GET',
url: 'events/new',
}).done(function (res) {
// 成功処理
// 受け取ったhtmlをさっき追加したmodalのbodyの中に挿入
$('#modal').html(res);
let modal = document.getElementById('modal')
let modalObj = new Modal(modal)
modalObj.show();
// フォームの年、月、日を自動入力
$('#log_logged_at_1i').val(year);
$('#log_logged_at_2i').val(month);
$('#log_logged_at_3i').val(day);
//// このあたりでリスナーセット(#activity-logs が生成されて、取得できることを確かめて)
}).fail(function (result) {
// 失敗処理
alert("new failed");
});
},
仮説が正しかった場合、このあたりにリスナー設定処理を入れれば、動くような気がします。
@murata0705 コメントありがとうございます! ご指摘のお通り、クリックイベントの箇所でリスナーを設定し、下記コミットで変更を加えました。 bd08259981120a9b0b4ee37c72732a6edf93289e
結果、下記の要件だけ正常に動きました。(よく見るとこちらの処理の関数はcallback内で呼んでいるものではありませんでした。)
残り、下記2点は動いていません。
やはりcallbackは動いていないのかと思い、console.log()
を仕込んでみるものの、動いていませんでした。
現状の以下動作になります。console.log($cocoonField)
はモーダルをクリックした際に表示されました。
require("@nathanvda/cocoon")
event.js にこれ追加してみるとどうです?このページで cocoon が読み込まれているか確かめたい感じです。
コールバックがちゃんと動いているならあとは関数のスコープとかの問題じゃないですかね?
console.log(checkCount(counter));
とかでちゃんと関数が呼べているか見ましょうか?エラーがでているようだったら、関数をコールバック設定の上にかいたり、dateClick
関数から出してみるとどうですかね?
@murata0705 コメントありがとうございます!
console.log(checkCount(counter));
はundefined
と出てますが、正常に動く飼育記録画面(app/views/logs/new.html.erb
)でも試してみたところundefined
と出ていたので、問題ないのかなと思いました。
event.jsにrequire("@nathanvda/cocoon")
を追加しましたが、このままでは二重に呼ばれている状態で動作がおかしいので、対処法はどのようにしたら良いですか?
あ、戻り値が無いのか。checkCount
内でconsole.log
してみて、関数が実行されているか見てもらっていいですか?
event.jsにrequire("@nathanvda/cocoon")を追加しましたが、このままでは二重に呼ばれている状態で動作がおかしいので、対処法はどのようにしたら良いですか?
これはどういうことですか?require("@nathanvda/cocoon")
をevent.js
内に設定しなくてもコールバックが動くということですか?
@murata0705
コメントありがとうございます。返事が遅れてしまい申し訳ございません。下記コミットにてcheckCount
関数内にconsole.log
を仕込みました。
b75e782
以下がその後の動作になります。
動画の通り、+ボタンをクリックする毎にcocoonコールバックが呼ばれ、その中のcheckCount
関数内のconsole.log
が表示されているのが分かります。 ですが、クリックする毎に.nested-field
が2つずつ増えています。
.nested-field
というのは、+ボタンをクリックした際に増えるエリアのことです。app/views/logs/_log_feeds_fields.html.erb
がまるまる該当のコードになっています。render
でパーシャルを呼んでいます。
以下、app/views/logs/_form.html.erb
<div class="mb-3 feed-area", id='activity-logs'>
<%= f.fields_for :log_feeds, local: true, id: 'js-log_feeds-field' do |log_feeds_f| %>
<%= render 'logs/log_feeds_fields', f: log_feeds_f %>
<% end %>
<div class="links">
<div id="js-feed-name"></div>
<%= link_to_add_association f, :log_feeds, partial: 'logs/log_feeds_fields', class: 'js-add-log_feeds-field-btn' do %>
<i class="fas fa-plus-circle"></i>
<% end %>
</div>
</div>
たしかにrequire("@nathanvda/cocoon")
をevent.js
に設定したことでcocoonのコールバックが動くようになりました。しかし同時に、+ボタンをクリックした際に.nested-field
が2つずつ増えるようになってしまいました。
require("@nathanvda/cocoon")
はもともと、app/javascript/packs/application.js
内に設定されています。それが関係しているのかなと思うのですが、どうでしょうか。
.nested-field
が2つずつ増えないようにするためにはどのように対処したらよいのでしょうか。
以下がevent.js
からrequire("@nathanvda/cocoon")
をコメントアウトした際の動きになります。
コールバックが動かなくなっていますが、.nested-field
も1つずつ増えています。
// app/javascript/packs/calendar/event.js
$(document).on("change", ".js-select-form", function(event) {});
$(document).on("change", ".js-select-form", function() {};
この2つのコードを削除すれば動きそうな気がします。
そもそもの不具合の原因をまず整理しましょうか。
// app/javascript/src/cocoon.js
document.addEventListener('DOMContentLoaded', function() {})
ここで定義しているDOMContentLoaded
は、DOMが読み込まれたタイミングで発火し、一度しか発火しません。
その中で、
// app/javascript/src/cocoon.js
let $cocoonField = $('#activity-logs');
$cocoonField
.on('cocoon:after-insert', function() {
counter++;
checkCount(counter);
})
...
とcocoon:after-insert
をなどを定義していますが、$cocoonField
は、カレンダーページではDOMが読み込まれたタイミングでは undefined です。そのためリスナーを設定できていません。これがそもそもの不具合の原因です。
// app/javascript/src/cocoon.js
$(document).on("change", ".js-select-form", function(event) {})
$(document).on("change", ".js-select-form", function() {})
しかし、これらは、document にリスナーをセットしているので、問題なくセットできています。 このコードを、
// app/javascript/packs/calendar/event.js
$.ajax({
type: 'GET',
url: 'events/new',
}).done(function (res) {
// ここ
}
上記にセットすると、change
イベントに二重にコールバックがセットされます。これがおそらく二重に処理が走っている原因です。
app/javascript/packs/calendar/event.js
で、cocoon
をrequire
してみてもらったのは、
$cocoonField
.on('cocoon:after-insert', function() {
counter++;
checkCount(counter);
})
のようにリスナーをセットしたにも関わらず、コールバックが実行されなかったからです。
cocoon:after-insert
イベントのリスナーをセットしているにも関わらず、コールバックが実行されない場合は、cocoon:after-insert
イベントが定義されていない可能性があります。
cocoon
は使ったことがないので詳細は知りませんが、おそらくライブラリの中でcocoon:after-insert
イベントを定義しているはずです。
https://www.hypertextcandy.com/javascript-custom-events
そのため、カレンダーページでcocoon
を読み込めていないことを疑いました。
上記解説したあたりの javascript の仕様を再確認して、event.js
とcocoon.js
内に書くべき処理を再検討してみてください。
一部処理が二重になっている以外問題ないのであれば、
// app/javascript/packs/calendar/event.js
$(document).on("change", ".js-select-form", function(event) {});
$(document).on("change", ".js-select-form", function() {};
の削除だけでいけそうな気はします。
event.js
とcocoon.js
内のそれぞれの
// event.js
$(document).on("change", ".js-select-form", function(event) {
console.log(1)
})
// cocoon.js
$(document).on("change", ".js-select-form", function(event) {
console.log(2)
})
みたいに仕込めば、二重にセットされているか確かめられそうです。
@murata0705
コメントありがとうございます!下記の給餌の画像を出す処理に関しては、そもそも最初から(cocoon.js
のみに書いてある状態のときから)処理が動いてましたので、event.js
に入れておりません。
// 給餌の画像を出す処理
$(document).on("change", ".js-select-form", function() {
// 選択したoptionのvalueを取得
let val = $(this).val();
// 親要素から画像リストを取得
let $imageList = $(this).parent().parent().parent().find('ul li');
// 先頭に#を付けてvalueの値をidに変換
let selectFeedId = '#' + val;
// 一度すべてのブロックを非表示にする
$imageList.hide();
// 選択したブロックのみを表示
if (selectFeedId === '#') {
return
} else {
$(this).parent().parent().parent().find('ul').find(selectFeedId).show();
}
});
event.js
、cocoon.js
それぞれの名前が重複していることを通知する処理
の中にconsole.log
を仕込みました。
// 名前が重複していることを通知する処理----------------
$(document).on("change", ".js-select-form", function(event) {
console.log("event.js側の名前が重複していることを通知する処理");
// 名前が重複していることを通知する処理----------------
$(document).on("change", ".js-select-form", function(event) {
console.log("cocoon.js側の名前が重複していることを通知する処理");
以下がその後の動作になります。両方から処理が呼ばれていることが分かりました。
event.js
の名前が重複していることを通知する処理
をコメントアウトしてみました。
以下がその後の動作になります。
赤文字で「給餌が重複しています」と出なくなったので名前が重複していることを通知する処理
が正常に動作しなくなりました。中の処理でさらに原因があるのかと思います。
また、event.js
から名前が重複していることを通知する処理
を削除しても、+ボタンをクリックした際に.nested-field
が2つずつ増えています。
名前が重複していることを通知する処理
はセレクトボックスを増やす処理に関しては直接関係ないので、コメントアウトしても.nested-field
が2つずつ増えていることには関係ないのかなと思います。
link_to_add_association
を押すことで.nested-fields
が増える仕組みについてですが、cocoon
内部で用意されているものだと認識しています。
https://github.com/nathanvda/cocoon#examples
ですので、やはりevent.js
とapp/javascript/packs/application.js
両方にrequire("@nathanvda/cocoon")
を書いたことが関係しているのかなと思います。
ああ、手元で動かしてみたらおっしゃっている意味がわかりました。
<%= javascript_pack_tag 'calendar/event', 'data-turbolinks-track': 'reload' %>
ここで、event.js だけ application に読み込まずに pack_tag で読んでるので、cocoon のモジュールを参照できなそうですね。
なので、飼育記録ページでも javascript_pack_tag で js を呼ぶようにして、event.js と cocoon をそれぞれ import するか、リスナーを設定する #activity-logs-containers
みたいなタグを作って、カレンダーページではこのタグ内に ajax で受け取った HTML を append するとかですかねぇ。
あとは、カレンダー処理自体もcocoon.js
のようにグローバルに設定しちゃう方法もありますね。
@murata0705
コメントありがとうございます!
カレンダー画面の方と飼育記録画面の方それぞれでrequire("@nathanvda/cocoon")
を呼び出すやり方を体現してみたく思い、
app/javascript/packs/application.js
のrequire("@nathanvda/cocoon")
をコメントアウトし、中身がrequire("@nathanvda/cocoon")
のみのapp/javascript/packs/nathanvda_cocoon.js
というファイルを作成し、
app/views/logs/new.html.erb
のほうで<%= javascript_pack_tag 'nathanvda_cocoon', 'data-turbolinks-track': 'reload' %>
として呼び出す方法を試してみました。
以下コミットが具体的な変更箇所になります。
f458169
結果、カレンダーページの方では、セレクトフォームが二重になることも無く、コールバックも動いているので、大体期待通りになりました。以下動作になります。
しかし、飼育記録ページの方では、コールバックが効かなくなりました。なぜこのような結果になるのか分かりませんが、以下動作になります。
リスナーを設定する #activity-logs-containers みたいなタグを作って、カレンダーページではこのタグ内に ajax で受け取った HTML を append する
こちらのやり方は、具体的にどのようにするのか分かりませんでした。
カレンダー処理自体もcocoon.jsのようにグローバルに設定しちゃう方法
こちらはFullcalendarの実装方法としてズレてくるのと、他に影響が出そうなので今回試していません。 https://fullcalendar.io/docs/initialize-es6
<%= javascript_pack_tag 'nathanvda_cocoon', 'data-turbolinks-track': 'reload' %>
でやる場合、nathanvda_cocoon.js
内に、cocoon.js
の内容を移さないと動かないですね。
cocoon.js
内のコードから、require("@nathanvda/cocoon")
が参照できないと思います。
(webpacker の仕様よくわかっていないので、もしかしたらなにか方法があるかもしれませんが)
@murata0705
コメントありがとうございます!
下記コミットでnathanvda_cocoon.js
内に、import "../src/cocoon"
を追加する変更を加えました。
5044173
結果、飼育記録ページの方でもcocoonコールバックが動くようになり、期待通りの動きになりまりました。ありがとうございます!
カレンダーページの方は、application.js
に書いてあるimport "../src/cocoon"
を参照してcocoonコールバックが動いているのかと思い、
飼育記録画面の方も同じくapplication.js
に書いてあるimport "../src/cocoon"
を参照してcocoonコールバックが動くものかと思ったので、
なぜnathanvda_cocoon.js
内に、import "../src/cocoon"
を書く必要があるのか腑に落ちませんが。。
@shuhei-T HTML を見ると、
みたいな js があると思いますが、これはそれぞれ、webpack でビルドした js になります。(たぶん) 今 webpacker を使っていると思いますが、webpacker は webpack をラップしたもので、実際は webpack がビルドしています。
application-[hash].js をビルドするときに src/cocoon.js を読み込んできますが、ビルドするときにrequire("@nathanvda/cocoon")
していたので、依存関係は問題ありません。
nathanvda_cocoon-[hash]-.js のビルドはまた application-[hash].js のビルドと別でビルドするので、その際にrequire("@nathanvda/cocoon")
していないために依存しているライブラリがなく、うまくリスナーを設定できなかったのだと思います。(webpack と webpacker の仕様をちゃんと確認していないですが)
link_to_add_association
に関しては、ライブラリの中身を見てみないとわかりませんが、スコープの違いだと思います。
ちゃんと理解したい場合は webpack と webpacker の仕様を確認して、 cocoon の中身も見てみましょう。
@murata0705 コメントありがとうございます! webpackやwebpackerについての理解も深めようと思います!
長々と対応して頂き本当にありがとうございました^^
一応まとめておきます。
カレンダーページの方は、application.jsに書いてあるimport "../src/cocoon"を参照してcocoonコールバックが動いているのかと思い、
../src/cocoon
内のコールバック設定時に#active-log
のタグが存在しないのでコールバックを設定できず
飼育記録画面の方も同じくapplication.jsに書いてあるimport "../src/cocoon"を参照してcocoonコールバックが動くものかと思ったので、
#active-log
のタグが存在するので、コールバックを設定できる
require もしているので event-[hash].js をビルドするときに問題なくコールバックも設定できる。ただし、二度 require しているので、グローバルにアクセスできるような処理が二重にセットされてしまう。link_to_add_association
など。
cocoon.js を import して、application-[hash].js をビルドするときに、require していないので、cocoon.js のコールバックのカスタムイベントを参照できず、コールバックが設定できない。
質問内容
Fullcalendarのクリックイベント後のモーダル内で、cocoonのcallback関数内の処理が発火しません。 どうゆう処理かというと、下記の三点になります。
+
ボタンがdisabled
化し、それ以上増やせなくなる。-
ボタンで給餌セレクトボックスを削除できるが、押すと一旦アラートメッセージが出る実装に際して参考にした記事と、公式GitHubを載せておきました。
以下正常な動作のGIF動画になります。
こちらが今回問題のフルカレンダー内の動作になります。
アプリのURL https://reptileslog.com/
現状発生している問題・エラーメッセージ
エラーは特に起きませんが、
console.log()
を仕込ませて発火していないことは確認できています。どの処理までうまく動いているか
Fullcalendarではなく、
app/controllers/logs_controller.rb
でも同じ機能を使っているのですが、こちらは問題なく動いています。該当のソースコード
app/javascript/src/cocoon.js
エラーから考えられる原因
FullcalendarのJSと干渉しているのかなと推測しました。
試したこと
Fullcalendar内のクリックイベント後の中に書く必要があるのかと、試しに書いてみても、発火しません。 https://github.com/shuhei-T/reptiles_log/pull/130
app/javascript/packs/calendar/event.js
参考記事
gemcocoon公式 callback https://qiita.com/tanutanu/items/a9f435e33b2d4533b3a2