はじめに
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
と併用できないため、既存プロジェクトへの導入はもうちょっと待つほうがよいかも