はじめに
DHH 兄貴が公開してくれた Hotwire 、Websocket でリアルタイム更新が反映できて便利だなと思いましたが、ちょっと触ってみたら、 get で画面遷移なしでHTMLを取得する時ってどうすればできるんだろう と疑問が出てきたので、 rails g scaffold の画面を SPA にしてみました。
Hotwire の基本については、公式のビデオと Hotwireでリアルタイムなチャットを作る を参考すればイメージできると思います。
結論
できたもの
https://github.com/blueplanet/hotwire-scaffold
- 入力フォームを index に直接に表示することで、SPA にできました
bootstrap を導入して入力フォームを modal でやりたかったんですが、HTML が取得できたものの、それを受けてから modal を表示するところがうまくできなかったので、諦めました-
turbo v7.0.0-beta.2 で異常系の status code の場合もレスポンスを描画するようになったので、
stimulusとturboを使って、成功したら modal をクローズ;エラーの場合そのままを実現できた
# controller にエラー時の `render :new` に `status: :unprocessable_entity` 追加
def create
@post = Post.new(post_params)
respond_to do |format|
if @post.save
format.html { redirect_to @post, notice: 'Post was successfully created.' }
format.json { render :show, status: :created, location: @post }
else
format.html { render :new, status: :unprocessable_entity }
format.json { render json: @post.errors, status: :unprocessable_entity }
end
end
end
# form に turbo:submit-end イベントに `modal#close` メソッドを紐づく
<%= form_with(model: post, data: { controller: "modal", action: 'turbo:submit-end->modal#close' }) do |form| %>
# modal の close アクションに `event.detail.formSubmission.result.success` で結果を判定できる
close(event) {
if (event.detail.formSubmission.result.success) {
$('#post-modal').modal('hide')
}
}
- SPA 対応のポイント
-
turbo_frame_tag 'post_form'を活用すれば、ほぼ既存の view を変更せずにできる -
link_toにdata: { turbo_frame: 'post_form' }を付けることで、リンク先のレスポンスから、上記のturbo_frame_tagと同じ HTML id (post_form) の要素を入れ替えてくれる - https://github.com/blueplanet/hotwire-scaffold/blob/master/app/views/posts/index.html.erb
-
感想
-
hotwire-rails は、 turbo-rails と stimulus-rails の組み合わせの設定をしてくれただけで、メインの仕事をしてくれているのは
turbo-rails -
turboは5種類の動きappend / prepend / replace / update / removeだけ定義されていて、それ以外の動きはstimulusで頑張ろう的な感じ - 更新系の処理をトリガーとして、view を更新する処理を broadcast すればよいですが、複数の処理が必要な場合、処理の順番をどう制限できるかは、まだ分かっていない
-
stimulusは、まだ良くわかってない
よかったところ
-
turbo_frame_tagとdata: { turbo_frame: 'post_form' }と組み合わせるだけで、既存の scaffold で生成した view を SPA にすることができた -
turbo_stream_fromとbroadcast*と組み合わせることで、修正系の結果をリアルタイムで反映できた
改善したいところ
- webpacker があると、
turbo_frame_tagの中にある submit ボタンが Form submit のレスポンスが 200 になっているにもかかわらず disabled のままになっている事象がある- よって、できたものの rails project は、
--skip-javascriptで初期化しました - 調べきれてなかったんですが、
rails/ujsとの整合性がよくない感じ
- よって、できたものの rails project は、
- webpacker をなく、
rails/ujsなくなり、form_with ... remote: true系が使えなくなってしまうので、不便 -
ujsがなくても、You can still use the data-confirm and data-disable-with.と ドキュメントに書かれています が、できたものでは、scaffold で生成した delete link は動きませんでした。
まとめ
-
turbo を使えば、js を書かなくて済むよ的な感じがありますが、turbo ならではのやり方は、慣れる必要がある -
ujsやwebpackerと併用できないため、既存プロジェクトへの導入はもうちょっと待つほうがよいかも