前回の続き。
前回、導入まではうまくいきましたが、厄介な問題が発生しました。
解決までの手法を考えていきます。
前回のおさらい
動作確認を行うと、unprocessable_entityでJavascriptがうまく実行できない
cocoonについては、Rails7でも、ほとんどの処理はうまく実行されると思いますが、バリデーションエラー(422:unprocessable_entity)時に、なぜかJQueryの動作が停止してしまうようです。
原因の洗い出し
これはcocoon固有の問題ではない
他の(cocoonを使用しない)入力画面でも確認しましたが、unprocessable_entityの段階で、ユーザー定義のJavascriptの動作が、同様に無効になるようです。
ですので、これはcocoon固有の問題ではありません。
よくある「Turboあるある」なのかもしれませんね。
これは不具合なのか?仕様なのか?
によると…
これであとは頑張ってバリデーションエラー時の処理を置き換えていくだけ…と思いきやそんなことはありません><バリデーションエラー時にturbo:loadが発火しないという現象に遭遇しました。https://github.com/hotwired/turbo/issues/85 を見た限りこれは仕様とのことです…。
やはり、Turboの問題かなと思います。
一見すると不具合だろ?と思うかもしれませんが、JS系で画面の一部を追加したり、差し替えたりすると、イベントリスナが無効化されることがあると思うので、多分に似たようなことかと思います。
セキュリティの観点も含めて、これは仕様でしょうね。
ただ、Hotwire周辺で何らかの解決方法があるのでは?と思い、さらに調べてみたことろ、解決方法はありそうです。
簡易的な解決策:Stimulusを導入し、page:loadを起動させる
まずは、取り急ぎ、問題を回避していきます。
Stimulusからpage:loadを発火させられれば、うまくいく?
私が把握しているStimulusでの解決方法は2つありますが、この記事では簡易的な解決方法で実装していきます。
によると、turboを含むhotwireの中のひとつのサービスであるStimulusから、page:load
を発火させてあげれば、前回のapp/javascript/application.js
の例でpage:load
の記載がありますので、ロジックが復活するかもしれません。
●app/javascript/application.js
// app/javascript/application.js $(document).on('ready page:load turbolinks:load turbo:load', function() { (以下略)
とりあえず、試してみましょう。
Stimulusのインストール
Gamfileには、デフォルトで…
gem "stimulus-rails"
がすでにあるので、コマンドラインから、
bundle exec rails stimulus:install
を実行すれば、Stimulusのインストールが完了します。
念の為に、
●config/importmap.rb
# config/importmap.rbの抜粋 pin "@hotwired/stimulus", to: "stimulus.min.js", preload: true pin "@hotwired/stimulus-loading", to: "stimulus-loading.js", preload: true pin_all_from "app/javascript/controllers", under: "controllers"
と、
●app/javascript/application.js
// app/javascript/application.jsの抜粋 import "@hotwired/stimulus" import "@hotwired/stimulus-loading" import './controllers'
を確認します。
とりあえず動作確認:helloをログに出力する
動作確認はしておきましょう。
サンプルとしてインストールされているapp/javascript/controllers/hello_controller.js
を書き換えます。
import { Controller } from "@hotwired/stimulus" export default class extends Controller { connect() { console.log("Hello World!"); } }
app/javascript/controllers/index.js
に追記します。
// app/javascript/controllers/index.js import HelloController from "controllers/hello_controller" application.register("hello", HelloController)
何らかのビューファイルの以下を貼り付けます。
.div{data: {controller: "hello"}}
F5でリロードし、F12のコンソールに「Hello World!」が表示されていれば、動作確認は完了します。
page:loadを実装する
サンプルの動作確認が完了したら、次に、page:loadを実装しましょう。
先程のhello_controller.js
をコピペして、新たなファイルを作成します。
仮にccreload_controller.js
としましょう。
●app/javascript/controllers/ccreload_controller.js
import { Controller } from "@hotwired/stimulus" import jquery from "jquery" export default class extends Controller { connect() { const event = new Event('page:load'); document.dispatchEvent(event); } }
7〜8行目は、以下のURLからの引用です。
app/javascript/controllers/index.js
に追記します。
// app/javascript/controllers/index.js //import HelloController from "controllers/hello_controller" //application.register("hello", HelloController) import CcReloadController from "controllers/ccreload_controller" application.register("ccreload", CcReloadController)
先程のhelloはコメントにしておきましょう。
app/views/何らかのページ.html.haml
のdivタグもコメントにするか、削除してください。
さらに、app/views/tasks/_form.html.haml
に追記します(最後の行で大丈夫です)。
.div{data: {controller: "ccreload"}}
動作確認
サーバーを起動し、動作確認します。
unprocessable_entityでバリデーションエラーが表示されている場合でも、JQuery内部の処理が動いていれば成功です!
とりあえず問題解決したかもしれないが…
上記の解決策は「現状のソース構成で、最低限の修正方法で解決できる」という意味ではOKなのかもしれませんが、多分に、本来のRails7が目指すものではない気がします。
Stimulusの中のロジックは、unprocessable_entityであろうがなかろうが、きちんと動作するということです。ということは…
続きはこちらを御覧ください。