Open kiddrew opened 3 years ago
Thank you for reporting this.
Does your application run RailsUJS? Are your forms created with data-remote="true"?
I'm not using data-remote. I did have UJS enabled, but I disabled it to test and that didn't help either.
same issue here
I had to explicitly use render ..., status: :unprocessible_entity
to make the page show with errors and redirect ..., status: :see_other
to make the page redirect.
Perhaps that helps?
@SleeplessByte That is correct. It's not well documented yet, but Rails is switching to returning a 422 on fail: https://github.com/rails/rails/pull/41026
Devise will be ready for this change soon: https://github.com/heartcombo/devise/pull/5340
Really excited to start playing with these new toys 🎉
@seanpdoyle there is a small issue with redirects.
When using Rails UJS, and setting the form as local: false, data-turbo="false"
redirects are not working.
respond_to do |format|
format.html { redirect ...} <---- This will not redirect. Request is a js.erb, worked on turbolinks
end
@dixpac If RailsUJS is present and local: false
is passed to form_with
, the resulting form will have [data-remote="true"]
, and will be intercepted by RailsUJS ahead of Turbo, and will be submitted as a JS request instead of an HTML request.
Am I understanding your question properly?
I tried modifying the response to use :unprocessable_entity
and :see_other
and it didn't fix my issue. The submit and subsequent get request both work as expected but the form render or redirect doesn't happen.
@seanpdoyle the problem is when migrating larger apps there is a lot of complex rails UJS code, which is impossible to migrate on turbo_streams
because UJS is more powerful DOM manipulations wise.
So to make it work some forms need to be remote with data-turbo=false
. Disabling the turbo enables old UJS code to continue working. But, trurbo-rails gem is missing this piece from the turbolinks, hence redirects are not working from the JS request.
This is maybe a different issue from the one @kiddrew posted.
@dixpac thsnks for clarifying. Let's open a different issue then.
As part of the new issue, could you please help us understand why we'd want to set local: true
and [data-turbo="false"]
, instead of [data-turbo="true"]
as part of a gradual migration?
@seanpdoyle sure, I will open issue or a PR but I need more time to inspect what is happening 😄
As part of the new issue, could you please help us understand why we'd want to set local: true and [data-turbo="false"], instead of [data-turbo="true"] as part of a gradual migration?
It is local: false (remote: true) and data-turbo=false
@dixpac right, is data-turbo="false"
preventing Turbo support, whereas data-turbo="true"
(or omitting it entirely) might opt them back in?
Yes.
Basically I need to disable turbo on the form and let Rails UJS handle the form. That works like a charm, rails UJS handles properly until I redirect inside the controller, that redirect is "broken" because turbo-rails doesn't have that "magic" concern that was included in turbolinks (trubolinks gem is removed).
I will try to execute Turbo.visit
manually inside the .js.erb
I think that could work.
This works on turblinks and doesn't work with turbo(remote: true, data-turbo=false
):
# Form is remote: true
def create
respond_to do |format|
if person.save
format.html { redirect .... } # redirect
else
format.js # Do some DOM manipulations with js
end
end
end
Thanks @dixpac. Could you open a separate issue?
@seanpdoyle same issue here. Turbo always handles the form even the form has data-turbo="false"
format.html ignored
if @post.save
format.turbo_stream
format.html { redirect_to posts_path, notice: Post was successfully created.'}
format.json { render :show, status: :created, location: @post}
end
@Petercopter --- I see https://github.com/heartcombo/devise/pull/5340 seems to fix the problem
I have steps to reproduce for Rails 6.1.3 and Devise 4.7.3 here:
https://stackoverflow.com/questions/66615478/turbo-rails-with-devise-does-not-redirect-consistently-rails-6-1-3-devise-4-7-3 My reproduction app against Devise 4.7.3 can be found here: https://github.com/jasonfb/TR002
I'd love to be able to upgrade my Rails 6.1 app for Turbo-Rails today, but my upgrade is riddled with these Devise bugs-- mostly redirects that don't redirect in the browser but the action has happened on the backend.
I see this pull appears to fix everything (🎉 !), true? false? truthy? falsy?
in master soon? Should I wait a bit? I guess if it were in master I could point my gem to the master branch of devise? Any tips appreciated, even just a monkey patch to get me going until the fix is released.
I got all the other parts of my TR upgrade working so as you can imagine I am eager to fix this last bit. Would be happy to help if there's any way I can.
@jasonfb thank you for creating https://github.com/jasonfb/TR001 to help reproduce the issue. Would it be possible for you to alter the git history so that the changes that are tied directly to reproducing the bug behavior are their own commit? It's very difficult to read through a commit that has a majority of its changes generated through Devise installation tasks.
After quickly scanning through, I have some high level questions:
1) Are the form_for
calls generating HTML with [data-remote="true"]
?
2) Is the format.turbo_stream { redirect_to "/nowhere" }
call intentionally redirecting, or is it to demonstrate something else? Typically, redirect_to
is better called from within the format.html { }
block, or in a controller response that omits the format
blocks entirely.
3) Have you tried replacing the link_to "Log out"
with a button_to
instead?
@seanpdoyle -- the whole app is to reproduce the issue. Yes I did it quickly and just made 1 commit. it's basically not very much more than 1) the turbo-rails install, a 2) the devise install, and 3) a small bit of customization
Oh bootstrap is in there for no reason -- I can take that out if you want.
Are the form_for calls generating HTML with [data-remote="true"]?
it would appear not.
Is the format.turbo_stream { redirect_to "/nowhere" } call intentionally redirecting, or is it to demonstrate something else?
So sorry… let me re-do, that is irrelevant now. (it was me debugging in some other way). The bug reproduces on devise directly, on the devise controller (which is not this one-- this HelloController#anything action is irrelevant, sorry).
FYI..... I think may be a devise issue related to devise because it seems like they said over there this was fixed in a PR. I will try the other things you suggest.
I removed the unneeded code, the anything
action was unrelated to this --- just ignore it.
https://github.com/jasonfb/TR001/commit/f141ceaf2a60af041f9f591935647f02bfa65f45
Have you tried replacing the link_to "Log out" with a button_to instead?
You are a genius (although, this is still a bug). First: the devise login partials come from the devise gem itself, so without overriding it, I cannot change the links inside of the devise partials.
Focusing on Symptom # 2 only (the Logout)--- your suggestion does indeed instantly restore the functionality.
which is interesting, and suggests to me there is a bug related to links on the Turbo side. That is, since simply changing from link_to
to button_to
fixed the problem with the same Devise backend controller, shouldn't a Turbo behave the same way with links as it does with buttons (seems like turbo's bug?)
As far as the rest of this, it seems like it should be addressed in devise (for one thing, if mods are to be made to the login logout form). it looks like they were discussing root cause of these issues back in January here https://github.com/heartcombo/devise/pull/5325
(TBH I did not debug that part of the stack.-- so I can only speak to the part of the stack that I debugged of course but I see the thing appears to all be related to 422 status codes in Rails responses or something around this area. )
it makes sense in the sense that links are supposed to be non-destructive and buttons should do destructive things but UJS has spoiled us with method: :delete on our links.
So...... at the very least maybe Turbo could give a console error ?
Here you go ... new example app here:
https://github.com/jasonfb/TR002
this is now without boostrap and without haml, so just enough to reproduce the Turbo-rails + Devise issue
I have cross-poseted this to https://github.com/heartcombo/devise/issues/5358
you can reproduce fully on your own machine using these steps https://gist.github.com/jasonfb/eb9cf8e90514dad1af0b98e01e9bce3d
Hello everyone,
I opened #152 earlier, but I think this issue is the same.
For me it is completely not correct that Turbo prevents plain HTML forms from working with this gem:
<form action="/url" method="POST">...</form>
It is old good HTML 1.0 and it gets broken unless you wrap it into<turbo-frame>
and/or make explicit turbo_stream
response!
For me it doesn't matter what status code Rails return (though correct codes are a good practice), it is just broken HTML standard. Thus Turbo doesn't do progressive enhancement, it forces you into yet another ecosystem, like React, Angular and other vendor-lock tools.
This is not correct. Forms must work the same way as links. We don't need to opt-out from turbo on every link, so we shouldn't do it for every form. Links are smart enough to use whole page as response when not wrapped in turbo_frame
and so should forms.
I suggest the following logic:
We will get everything working for all existing and all non-Rails code out of the box. It will respect old good HTML basics. And it will be progressive enhancement, not a mandatory Turbo lock-in. It will be effortless magic!
I think it is a more high-level problem of current implementation which will solve original redirect issue, too. It looks more server-side.
Sorry if I'm missing some big idea or technical restriction, but this part is confusing.
To add to what @dmitry-rychkov said, this would also improve the Turbolinks-to-Turbo upgrade path quite a lot.
Most of it was straightforward, but having form submissions suddenly doing nothing (even without remote: true
and even with status: 303
) was quite a head-scratcher. The docs also seem to say that you can progressively use just Turbo Drive and later opt into more fanciness with Frames/Streams, but reality currently seems to be that you can't use just Turbo Drive on its own for form submissions.
@jasonfb, my observation is that when server response doesn’t contain any turbo-frame
, then it responds with empty response even if you generate full HTML page.
When there is a turbo-frame
in response HTML then whole HTML page is returned and turbo works fine.
I couldn’t find a line in Rails code which prevents normal HTML response when there is no frame inside.
Still don’t see any reason for Turbo forms not to allow same logic as links (without any UJS)
I ended up adding config.action_view.form_with_generates_remote_forms = false
to my application.rb
which prevented me needing to touch all forms by default
Here is a quick fix for those who came there:
config.action_view.form_with_generates_remote_forms = true
at your initializer e.g. config/initializers/new_framework_defaults_6_1.rb
Create a concern at app/controllers/concerns/turbo/redirection.rb
module Turbo
module Redirection
extend ActiveSupport::Concern
def redirect_to(url = {}, options = {})
turbo = options.delete(:turbo)
super.tap do
visit_location_with_turbo(location, turbo) if turbo != false && request.xhr? && !request.get?
end
end
private
def visit_location_with_turbo(location, action)
visit_options = {
action: action.to_s == 'advance' ? action : 'replace'
}
script = []
script << 'Turbo.clearCache()'
script << "Turbo.visit(#{location.to_json}, #{visit_options.to_json})"
self.status = 200
self.response_body = script.join("\n")
response.content_type = 'text/javascript'
response.headers['X-Xhr-Redirect'] = location
end
end
end
It's just a copy of the redirection handler from turbolinks-rails
gem
import { Turbo } from "@hotwired/turbo-rails"
...
window.Turbo = Turbo
class ApplicationController < ActionController::Base
include Turbo::Redirection
...
That's it. Your legacy code works.
@givemetraffic Thanks for your reply. I can find the similar code from Turbolink。 It works for me in my activeadmin's legacy code.
For what it's worth, the solution in issue 138 in hotwired/turbo worked for me - simply setting data-turbo-frame = "_top"
in the form. Full credit goes to @inopinatus.
That enables your form to accept responses which don't contain a turbo-frame
and properly render the redirect.
Is there a solution for this yet?
I added Turbo to my Rails app and no form is working. I can confirm they're NOT remote (I even kicked rails-ujs, but it didn't help). As others I can confirm that redirects are being triggered and sent back to the browser, but the page in the browser remains frozen. Tricks like local=true
or data-turbo-frame='_top'
didn't do anything.
Here's the step we did for Basecamp 3 to arrive at coexistence for an app built for Turbolinks/UJS: https://world.hey.com/dhh/bringing-hotwire-to-basecamp-91a442d6
Thanks. Followed your instructions at https://github.com/hotwired/turbo-rails/blob/main/UPGRADING.md and tried so many ways to make it work - it still won't render the redirects :(. Only solution for me is to opt out of Turbo for all my forms using data-turbo='false'
. Hoping for a fix in the future, because I like the lazy loading and the general idea.
I just tried again to upgrade from Turbolinks to Turbo using the concern method that @givemetraffic posted, and all my forms are still broken. Rails version is 6.1.4. Turbo-rails is 0.5.12. Manually adding data-turbo: false
to each form works.
Edit: I noticed that button_to and simple_form_for don't use form_with, so the action_view config line doesn't make those remote automatically. I confirmed that forms built with form_with work as expected. Every form I have is generated using simple_form or button_to, so manually updating each appears to be my only option.
I'm running into issues with this too. We have a download button that uses Rails UJS and submits a POST request to the action which redirects to an external URL for download using status: :see_other
that I no longer can figure out how to get working.
It's a bit sad that the previous API is broken. This will be a huge stone on the road of migration for community.
@jclusso if you don't have a responds ... do |format|
block, then Rails will respond with a "turbo stream" content-type which won't work. You can solve this by either adding a block like that, or explicitly setting content_type
on the redirect.
Not being able to redirect the user to a new URL after form submission is making it difficult to adopt Turbo. Would it be reasonable to redirect the URL (Turbo.visit
) if we either included, or even omitted, a specific status code? For example, if we redirect with a :status
of :see_other
, then Turbo will handle redirects as it does now, but if we used :found
(or no status code) the browser would be redirected entirely.
IMO it it's absolutely a mistake to default to a turbo response but then ignoring :found
. A server should not responds with a turbo response of :found
if it knows that turbo won't be able to render it (it won't).
So instead, having rails respond with text/html
(if :found
) by default, would solve all these issues.
I got rid of Rails UJS since I was told it would conflict with Turbo and wrapped the form in a turbo_frame_tag
. Displaying form errors works but the browser following the redirect isn't. The request format is turbostream and the response is in a format.html
block. Like everyone else says, the browser is left hanging on the old page with the submitted form.
The really confusing thing to me is that the server renders the redirected page, which means the browser made the request for the redirect but didn't render it. I setup a bunch of logging on all the Turbo events and found that there's 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 doesn'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
I wrote this SO question and got some insight. Changing format.html { redirect_to whatever_path }
to format.html { redirect_to whatever_path(format: :html) }
does actually change the behaviour despite the common format block. It went from hanging on the previous page with the submitted form to replacing the contents of the turbo_frame
with nothing and the console complained about: Response has no matching <turbo-frame id="new_label"> element
. I guess it's passing the format along as turbostream to the redirect despite entering the format.html block. The fallback to handling turbostream requests with html responses seems to put us in a weird place when upgrading.
The handbook says:
After a stateful request from a form submission, Turbo Drive expects the server to return an HTTP 303 redirect response, which it will then follow and use to navigate and update the page without reloading.
That just doesn't seem to be happening. It's following the redirect, making the request with the turbostream content_type, then not rendering the result.
Forcing the response to be considered HTML is a quick fix for this issue, but it results in some messy code. I'm having to do things like this:
result = Command::Record::Create.new.call
if result.failure?
respond_to do |format|
format.html do
render View::Record::New.new(record: result.failure), status: :unprocessable_entity
end
end
return
end
redirect_to comments_url(**comment_to_params(result.value!), format: :html), status: :see_other
After updating to Turbo I'm seeing this as well -- form a successfully submitting, but not redirecting -- this is when: as JS
is a part of the Started POST
-- is there a simple solution here? -- everything I'm seeing in this thread seems quite complex. -- if not, what's the way to handle a normal form?
(Almost) everything in this thread works.
@jakemumu After fighting the issue for a while I just disabled Turbo on all forms that have a potential redirect response. This PR fires an event (turbo:frame-missing) and makes it so Turbo.visit
can accept response HTML, meaning you won't have to request a redirect URL twice.
(Almost) everything in this thread works.
Some of the things in this thread may work circumstantially. If you have a form in a turbo-frame, forcing the format to be HTML changes the behaviour but does not force it to render the redirect. Changing the status to 303 doesn't change anything. Using target _top may work but destroys the point of putting the form in a turbo-frame, which handles rendering form errors really well.
@archonic you've changed more things such as dropping Rails UJS and using a turbo frame tag, whereas we haven't. Ensuring you return HTML and responding with a 303 (or 307/308 -> 303 chain) work together with turbo. Using data-turbo="false" also works. When you still have UJS, making sure the forms aren't trying to submit via UJS and turbo is also mandatory. All that advice came from this thread.
I still think bad decisions were made regarding how turbo works, but that doesn't change that the advice does work.
as a workaround I have disabled turbo_stream
at server
created config/initializers/turbo.rb
with
Rails.application.config.after_initialize do
Mime::Type.unregister(:turbo_stream)
end
Does that stop you from manually sending a turbo stream response?
A bit ridiculous that you can create forms now but can't redirect after submission. Is there a way to get rid of this JS crap completely and do simple html redirects? I'm stuck now right now. I've removed turbo-rails
gem from Gemfile and the forms still don't redirect. What am I doing wrong?
If you disable turbo, it will be summited in the typical fashion. You can disable turbo on the form instead of removing it completely.
https://turbo.hotwired.dev/handbook/drive#disabling-turbo-drive-on-specific-links-or-forms
If you disable turbo, it will be summited in the typical fashion. You can disable turbo on the form instead of removing it completely. https://turbo.hotwired.dev/handbook/drive#disabling-turbo-drive-on-specific-links-or-forms
Thank you. That worked!
more than a year later and redirection still doesn't work with form when using turbo by default in rails 7, anyone got a solution?
I just installed Turbo in an existing Rails 6 app and all my existing forms are broken. They submit as expected but the redirect after doesn't happen. I'm able to interact with Turbo as expected - ie, I have a frame loading correctly, so it appears I loaded Turbo correctly in Webpack.
And with just a very simple form (using slim and simple_form):
My controller performs the redirect (using responders):
The comment gets created and the request for the redirect URL happens but the redirect does not. I have to refresh to see the changes.