-
Notifications
You must be signed in to change notification settings - Fork 436
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
Optionally update URLs on Frame navigation and stream responses #167
Conversation
@seanpdoyle Any thoughts on this PR? I know you and the team are all busy and I have no expectation that this would be merged in as is without discussion, but I'm curious to hear what you think.
I'm open to any changes you might want to make, both technically and as a feature, to get this merged in (if you think it's valuable, of course). Thanks for your hard work on this library 🙏 |
As I understand it: Yes, this is something we've discussed and are intent on supporting in some form.
We're still not sure what the surface area of this "API" should be, and what is the best way for consumers to interact with it. I'm not sure whether or not there will ever be support for changing the URL with There are also some additional considerations that somewhat straddle the framework-application divide that still need to be hashed out: what happens when you refresh the page after a turbo-frame drives the URL? What happens when you navigate with back/forward? Does a In the short term, I think that Any built-in support for this will likely require that |
Thanks @seanpdoyle.
It initiates a full page reload for that URL/route. So, if you refresh on
Right now, same as above.
That is a great question and one I was hoping someone on the core team could help me think about and implement. TBH, I don't fully understand the full logic around snapshots/visits and caching. If this could hook into Turbo Drive's snapshot functionality, that would be awesome.
That's my feeling as well. For example, previously (before some changes to the turbo accept request headers, I believe) I was able to avoid a full layout re-render by including:
in my The main reason I want to update the URL with frame/stream navigation is so I can synchronize as much of the browser UI state with the server as possible, while also performing the minimum amount of DOM manipulation. Basically to make Hotwire feel as good as the best SPAs. For example, in this view: I want to capture:
Ideally, I can navigate to this UI state with one server request and one DOM update from a turbo frame navigation (by clicking on the message in the sidebar). But, crucially, I can also render this state from a "normal" server side rendered template, by following an external link or refreshing the browser, etc. In my experience so far, having the URL capture as much of the UI state as possible has been an extremely powerful pattern for allowing Hotwire to update the page with the minimal amount of changes, while also not breaking normal server rendering, and providing a user experience as snappy and smooth as the best SPA's. Anyway, all that is to say, I'm really motivated to continue pushing this forward and to see how far Hotwire can take this paradigm. Exciting times. |
I'm a fan of this functionality. It's going to get implemented as extensions/plugins anyway and I think it has a valid use-case. @bfitch example of a still document-centric interface that has multiple independent frames but where one functions as a main frame where you'd want navigating it to update pushState makes total sense. I think the HTML attribute |
Maybe declaring data-turbo-action on the frame element or the form/link driving it would be a more appropriate data attribute interface, since that's already supported for page-level links. Conceptually, top level navigations default to data-turbo-action="advance", and frames default to data-turbo-action="replace" (since they don't have a history of their own). To support this, we'd likely have to make the Navigator frame-aware, and retain references of the frame elements that push onto the applications History stack. |
That sounds like a good option to me @seanpdoyle. So, with that attribute interface:
would render messages into the fram and update the url to I assume this approach could also handle form submissions/redirects and we could still make it work with forms and links outside frames that render into frame elements with
I hope this wouldn't be a huge lift to implement, but that sounds exactly right to me. Unifying navigation across pages and visits and frame navigation sounds like the right direction. |
Love that @bfitch - I would probably break it into 2 - first implement the attribute then allow it to actually specify target. If we get consensus that this would be desired behavior I'm happy to pair with someone on implementing it based on the already very good work submitted in this PR. |
@bfitch Thanks for your effort on this one. 🙏 We're currently trying it out in our app. (I built a custom version of the One thing that would be nice would be to get the PR rebased on top of the latest release. Right now, it's based on beta 4 (if I get it right) and we're hitting some issues which I think is fixed in beta 5 (the Can it be done? 😇 |
this is definitely one of my top needs as well. |
Has there been any progress on this @bfitch or @seanpdoyle? This looks like it would be really helpful. I have a turbo frame which filters results and I need the URL to update so a user can copy and paste that filter (or see the updated results if they navigate their history). I played around with using Turbo.visit(). However, this always resets the page back to the top which means the user has to scroll back down after filtering. |
I would very welcome this feature |
This one looks super helpful! |
Closes hotwired#50 Closes hotwired#361 Closes hotwired#167 --- Extend of built-in support for `<a>` elements with [data-turbo-action][] (with `"replace"` or `"advance"`) to also encompass `<turbo-frame>` navigations. Account for the combination of of `[data-turbo-frame]` and `[data-turbo-action]` to navigate the target `<turbo-frame>` _and_ navigate the page's history push state, supporting: * `turbo-frame[data-turbo-action="..."]` * `turbo-frame a[data-turbo-action="..."]` * `a[data-turbo-frame="..."][data-turbo-action="..."]` * `form[data-turbo-frame="..."][data-turbo-action="..."]` * `form[data-turbo-frame="..."] button[data-turbo-action="..."]` * `form button[data-turbo-frame="..."][data-turbo-action="..."]` Whenever a Turbo Frame response is loaded that was initiated from one of those submitters, forms, anchors, or turbo-frames annotated with a `[data-turbo-action]`, the subsequent firing `turbo:frame-render` event will create a `Visit` instance that will skip rendering, won't result in a network request, and will instead only update the snapshot cache and history. [data-turbo-action]: https://turbo.hotwired.dev/handbook/drive#application-visits
I've opened #398 as an alternative, based off of #167 (comment). |
Closes hotwired#50 Closes hotwired#361 Closes hotwired#167 --- Extend of built-in support for `<a>` elements with [data-turbo-action][] (with `"replace"` or `"advance"`) to also encompass `<turbo-frame>` navigations. Account for the combination of of `[data-turbo-frame]` and `[data-turbo-action]` to navigate the target `<turbo-frame>` _and_ navigate the page's history push state, supporting: * `turbo-frame[data-turbo-action="..."]` * `turbo-frame a[data-turbo-action="..."]` * `a[data-turbo-frame="..."][data-turbo-action="..."]` * `form[data-turbo-frame="..."][data-turbo-action="..."]` * `form[data-turbo-frame="..."] button[data-turbo-action="..."]` * `form button[data-turbo-frame="..."][data-turbo-action="..."]` Whenever a Turbo Frame response is loaded that was initiated from one of those submitters, forms, anchors, or turbo-frames annotated with a `[data-turbo-action]`, the subsequent firing `turbo:frame-render` event will create a `Visit` instance that will skip rendering, won't result in a network request, and will instead only update the snapshot cache and history. [data-turbo-action]: https://turbo.hotwired.dev/handbook/drive#application-visits
Closes hotwired#50 Closes hotwired#361 Closes hotwired#167 --- Extend of built-in support for `<a>` elements with [data-turbo-action][] (with `"replace"` or `"advance"`) to also encompass `<turbo-frame>` navigations. Account for the combination of of `[data-turbo-frame]` and `[data-turbo-action]` to navigate the target `<turbo-frame>` _and_ navigate the page's history push state, supporting: * `turbo-frame[data-turbo-action="..."]` * `turbo-frame a[data-turbo-action="..."]` * `a[data-turbo-frame="..."][data-turbo-action="..."]` * `form[data-turbo-frame="..."][data-turbo-action="..."]` * `form[data-turbo-frame="..."] button[data-turbo-action="..."]` * `form button[data-turbo-frame="..."][data-turbo-action="..."]` Whenever a Turbo Frame response is loaded that was initiated from one of those submitters, forms, anchors, or turbo-frames annotated with a `[data-turbo-action]`, the subsequent firing `turbo:frame-render` event will create a `Visit` instance that will skip rendering, won't result in a network request, and will instead only update the snapshot cache and history. [data-turbo-action]: https://turbo.hotwired.dev/handbook/drive#application-visits
Closes hotwired#50 Closes hotwired#361 Closes hotwired#167 --- Extend of built-in support for `<a>` elements with [data-turbo-action][] (with `"replace"` or `"advance"`) to also encompass `<turbo-frame>` navigations. Account for the combination of of `[data-turbo-frame]` and `[data-turbo-action]` to navigate the target `<turbo-frame>` _and_ navigate the page's history push state, supporting: * `turbo-frame[data-turbo-action="..."]` * `turbo-frame a[data-turbo-action="..."]` * `a[data-turbo-frame="..."][data-turbo-action="..."]` * `form[data-turbo-frame="..."][data-turbo-action="..."]` * `form[data-turbo-frame="..."] button[data-turbo-action="..."]` * `form button[data-turbo-frame="..."][data-turbo-action="..."]` Whenever a Turbo Frame response is loaded that was initiated from one of those submitters, forms, anchors, or turbo-frames annotated with a `[data-turbo-action]`, the subsequent firing `turbo:frame-render` event will create a `Visit` instance that will skip rendering, won't result in a network request, and will instead only update the snapshot cache and history. [data-turbo-action]: https://turbo.hotwired.dev/handbook/drive#application-visits
Closes hotwired#50 Closes hotwired#361 Closes hotwired#167 --- Extend of built-in support for `<a>` elements with [data-turbo-action][] (with `"replace"` or `"advance"`) to also encompass `<turbo-frame>` navigations. Account for the combination of of `[data-turbo-frame]` and `[data-turbo-action]` to navigate the target `<turbo-frame>` _and_ navigate the page's history push state, supporting: * `turbo-frame[data-turbo-action="..."]` * `turbo-frame a[data-turbo-action="..."]` * `a[data-turbo-frame="..."][data-turbo-action="..."]` * `form[data-turbo-frame="..."][data-turbo-action="..."]` * `form[data-turbo-frame="..."] button[data-turbo-action="..."]` * `form button[data-turbo-frame="..."][data-turbo-action="..."]` Whenever a Turbo Frame response is loaded that was initiated from one of those submitters, forms, anchors, or turbo-frames annotated with a `[data-turbo-action]`, the subsequent firing `turbo:frame-render` event will create a `Visit` instance that will skip rendering, won't result in a network request, and will instead only update the snapshot cache and history. [data-turbo-action]: https://turbo.hotwired.dev/handbook/drive#application-visits
Closes hotwired#50 Closes hotwired#361 Closes hotwired#167 --- Extend of built-in support for `<a>` elements with [data-turbo-action][] (with `"replace"` or `"advance"`) to also encompass `<turbo-frame>` navigations. Account for the combination of of `[data-turbo-frame]` and `[data-turbo-action]` to navigate the target `<turbo-frame>` _and_ navigate the page's history push state, supporting: * `turbo-frame[data-turbo-action="..."]` * `turbo-frame a[data-turbo-action="..."]` * `a[data-turbo-frame="..."][data-turbo-action="..."]` * `form[data-turbo-frame="..."][data-turbo-action="..."]` * `form[data-turbo-frame="..."] button[data-turbo-action="..."]` * `form button[data-turbo-frame="..."][data-turbo-action="..."]` Whenever a Turbo Frame response is loaded that was initiated from one of those submitters, forms, anchors, or turbo-frames annotated with a `[data-turbo-action]`, the subsequent firing `turbo:frame-render` event will create a `Visit` instance that will skip rendering, won't result in a network request, and will instead only update the snapshot cache and history. [data-turbo-action]: https://turbo.hotwired.dev/handbook/drive#application-visits
Closes hotwired#50 Closes hotwired#361 Closes hotwired#167 --- Extend of built-in support for `<a>` elements with [data-turbo-action][] (with `"replace"` or `"advance"`) to also encompass `<turbo-frame>` navigations. Account for the combination of of `[data-turbo-frame]` and `[data-turbo-action]` to navigate the target `<turbo-frame>` _and_ navigate the page's history push state, supporting: * `turbo-frame[data-turbo-action="..."]` * `turbo-frame a[data-turbo-action="..."]` * `a[data-turbo-frame="..."][data-turbo-action="..."]` * `form[data-turbo-frame="..."][data-turbo-action="..."]` * `form[data-turbo-frame="..."] button[data-turbo-action="..."]` * `form button[data-turbo-frame="..."][data-turbo-action="..."]` Whenever a Turbo Frame response is loaded that was initiated from one of those submitters, forms, anchors, or turbo-frames annotated with a `[data-turbo-action]`, the subsequent firing `turbo:frame-render` event will create a `Visit` instance that will skip rendering, won't result in a network request, and will instead only update the snapshot cache and history. [data-turbo-action]: https://turbo.hotwired.dev/handbook/drive#application-visits
* Add `linkClickIntercepted` to FrameElementDelegate Expand the `FrameElementDelegate` interface to include a `linkClickIntercepted` to match its existing `formSubmissionIntercepted`, then replace a manual `setAttribute` and `src` assignment with a delegation to the `FrameElementDelegate` instance. * Push history state from frame navigations Closes #50 Closes #361 Closes #167 --- Extend of built-in support for `<a>` elements with [data-turbo-action][] (with `"replace"` or `"advance"`) to also encompass `<turbo-frame>` navigations. Account for the combination of of `[data-turbo-frame]` and `[data-turbo-action]` to navigate the target `<turbo-frame>` _and_ navigate the page's history push state, supporting: * `turbo-frame[data-turbo-action="..."]` * `turbo-frame a[data-turbo-action="..."]` * `a[data-turbo-frame="..."][data-turbo-action="..."]` * `form[data-turbo-frame="..."][data-turbo-action="..."]` * `form[data-turbo-frame="..."] button[data-turbo-action="..."]` * `form button[data-turbo-frame="..."][data-turbo-action="..."]` Whenever a Turbo Frame response is loaded that was initiated from one of those submitters, forms, anchors, or turbo-frames annotated with a `[data-turbo-action]`, the subsequent firing `turbo:frame-render` event will create a `Visit` instance that will skip rendering, won't result in a network request, and will instead only update the snapshot cache and history. [data-turbo-action]: https://turbo.hotwired.dev/handbook/drive#application-visits * Extract `getAttribute` utility function For cases where we need to find an attribute value from a collection of elements, use `getAttribute` instead of a long chain of `||` and `?` operators.
Awesome!!! |
If anyone is looking for a workaround to manage history from stream response, check this one: #792 |
Adds the ability for Turbo Frames to update the URL on link navigation and in response to form submissions. Also, enables updating the URL in Turbo Stream responses.
Frame Navigation
If a frame has a link with the data attribute
data-turbo-history
, when clicked, it will updatewindow.location
viapushState
orreplaceState
. For example:will update the url to
/messages
.You can also manually override the URL to whatever value you may need:
updates the url to
/special-messages
.To update the URL with
replaceState
, add"replace"
as the attribute value:replaces the current url with
/messages
.Frame Form Submission
The same data attributes can be added to form elements. URLs will either match the form's
action
attribute or any redirect URLs from the server:will update the URL to
/messages
. Or if the server redirects, for example to/messages/3
, the redirected URL will be displayed instead.Note: URL's can also be updated by forms and links outside frames that render into frame elements with
data-turbo-frame="<frame_id>"
.Stream Rendering
The same data attributes can be used to update the URL in response to
<turbo-stream>
responses:appends message 2 to the messages list and updates the URL to
messages/2
.Prior Issues and Discussion