I have this form:
<%= form_with url: suggestions_path, method: :get, data: { turbo_stream: true } do |form| %>
<%= form.search_field :query %>
<% end %>
<div id="salesMemberSuggestions"></div>
suggestions_path
maps to this method in the controller:
def index
if params[:query].present?
# Gets the suggestions from an external API
...
@suggestions = # ... formatted data form the API
# Renders the turbo stream...
render(turbo_stream: turbo_stream.update(
"salesMemberSuggestions",
partial: "copilot/suggestions/suggestions",
locals: { suggestions: @suggestions },
))
else
render(turbo_stream: turbo_stream.update("salesMemberSuggestions", ""))
end
end
The copilot/suggestions/suggestions
partial looks like this:
<div>
<% if suggestions.any? %>
<ul>
<% suggestions.each do |suggestion| %>
<li>
<%= suggestion.title %>
</li>
<% end %>
</ul>
<% else %>
<p>No results</p>
<% end %>
</div>
When I press enter
to submit the form, I'd expect the suggestions to be rendered below the input, but a completely new page loads with the turbo stream as raw text:
<turbo-stream action="update" target="salesMemberSuggestions"><template><div>
<ul>
<li>
Movie 1
</li>
<li>
Movie 2
</li>
</ul>
</div></template></turbo-stream>
When I look at the network tab the Accept
header is text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
. It should be turbo, but I don't know how to tell the form it should expect a turbo stream without using stimulus. Other questions suggest data: { turbo_stream: true }
, but that didn't work for me.
I have this form:
<%= form_with url: suggestions_path, method: :get, data: { turbo_stream: true } do |form| %>
<%= form.search_field :query %>
<% end %>
<div id="salesMemberSuggestions"></div>
suggestions_path
maps to this method in the controller:
def index
if params[:query].present?
# Gets the suggestions from an external API
...
@suggestions = # ... formatted data form the API
# Renders the turbo stream...
render(turbo_stream: turbo_stream.update(
"salesMemberSuggestions",
partial: "copilot/suggestions/suggestions",
locals: { suggestions: @suggestions },
))
else
render(turbo_stream: turbo_stream.update("salesMemberSuggestions", ""))
end
end
The copilot/suggestions/suggestions
partial looks like this:
<div>
<% if suggestions.any? %>
<ul>
<% suggestions.each do |suggestion| %>
<li>
<%= suggestion.title %>
</li>
<% end %>
</ul>
<% else %>
<p>No results</p>
<% end %>
</div>
When I press enter
to submit the form, I'd expect the suggestions to be rendered below the input, but a completely new page loads with the turbo stream as raw text:
<turbo-stream action="update" target="salesMemberSuggestions"><template><div>
<ul>
<li>
Movie 1
</li>
<li>
Movie 2
</li>
</ul>
</div></template></turbo-stream>
When I look at the network tab the Accept
header is text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
. It should be turbo, but I don't know how to tell the form it should expect a turbo stream without using stimulus. Other questions suggest data: { turbo_stream: true }
, but that didn't work for me.
1 Answer
Reset to default 1After many tears, I figured out that you need to include an import statement in app/javascript/application.js
for turbo to work. I added this to config/importmap
:
pin "@hotwired/turbo-rails", to: "turbo.min.js"
Once you've pinned turbo, you can import it in app/javascript/application.js
:
import "@hotwired/turbo-rails"
After that, Turbo Streams worked like a charm: the code a wrote in the question was correct.
The import for Turbo is included by default in the happy path of Rails development, so most people don't run into this, but my app was in a weird state.