inuinu blog(開発用)

BOT @wagagun の開発ノウハウや、IT向け?の雑記ブログです。

【Ruby on Rails7】cocoonを導入(その2:バリデーション問題解決:簡易版)

この記事では、Ruby on Rails7のgemパッケージcocoonについて、導入・カスタマイズ行います。
今回はバリデーションエラー時に発生する問題の解決策なのですが、簡単に解決できる手法を考えていきます。

前回の続き。

inuinu-tech.hatenablog.jp

前回、導入まではうまくいきましたが、厄介な問題が発生しました。
解決までの手法を考えていきます。

前回のおさらい

動作確認を行うと、unprocessable_entityでJavascriptがうまく実行できない

cocoonについては、Rails7でも、ほとんどの処理はうまく実行されると思いますが、バリデーションエラー(422:unprocessable_entity)時に、なぜかJQueryの動作が停止してしまうようです。

原因の洗い出し

これはcocoon固有の問題ではない

他の(cocoonを使用しない)入力画面でも確認しましたが、unprocessable_entityの段階で、ユーザー定義のJavascriptの動作が、同様に無効になるようです。

ですので、これはcocoon固有の問題ではありません。
よくある「Turboあるある」なのかもしれませんね。

検索すると、海外のQ&Aサイトで「Turboの問題ではなく、サードパーティで起きた現象だ」旨の発言があったりするようですが、惑わされないでください。

これは不具合なのか?仕様なのか?

blog.willnet.in

によると…

これであとは頑張ってバリデーションエラー時の処理を置き換えていくだけ…と思いきやそんなことはありません><バリデーションエラー時にturbo:loadが発火しないという現象に遭遇しました。https://github.com/hotwired/turbo/issues/85 を見た限りこれは仕様とのことです…。

やはり、Turboの問題かなと思います。

一見すると不具合だろ?と思うかもしれませんが、JS系で画面の一部を追加したり、差し替えたりすると、イベントリスナが無効化されることがあると思うので、多分に似たようなことかと思います。
セキュリティの観点も含めて、これは仕様でしょうね。

ただ、Hotwire周辺で何らかの解決方法があるのでは?と思い、さらに調べてみたことろ、解決方法はありそうです。

簡易的な解決策:Stimulusを導入し、page:loadを起動させる

まずは、取り急ぎ、問題を回避していきます。

Stimulusからpage:loadを発火させられれば、うまくいく?

私が把握しているStimulusでの解決方法は2つありますが、この記事では簡易的な解決方法で実装していきます。

github.com

によると、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からの引用です。

github.com

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内部の処理が動いていれば成功です!

追加前、追加後、削除前、削除後が各々動作しているかを今のうちに確認してください。 console.log()で確認するだけでも十分でしょう。

とりあえず問題解決したかもしれないが…

上記の解決策は「現状のソース構成で、最低限の修正方法で解決できる」という意味ではOKなのかもしれませんが、多分に、本来のRails7が目指すものではない気がします。

Stimulusの中のロジックは、unprocessable_entityであろうがなかろうが、きちんと動作するということです。ということは…

続きはこちらを御覧ください。

inuinu-tech.hatenablog.jp