I ran the following in the command line:
> rails new test-turbo
> rails g controller Home index
Added the following route to config/routes.rb
:
resources :home, only: [:index]
Wrapped the <%= yield %>
in app/views/layouts/application.html.erb
in a turbo frame tag:
<body>
<%= turbo_frame_tag :foobar do %>
<%= yield %>
<% end %>
</body>
Added a link in app/views/home/index.html.erb
:
<%= link_to "Link to this page", home_index_path %>
Then, when clicking on that link, I get a turbo frame error:
If I move the turbo frame tag into the home/index page, it works fine:
app/views/home/index.html.erb
<%= turbo_frame_tag :foobar do %>
<h1>Home#index</h1>
<p>Find me in app/views/home/index.html.erb</p>
<%= link_to "Link to this page", home_index_path %>
<% end %>
Why does this work, but putting it in app/views/layouts/application.html.erb
doesn't work? It's generating the same HTML at the end of the day, right?
In case its helpful, here's the GitHub repo. Commit 537c40a
has the error, commit b42ca66
works fine.
Update: here's a gif displaying a solution that works with two turbo frames, one that is persistent across pages:
When navigating in a frame your application
layout isn't rendered. Which you can see from the logs:
Rendered home/index.html.erb within layouts/turbo_rails/frame (Duration: 0.1ms | Allocations: 38)
You're looking at the inspector and <turbo-frame id="foobar">
, when you click the link, current page html is not replaced with the response html, that's really the whole point of a frame. Only content inside the frame is updated. You can see the actual response from the network tab:
No turbo frame in the response ^
To render your layout, you can explicitly set it in the controller to override turbo frame's layout:
layout "application"
Just FYI, there is very little need for turbo frame in the layout, especially, one that wraps around everything. <body>
element already acts like a frame, and another frame directly under it doesn't really gain you anything.
Update
Maybe having a whole controller is bit too much. I'd start with something simple. The simplest way is to always render application
layout. Skipping layout rendering is only an optimization to speed things up, anything outside of the frame gets discarded anyway.
You can add some if..else
to the layout:
...
<body>
<% unless request.headers["Turbo-Frame"] == "main_content" %>
<%= turbo_frame_tag :side_bar do %>
# TODO: sidebar
<% end %>
<% end %>
<%= turbo_frame_tag :main_content do %>
<%= yield %>
<% end %>
</body>