Skip to content
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

Turbo frame history not navigating properly w/ back button after upgrading from 7 to 8 #1241

Open
charlesobrien opened this issue Apr 5, 2024 · 28 comments · May be fixed by #1281
Open

Turbo frame history not navigating properly w/ back button after upgrading from 7 to 8 #1241

charlesobrien opened this issue Apr 5, 2024 · 28 comments · May be fixed by #1281

Comments

@charlesobrien
Copy link

Context:

Application has a simple live search via form:

  • Form uses "get" method (just showing data; no updates)
  • Form targets a "results" turbo frame
  • Form uses data-turbo-action="advance" to update URL (to support users being able to copy/paste w/ params for the search and filter)

What's happening: Live search is working fine. When the form is submitted (automatically as a user types), the turbo frame is updated with the correct results and the URL displays the correct query parameters. However, when navigating with the back button the turbo frame does not navigate backwards, it only shows the most recent set of results (despite the url and associated query params appropriately changing backwards).

What should be happening: As the user navigates backwards the URL query params should change (which is happening) and the turbo frame should reflect the history from that point in time.

When did this start happening: This happened on Turbo 8.0.4. and Turbo Rails 2. If I revert to Turbo < 8 and Turbo Rails < 2, the behavior works as expected.

@deluxetom
Copy link

I'm experiencing the same issue, after the upgrade, a link inside a turbo frame is updating the URL correctly but when I use the back button, it only shows the current snapshot.
The weird part is that its only for the first link inside the turbo frame, if I click on another link afterwards, the history and the snapshots seem to work as expected

@tleish
Copy link
Contributor

tleish commented May 31, 2024

I tried reproducing and could not. Is there a simple code example that reproduces this?

@charlesobrien
Copy link
Author

I tried reproducing and could not. Is there a simple code example that reproduces this?

Hi @tleish! Sorry for the delay. I should have time this weekend to make a simple app to demo the issue.

@pjg
Copy link

pjg commented Jun 11, 2024

I've run into this as well. A simple html page with turbo_frame and a link can trigger this.

Controller action index and corresponding index.html.erb:

<%= turbo_frame_tag 'page' do %>
  <%= render 'users', users: @users %>

  <p>
    <%= link_to_prev_page @users, '< Previous Page', data: { turbo_frame: 'page', turbo_action: 'advance' } %>
    <%= link_to_next_page @users, 'Next Page >', data: { turbo_frame: 'page', turbo_action: 'advance' } %>
  </p>
<% end %>

(prev/next links are from kaminari).

Loading index, clicking "Next", then Browser's back button triggers this issue. The page content is not being refreshed.

@deluxetom
Copy link

I just tested again and like @pjg, when I use:

Turbo.visit(url.toString(), {frame: 'search-results', action: 'advance'});

The URL is updated but when I hit the back button, I don't see the previous page. it only happens with the initial call, if I use Turbo.visit a second time and hit the back button, it works as expected.

@tleish
Copy link
Contributor

tleish commented Jun 11, 2024

Ok, I was able to reproduce this by moving the links outside the turbo-frame, where it did work where links were within the turbo-frame.

@tpaulshippy
Copy link

I am not able reproduce this, even by moving the links outside the frame. I was looking primarily at this page after running npm start

Does this only happen in rails? Do the URLs have to have query strings? Trying to figure out how you break this.

@tleish
Copy link
Contributor

tleish commented Jun 14, 2024

Tried my local test again. Realized it had nothing to do with links inside or outside the turbo-frame, but rather IF the content inside the turbo-frame had any HTML, which I don't necessarily think is a bug.

For example:

<turbo-frame id="results">
 Does not cache and show on Restoration Visit
</turbo-frame>
<turbo-frame id="results">
  <div>Caches and shows on Restoration Visit</div>
</turbo-frame>

So, I'm still unable to reproduce the original issue.

@tpaulshippy
Copy link

Interesting. That sounds like a different bug. I wonder why the HTML tags affect the behavior.

@tpaulshippy
Copy link

tpaulshippy commented Jun 14, 2024

I'm not able to reproduce that issue either @tleish

Got it. If the original frame content has just plain text, the back button will not restore it. Weird. Are you going to open another issue for that one? @tleish

@tleish
Copy link
Contributor

tleish commented Jun 14, 2024

I'm not sure the turbo-frame with text only not rendering on restoration visits is a bug. I believe turbo uses cloneNode to cache html. If the frame has no nodes to cache (text only), then it doesn't cache it.

@tleish
Copy link
Contributor

tleish commented Jun 14, 2024

