I'm building a new Rails 7 app and trying to follow the intended default uses of turbo streams for ajax content replacements (and stimulus where other JS control is needed). This is my first time using Turbo Drive.
I'm also using view components (https://viewcomponent.org/).
The problem is that the response from the server appears to be treated as plain text by the browser. I've tried in Chrome and Brave and the response mime-type is as expected (text/vnd.turbo-stream.html
per the browser reported response header). Further, the browser console reports that window.Turbo
is defined so Turbo seems to be available on the client side JS.
As a starting point, this is a snippet of html rendered during initial display before an intended operation:
<div class='grid grid-cols-2 gap-4'>
<div class='card bg-base-100 shadow-x1 mb-4'>
<div class='card-body'>...some text here...</div>
<div class='card-actions justify-center'>
<form action="/someroute.turbo_stream" accept-charset="UTF-8" method="get">
<div class='input-group'>
<input placeholder="Bring out your text" type="text" name="location_text" id="location_text" />
<input type="submit" name="commit" value="Button Legend" data-disable-with="Button Legend" />
</div>
</form>
</div>
</div>
</div>
.
.
.
<turbo-frame data-turbo='true' id='some-container'>
<div class='border rounded p-4 mb-4'>
<div class='text-center mb-2'>...some text...</div>
</div>
</turbo-frame>
The intention is that some-container
html be replaced upon submitting the button form (see <input type="submit"...>
above). Note that I intend it to be a GET operation (see the <form ...> element).
That part works fine. The button is pressed and my controller is triggered as expected.
controller:
def show
# ...other code to prepare content...
respond_to do |format|
format.turbo_stream
format.html do
raise 'unexpectedly HTML format'
end
end
end
show.turbo_stream.haml:
= turbo_stream.replace '#some-container' do
= render SomeContainerComponent.new(<some arguments>) # just for interior content
and just in case, here's the HAML for the component (that same component was used to initially render the page and intention is just to update it):
%turbo-frame#some-container{data: {turbo: 'true'}}
.border.rounded.p-4.mb-4
= render(AnotherComponent.new(<some arguments>))
From what I've seen, I should really need the turbo-frame element since the id of some-container
should be sufficient. And I shouldn't need the data-turbo=true
either (I haven't disabled Turbo as far as I can tell.). I've tried it with and without those pieces without any appreciable change.
The only other thing that seems relevant is to share that this is the line in the page view layout for involving the JS:
= javascript_include_tag 'application', 'data-turbo-track': 'reload', defer: true
Still, when I submit the form, the response from the server replaces the page with a plain text rendering of the HTML intended to be used to replace the #some-container
element and looks otherwise properly formed.
Here's what that HTML response looks like on the browser:
<turbo-stream action="replace" target="#some-container"><template><turbo-frame data-turbo='true' id='some-container'>
.
.
.
...HTML elements for the content to be displayed...
.
.
.
</turbo-frame>
</template></turbo-stream>
Since the form submits and triggers the controller, it seems that this is really just a JS related issue with Turbo. I can always fallback to the older UJS form with remote: true
and explicitly handle ajax:success
, but wanted to make what is apparently the new recommended way to do this in Rails 7 with Turbo.
Any advice how to get it to stop rendering the response snippet in plain text?
CodePudding user response:
Don't add format extension to url. Turbo only works with html anyway. If extension is x?html
or empty, turbo kicks in. If it is something else, a regular request is sent. Notice when submitting your form the page actually does a full refresh. Request to /page.turbo_stream
renders a partial with a <turbo-stream> tag (no layout, no javascript) and a header Content-Type: text/vnd.turbo-stream.html;
. Because it is not text/html, browser renders it as plain text.
To start, just use <turbo-frame>.
<%= form_with url: "/path",
method: :get,
data: { turbo_frame: :form_response } do |f| %>
<%= f.submit %>
<% end %>
<%= turbo_frame_tag :form_response %>
Response is in html format:
format.html { render :something }
and it can look like this:
<!-- Anything else on this page outside of this frame will be discarded -->
<%= turbo_frame_tag :form_response do %>
<dialog open>
<p>Updated</p>
<form method="dialog"><button type="submit">Close</button></form>
</dialog>
<% end %>
GET streams are only supported in the recent beta v7.2.0-beta.1
https://github.com/hotwired/turbo/pull/612
# Gemfile
# gem "turbo-rails"
gem "turbo-rails", github: "hotwired/turbo-rails", branch: "turbo-7-2-0"