Turbolinks5でPOSTするときはajax経由のほうが良いのかも

Turbolinks5になってから「フォームでバリデーションエラーになったあとその画面をリロードしたときの挙動がTurbolinksを利用していないときと異なる」という問題がある。

2年半くらい前からIssueが立っている

Turbolinks doesn't recognize form re-submission [POST] and navigates instead [GET]. · Issue #251 · turbolinks/turbolinks

POSTしたあとの画面をリロードしようとすると、通常は「フォームの内容を再送信しますか?」みたいな確認ダイアログが出る。しかしTurbolinks経由だと単にいまのURLをGETで取得しようとする*1。これは、Turbolinksがhistory apiを使って履歴を操作しているのが原因。history apiに履歴を足すときはGETとしてのURLしか足せないので、現状のTurbolinksの仕様では対応するのは無理ぽい(放置されている原因の一つはこれだと思う)

そもそもTurbolinks(というかBasecamp)的には、formは全部js経由にしてしまう方針なので上記の問題で困っていない、というのもありそう。Railsでform_withを使ったとき、デフォルトではremote: trueになる。

普通はajaxリクエストでリダイレクトはできないのだけど、turbolinks(turbolinks-rails)ではajaxでpostを受け付けたときのハックが書かれていて、redirect_toされたときはTurbolinks.visitを使うようにしてリダイレクトを実現している。なので正常系だけならremote: trueで問題ない。

バリデーションエラーになったときの対応が面倒で、これは独自に定義をしないといけない。DHHはSJR(Server generated Javascript Response)でやればいいと書いてる。ようするにcreate.js.erbみたいなやつ。

Provide form_with as a new alternative to form_for/form_tag · Issue #25197 · rails/rails

わかるんだけどバリデーションエラーを表示するためだけにそれをやるのってめんどいのですよね。画一的に処理できるgemをつくればいいのか。すでに誰か作っていそうだからそれでも良さそう。

追記

だれも作ってなさそうなので作った turbolinksとform_withを便利に使うためのgemを作った - おもしろwebサービス開発日記

そのあとに似たようなのが作られていたことに気づいた>< jorgemanrubia/turbolinks_render: Support for render with Turbolinks in Rails controllers

*1:railsであればindexアクションのURLになるはず