FYI, one (failed) attempt to reproduce the issue:

https://github.com/tleish/turbo-frame-history

Perhaps others can use this as a starting point to reproduce the issue.

@tleish
Copy link
Contributor

tleish commented Jun 14, 2024

Note: I tested this on MacOS Chrome, Firefox and Safari. Perhaps it's a specific OS and/or browser?

@pjg
Copy link

pjg commented Jun 14, 2024

I've created a minimal reproducible testcase. I'm using Rails. Here's the repo: https://github.com/pjg/turbo-frame-bug

Steps to reproduce:

bundle install
bin/rails server

Navigate to http://localhost:3000

Click "Next page".

Click Back browser button.

On my end (MacOS; Chrome, Safari, Firefox) when I click Back page content doesn't refresh. It stays the same, while it should display the previous page.

NOTE: It starts working again if I click Next Page/Previous Page a couple of times.

@tpaulshippy
Copy link

Here's a more minimal way to reproduce (outside of rails):

  1. Create a folder.
  2. Drop the turbo.es2017-umd.js file into it.
  3. Create the following two html files:

firstpage.html

<!DOCTYPE html>
<html>
  <head>
    <title>Frame advance bug</title>
    <script data-turbo-track="reload">
    </script>
    <script src="turbo.es2017-umd.js"></script>
  </head>

  <body>
    <h1>My Page</h1>

    <turbo-frame id="page">
      <p>First page</p>
    </turbo-frame>
    <p>
      <a
        data-turbo-frame="page"
        data-turbo-action="advance"
        href="/nextpage.html"
        >Next page</a
      >
    </p>
  </body>
</html>

nextpage.html

<turbo-frame id="page">
    <p>Next page</p>
</turbo-frame>

The issue seems to be the presence of the data-turbo-track="reload" attribute on any valid tag in the HEAD. If you remove that attribute, the issue goes away. But if you put it on any tag (I tried it on <title>, <meta>, and <script>), you see the issue.

@pjg
Copy link

pjg commented Jun 14, 2024

The issue seems to be the presence of the data-turbo-track="reload" attribute on any valid tag in the HEAD. If you remove that attribute, the issue goes away.

I do not find it to be true. In my regular app I'm running with data-turbo-track: '' (empty string). If I remove data-turbo-track from my minimal Rails example app, I can still reproduce the issue.

@tpaulshippy
Copy link

Have you viewed source on the page and confirmed that there is no data-turbo-track="reload" anywhere in your HEAD? I ask because I'm not sure it's easily removed from a boilerplate rails app.

@pjg
Copy link

pjg commented Jun 14, 2024

You're right. Rails adds data-turbo-track: "reload" for importmaps.

The source is here: https://github.com/rails/importmap-rails/blob/0a6330133b75a72d7c5d07fe87c2fce50d76b997/app/helpers/importmap/importmap_tags_helper.rb#L15 -- I don't think it can be "turned off".

@tpaulshippy
Copy link

tpaulshippy commented Jun 14, 2024

Yep. That's the issue. Well, the issue is in Turbo but yeah that's why you can't workaround the issue easily.

@tleish
Copy link
Contributor

tleish commented Jun 15, 2024

@tpaulshippy - I think you found the cause. Testing out @pjg github project, I saw it reproduced. The original page requesting includes several elements with data-turbo-track, but the HTML from turbo-frame response has an empty <head>. This is because the turbo-rails gem includes the following logic:

app/controllers/turbo/frames/frame_request.rb

# turbo-rails-2.0.5/app/controllers/turbo/frames/frame_request.rb
# The layout used is <tt>turbo_rails/frame.html.erb</tt>. If there's a need to customize this layout, an application can
# supply its own (such as <tt>app/views/layouts/turbo_rails/frame.html.erb</tt>) which will be used instead.

layout -> { "turbo_rails/frame" if turbo_frame_request? }

From the above comment you can replace the minimal app/views/layouts/turbo_rails/frame.html.erb with your own. Testing this out I addedapp/views/layouts/turbo_rails/frame.html.erb to the project with tags that include data-turbo-track (javascript_importmap_tags adds it). Note that I ignore title, meta and other tags that do not include data-turbo-track.

<html>
<head>
  <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
  <%= javascript_importmap_tags %>
</head>
<body>
<%= yield %>
</body>
</html>

After doing this workaround, turbo-frame Restoration Visits (back) work as expected.

I don't see this as the "solution", just a temporary workaround until the actual problem is fixed.

@jonh-favo
Copy link

Will this also make sure to trigger a reload of the page if there has been a new deploy and the asset files has changed? If so, it maybe a smart thing to do anyway?

@tpaulshippy
Copy link

tpaulshippy commented Jun 18, 2024

Yes @jonh-favo

Looks like this issue started to break here: ed72ba1

It doesn't seem like the frame should be required to have all the same tags (with turbo-track=reload) in head as the hosting page for the back button to work. But that seems to be what's happening.

@jonh-favo
Copy link

I discovered that I also need to add all meta tags that configures turbo in that turbo_rails/frame.html.erb layout, and I needed to add csrf_meta_tag. Otherwise all requests made by turbo afterwards were missing the the x-csrf-token header and reverted to default turbo behavior.

For instance I've disabled the prefetch functionality in the application.html.erb layout, but after a single turbo request that updated a frame it was suddenly enabled again.

tleish added a commit to tleish/turbo that referenced this issue Jun 21, 2024
tleish added a commit to tleish/turbo that referenced this issue Jun 21, 2024
@kenkantzer-truss
Copy link

I also ran into this just today, can confirm @tleish's workaround fixes it.

tleish added a commit to tleish/turbo that referenced this issue Jun 24, 2024
tleish added a commit to tleish/turbo that referenced this issue Jun 24, 2024
tleish added a commit to tleish/turbo that referenced this issue Jun 24, 2024
tleish added a commit to tleish/turbo that referenced this issue Jun 24, 2024
tleish added a commit to tleish/turbo that referenced this issue Jun 24, 2024
tleish referenced this issue Jun 24, 2024
After the changes made in [@hotwired/turbo#867][] and changes made in
[@hotwired/turbo-rails#428][] (the canonical server-side
implementation), Turbo expects full HTML documents in response to
requests with `Turbo-Frame:` headers.

Prior to this commit, the `FrameController` compensated for missing
pieces of an HTML document by taking an HTML "snapshot" of the current
page through the `<html>` element's [outerHTML][].

This commit changes the `fetchResponseLoaded` callback to read the
`responseHTML` directly from the `FetchResponse`, since that will be a
fully formed HTML document in Turbo v7.3.0 and later.

To support that change, this commit also updates various
`src/test/fixtures` files to render fully-formed HTML documents.

[@hotwired/turbo#867]: #867
[@hotwired/turbo-rails#428]: hotwired/turbo-rails#428
[outerHTML]: https://developer.mozilla.org/en-US/docs/Web/API/Element/outerHTML
@kenkantzer-truss
Copy link

kenkantzer-truss commented Jul 2, 2024

Just noting this for @tleish, primarily in case it's relevant to the fix in the merged PR:

I discovered that the above workaround breaks pages that try to force reload a new page via <% turbo_page_requires_reload %> or <meta "turbo-visit-control", content: "reload"</meta>.

It looks like adding <%= yield :head %> to the workaround fixes it, but I don't know turbo well enough to be confindent:

<html>
  <head>
    <%= csrf_meta_tags %>
    <%= stylesheet_link_tag "application", "custom", "actiontext", "data-turbo-track": "reload" %>
    <%= javascript_importmap_tags %>
    <%= yield :head %>
  </head>
  <body>
    <%= yield %>
  </body>
</html>

@cavenk
Copy link

cavenk commented Aug 19, 2024

Hi, I have tried doing the solution above by replacing the frame.html.erd but it's still not working.

When clicking the back button with or without this solution, the back button seems to work 1 time out of 10 but I am not sure what I did different to make it work. Also I noticed that when it works, sometimes its getting the page from the cache but sometimes its doing a full page reload instead.

Did someone still has the same problem?

@kenkantzer-truss
Copy link

hey @cavenk, the above workarounds should work - for turbo 8, as a recap, the full workaround I'm currently using in production for the past 6 weeks or so is:

in app/views/layouts/turbo_rails/frame.html.erb:

<html>
  <head>
    <%= csrf_meta_tags %>
    <%= stylesheet_link_tag "application", "custom", "actiontext", "data-turbo-track": "reload" %>
    <%= javascript_importmap_tags %>
    <%= yield :head %>
  </head>
  <body>
    <%= yield %>
  </body>
</html>

ymmv - but the problem you're mentioned (1/10 times it works) does sound similar to what I was experiencing - the bacl bittpm would sometimes be successful depending on if a turbo frame action had previously triggered or not.

@fonji
Copy link

fonji commented Oct 31, 2024

Hi, I also run into this problem and tried the layout patch without success.
It always fails if I try to go back to the very first page.

If I do a single navigation, the back button does nothing (except breaking things mostly because of outdated CSRF).
If I navigate twice, then the back button works once.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
9 participants