-
Notifications
You must be signed in to change notification settings - Fork 430
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Redirect to new page on successful form submission, rerender otherwise #138
Comments
At a guess, have a Stimulus controller listen to the turbo:submitEnd event and trigger a redirect on success (i.e. Turbo.visit()) by checking the Location response header. |
I like this idea. Haven't really thought into hooking into Turbo events for this. I'll look into it and see how it goes. |
That having been said, it might be good to build functionality into Turbo for this directly. |
Agreed: perhaps redirects should always assume "_top". |
You can use a |
@tleish how would that work if we want to actually redirect to another page (including change to history etc)? |
@danjac - what backend platform are you using? |
@tleish Django. However I think if I understood correctly the pattern would be something like:
|
That sounds like it could be viable. And I suppose using |
@danjac — this is correct. Also, if the @boardfish - I'm not sure what do you mean by "but there's still a dependence on that turbo-stream that you'd probably need to make known"? |
@boardfish you can check the request accept header server-side and return a stream/full page etc accordingly |
I think the main confusion here is about turbo_frame_tag use. Ideally, redirect should just replace the page, regardless of what turbo_frame_tags are on the page or where it came from. That's what redirect implies. Take me to a new page. For anything else, if you want specific section of the page replaced, using turbo_stream to replace that page section makes sense. Currently that's not what's happening. Any work arounds with stimulus or anything else are not ideal, because it breaks the general flow. Redirect should redirect. Or at least if it finds the matching frame_tags it should replace those, if there aren't any matching tags, it should replace the whole page. |
I am 100% for this. I use ASP.NET Core and there are no helpers for turbo-streams right now so I cannot use them. But with your approach I get magic of replacing part of page with scroll being restored correctly with regular rendering pipeline (I just put turbo-frame). I only miss redirect to completely new page. |
I did dive into source code and came with ugly hack. And looking at sources I realized it maybe harder than I thought to do it properly.
But this shows design decision of Turbo: each frame is independent. If fetch does not provide a new frame it is a fatal error situation. Without link from Frame to Drive to cause "partial visit" it is not possible get what @tdak and I want. And I am not sure if authors of Turbo are willing to make such design change. |
Adding link to Discussion topic for future reference. |
Another person having problems with redirect to another page from within a frame. |
To chime in, I'm also struggling with this a bit. Maybe the |
I've come back to this. @nwjlyons wrote a really neat solution elsewhere that I've adapted: /* turbo_form_submit_redirect_controller.js */
import { Controller } from "stimulus"
import * as Turbo from "@hotwired/turbo"
export default class extends Controller {
connect() {
this.element.addEventListener("turbo:submit-end", (event) => {
this.next(event)
})
}
next(event) {
if (event.detail.success) {
Turbo.visit(event.detail.fetchResponse.response.url)
}
}
} |
Their original solution is here. |
I think I'll close the issue because the above addresses my use case just fine, and a change specific to Turbo for this would most likely create edge cases. |
Thanks to everyone in the thread for contributing solutions! |
After a lot of searching, I'm glad to see that there is a solution, but this still feels icky to me. Two things that annoy me:
Luckily I can encapsulate this once in a form_controller.js, but it still feels like I'm doing something wrong with Turbo by doing this. In the meantime it would be nice to see this as an example in the docs at https://turbo.hotwire.dev/handbook/drive#redirecting-after-a-form-submission |
I can arrange that! |
This works although seems to just be a workaround to something that feels like a bug in Turbo. Two drawbacks to this approach:
|
Those are some good points. I'm happy to reopen this on the grounds of your and @DaveSanders' comments, and I'd be interested to hear from the core maintainers on whether this should change and why. After all, I suppose the verdict's up to them. |
so i ended up down here and
can’t remember having that much friction in the rails stack before… |
@tomu123 I can't see anything obviously wrong, I'd double check the Network tab of the browser tools to make sure it is returning what you expect. Though since you asked back in August I hope you've solved it by now... @glaszig what's hacky about this solution #138 (comment)? It uses Turbo in the way it is intended to be used with zero custom JavaScript or changes on the server side. I've done a write-up on building forms with Turbo here, it might help explain what's going on. |
|
For what it's worth, @glaszig, I'm with you — there are a few classic workflows that ought to fit really well with using frames and only frames: multi-step sign-in workflows and multi-step forms ('wizards') both are great candidates. And I too want to do that using Turbo Frames alone; not streams, not Stimulus controllers (though I do love them 😛). That said, this is actually somewhat of an old thread. I've been watching this space and reading along with many of the PRs in the "breaking out of the frame" concept for the last couple years. I'm going to point you toward this PR instead: #445. I know it's long but give that whole thing a good solid read. They're iterating on it right now for 7.3, but if you consider the initial premise of this issue,
Then add the idea of "well if it's successful then just redirect them to a page that doesn't have a corresponding frame", #445 can complete the concept:
|
thanks for the pointer to that rabbit hole. looks like that'll resolve this issue. also a blessing that #677 was rejected with
i'm somewhat relieved. |
I agree 😊 Anyway, hopefully this issue can rest a while now 😆 |
A side note for future readers: if your form is within a frame, and redirect to a new page without a matching frame-id, the redirection will be followed and the response will be rendered without its default layout (the one in
|
I have read through this thread and searched around somewhat but I am still confused. I'm using turbo stream (in a rails app) for a classic case: form submission to re-render only the form on validation errors. On success it should redirect to a completely new page. It doesn't perform the redirect, even though I'm using turbo stream and have used Any ideas? From what I have read here and elsewhere, the redirect should function normally since it is a turbo stream and not a frame. Even status_code: 303, status: :see_other should not be needed. In my opinion turbo should not mess with redirects, regardless of if its a stream or frame. There should be no need to use stimulus to perform the redirect. Any ideas? |
My issue was that, since I am using HAML, I was redirecting to a page who's view was simply Still kinda of a bug or not? Why does turbo rely on the filename extension for determining what format to use for a |
If I understand the problem correctly, this applies to the Rails + HAML backend, not the Turbo frontend. Rails and HAML require the extension of ".html.haml" because HAML is a markup language that generates HTML code, and the ".html" extension indicates that the file is an HTML file. By using the ".html.haml" extension, Rails knows to use HAML to generate the HTML code for the view. The ".haml" extension alone could also be used to indicate that the file contains HAML markup, but then Rails would not know which format to use to generate the final output (e.g. HAML's documentation also states:
|
@tleish Yes exactly. Totally understand your points and the documentation. I am familiar with the extensions and their usage for defining what format a view should be used for. In a nutshell: a controller action responding with It does seem more like a bug than intended functionality. The lack of |
@leewaa - thanks for clarifying. I am familiar with HAML, but never used it. When not using
|
Yep, There are two redirects though since I have constraints on the routes which depend on the role of the user.
Through out the chain (both responses) the response header is |
@leewaa - Happened to come across the following threads on turbo-rails that might help? |
Thanks @tleish |
After reading this whole thread, I think a lot of people are unsatisfied with the good solutions offered by @TastyPi and others because they are in a situation where "not using a frame" isn't an option (lots of things being discussed in here 😅) For instance, imagine you're loading a modal into a turbo frame. You're now in a situation where you likely operating under the constraint of having a form inside a frame -- this is exactly where I found myself. I ultimately landed on setting the turbo frame target of the form to Alternatively, I could have optimized in the other direction by removing the I landed on the former because I thought that updating the DOM appropriately was more work and more error prone considering the types of things I was rendering were ordered, whereas simply re-rendering the form with errors seems like it won't change much over time. All that said, it does seem like something I "shouldn't have to do". I think the work being done in this issue is the ideal solution, but it seems to have stalled. Going to comment over there to try and get more info. |
The general pattern we've settled on is:
I think the main source of confusion is not understanding what the intended purpose of frames and streams are. Frames are intended to be a section of the UI that works like a nested app, kinda like an iframe, including having separate navigation. They're not intended to be "everything that might need to be changed in the page needs to be in frame". Streams are intended to be actions to take on the page to change it in some way. They were originally for live updates hence "stream", but they also work for one-off requests. In fact, there's a proposal to rename them to make that intention clearer #580. |
Here's a version of @TastyPi solution, but removes the need for a Stimulus controller: <%#
Define a custom redirect action
<turbo-stream action="redirect" location="http://localhost:3000/"></turbo-stream>
%>
<%= turbo_stream_action_tag("redirect", location: root_url) %> # application.js
addEventListener("turbo:before-stream-render", ((event) => {
const fallbackToDefaultActions = event.detail.render
event.detail.render = function (streamElement) {
if (streamElement.action == "redirect") {
window.location.href = streamElement.getAttribute('location')
} else {
fallbackToDefaultActions(streamElement)
}
}
})) |
This is still confusing to me. I am in the same boat as @archonic described in their comment.
Turbo just ignores this. Is there really no way to make this work out of the box? Most of the replies here talk about frames and streams - but there won't be any HTML in a 303 redirect response. |
Is your view file I'd suggest trying from scratch somewhere else, as there's no reason not to work. The issue here was when a turbo-frame was involved. Even with turbo-frame, a redirect now works if it's missign the turbo frame in the new response. See #138 (comment) and after. |
It's not a file, it's a PHP application that generated the HTML. Or what exactly do you mean?
But there are no streams or frames involved in my case. |
In this case, it's likely to be something to do with the content type that's returned from the PHP application. Maybe it needs to be
That's why this issue is not really applicable to your use case. Came across this - https://github.com/hotwired-laravel/turbo-laravel - which should be of help. Maybe post an issue there? |
I see the issue now. The redirect (eventually) responds with a
|
Yes - I think #401 is the exact use case. |
@fritzmg I'm wondering if you observed the same thing as I do here #1018 My use case is exactly like yours: I have a POST form that isn't wrapped by a turbo-frame. As the form is being submitted I expect a user to be redirected to a new location. The new location doesn't have a I've managed to mitigate this issue temporarily by using a custom turbo action response like this: |
Ended up here searching for why POST form that isn't wrapped by a turbo-frame doesn't follow redirect. What helped is adding
to this:
|
I'm trying to figure out how to do this. Right now, when I submit a form from within a
turbo_frame_tag
, I'm reliant on the response having a matchingturbo_frame_tag
to replace the content of the tag. So regardless of whether it's a successful or failed form submission, the user doesn't leave the current page right now.On a successful submission, I'd like to redirect the user to a new page. There are a couple of angles I've considered:
target
attribute to_top
, which isn't optimal because it'd replace the view in full in either case.turbo_frame_tag
, which feels clunky and also might not account for wider layout changes.Is there a known way to do this?
The text was updated successfully, but these errors were encountered: