I'm attempting to upgrade to Turbo from Turbolinks and I've found that the client is not rendering redirects for form submissions.
Versions:
I've ignored the incompatibility between Turbo and Devise for now - just trying to get regular forms working without having to disable Turbo on them.
Here's an example action:
def update
authorize @label
@label.update(label_params)
if @label.save
redirect_to document_labels_path(document_id: @document.id)
else
render :new, status: :unprocessable_entity
end
end
Here's a rendered form:
<form class="simple_form new_label" id="label_form" novalidate="novalidate" action="/documents/72/labels" accept-charset="UTF-8" method="post">
...
</form>
When submitting a valid form, the server will say Processing by LabelsController#create as TURBO_STREAM
and correctly serve a 302. It will then serve the 200 for the redirect location. The browser however is left just looking at the submitted form. Changing the redirect status to 303 doesn't change anything.
I added a console.log for every Turbo event:
document.addEventListener("turbo:load", function () {
console.log('TURBO:LOAD')
})
document.addEventListener("turbo:click", function () {
console.log('TURBO:CLICK')
})
document.addEventListener("turbo:before-visit", function () {
console.log('TURBO:BEFORE-VISIT')
})
document.addEventListener("turbo:visit", function () {
console.log('TURBO:VISIT')
})
document.addEventListener("turbo:submit-start", function () {
console.log('TURBO:SUBMIT-START')
})
document.addEventListener("turbo:before-fetch-request", function () {
console.log('TURBO:BEFORE-FETCH-REQUEST')
})
document.addEventListener("turbo:before-fetch-response", function () {
console.log('TURBO:BEFORE-FETCH-RESPONSE')
})
document.addEventListener("turbo:submit-end", function (event) {
console.log('TURBO:SUBMIT-END')
// event.detail
})
document.addEventListener("turbo:before-cache", function () {
console.log('TURBO:BEFORE-CACHE')
})
document.addEventListener("turbo:before-stream-render", function () {
console.log('TURBO:BEFORE-STREAM-RENDER')
})
document.addEventListener("turbo:render", function () {
console.log('TURBO:RENDER')
})
This is what the output is for a successful form submission:
TURBO:BEFORE-FETCH-REQUEST
TURBO:SUBMIT-START
TURBO:BEFORE-FETCH-RESPONSE
TURBO:SUBMIT-END
There is no render event. Investigating event.detail.fetchResponse.response
for turbo:submit-end
it seems to be perfectly aware that the client should redirect, it just didn't.
Response {type: "basic", url: "http://lvh.me:3000/documents/72/labels", redirected: true, status: 200, ok: true, …}
body: (...)
bodyUsed: true
headers: Headers {}
ok: true
redirected: true
status: 200
statusText: "OK"
type: "basic"
url: "http://lvh.me:3000/documents/72/labels"
__proto__: Response
Update: It is actually performing the redirect and the server is generating the response. The issue is that the client is not rendering the redirect response.
Jeff's answer is correct but I wanted to share the specific fix for the issue I was having.
If you use HAML or Slim, I've seen it on more than one codebase where developers rename all template files .haml
instead of .html.haml
(same for Slim). It's never bitten me before using Turbo, but without .html
in the filename, part of Rails won't know what format to serve a response in, so it defaults to the request format.
Turbo makes a turbostream request when submitting a form, but if the response is a redirect, it expects it to be text/html
in order to render it. If it receives a turbostream response to a redirect request, Turbo just sits there doing nothing with no console errors or warnings (terrible default behavior IMO).
So if your templates do not include .html
, just add it back and Turbo will render redirects. You may still need status: :see_other
.
More information: