Using turbo rails gem. After watching videos so far I was expecting returning HTML from turbo requests to be automatically with layout: false, however my actions are getting returned with full layout. Everything is working, however much slower than expected due to this .
Interesting, turbo is supposed to return the view/partial content. even the content that dont belong to the specified turbo-frame ( turbo will cut and use only the specified turbo-frame id ), but yeah, the layout shouldn’t be present on turbo fetch responses.
Below is the code I have in my view and controller. Everything works fine, but full layout comes back as you can see from screenshot. I think its because Hotwire does full page if you are doing a fetch?
View:
<%= form_for @criteria,
as: :criteria,
method: :get,
url: locations_url,
data: { controller: "search", "turbo-frame": "locations" } do |f| %>
<%= f.search_field :query,
class: "form-control",
placeholder: t("common.buttons.search"),
autofocus: true,
data: { action: "debounced:input->search#trigger" } %>
<% end %>
<turbo-frame id="locations">
<% for location in @locations %>
...
<% end %>
</turbo-frame>
Controller:
def index
@locations = Location.order("name ASC")
@criteria = Criteria.new(permit_criteria_params)
@locations = @locations.where("lower(name) LIKE lower(?)", "#{@criteria.query}%") if @criteria.query.present?
end
Stimulus controller:
import { Controller } from "stimulus"
export default class extends Controller {
trigger(event) {
this.element.requestSubmit();
}
}
Rails will send the full page, including layout, if you request that controller action. Turbo Frames expects this—it searches the full page for the frame that you wanted to replace and take out its contents. But for cases like these (search, autocomplete) you could create a separate action and use a nil layout!
@andrew in the Chrome Dev Tools request you’ve shown in your screenshot, can you check the request headers and look if a Turbo-Frame header is being sent?
This will help determine whether the issue is in the view code or if something in your controller is overriding the behaviour that prevents the the layout being used for a Turbo Frames request.
Getting this and tried many things to try narrow down the issue. I have a fairly complex ApplicationController, and when inheriting from just ActionController::Base , I still get the same issue.
I am trying to see if its some conflicting gem. Will have to try disable some slowly and see if something could be causing it.
One idea is to open up the source for Action View and put some trusty puts debugging in the layout method of layouts.rb to see what calls are being made to that method that might be overriding the layout -> { false if turbo_frame_request? }.
Hi @mikej@stgm I think I found the issue within my application anyway, pretty sure it will be same for everyone else. It is caused whenever I have controllers that set the layout at the controller level. It will force turbo to always use the layout.
Steps to reproduce
class TestController < ActionController::Base
def index
end
end
Everything will work fine and application.html.erb layout will NOT be used
class TestController < ActionController::Base
layout "custom"
def index
end
end
custom.html.erb layout will be always used for turbo response and seems to take precendence.
Expectation is that turbo should never use the layout in its response
Ah, of course! Coincidentally, I have been using this behaviour to my advantage in my application.
In this application we have a lot of settings/administration screens that are presented in an easy-to-dismiss modal dialog. All of these screens have their own controllers and they are loaded on demand. To clean up the views, I have actually put all of the boilerplate into a layout and assigned this layout to each of the relevant controllers. This is the layout:
<turbo-frame id="modal">
<div class="modal-content">
<% if content_for?(:title) %>
<div id="modal-browser-header">
<%= yield(:title) %>
</div>
<% end %>
<div id="modal-browser-body">
<%= yield %>
</div>
<% if content_for?(:footer) %>
<div id="modal-browser-footer">
<%= yield(:footer) %>
</div>
<% end %>
</div>
</turbo-frame>
Because of this, I must disagree that layouts should be ignored for Turbo Frame requests! Plainly ignoring layouts would make it impossible to do some of these nice optimizations.
I do understand that some layouts may be too heavy to needlessly render, so I hope we can find some middle ground
That’s is quite interesting, at glance, I’d say that it’s the wrong behavior, but I can’t say for sure. Maybe you could open an issue on GitHub to make sure this is the expected behavior