I have a form with a select tag. The select’s options are a list of exercises, and the values are the id.
When the value of that tag changes, I’m want to update a turbo frame with the response from the path /exercises/[id]
using a stimulus controller.
The exercises#show
action responds to both html and turbo_stream
exercises_controller.rb
respond_to do |format|
format.html
format.turbo_stream
end
show.html.erb
<h1>This is the html template</h1>
show.turbo_stream.erb
<%= turbo_stream.update 'exercise_details', partial: 'exercises/history' %>
exercises/_history.html.erb
<h1>This is the turbo template</h1>
history_controller.js
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
connect() {
}
change(event) {
let frame = document.getElementById('exercise_details')
frame.src = "/exercises/" + event.currentTarget.value
frame.reload()
}
}
The frame is updating as expected on change, but with the html template rather than the turbo template.
How do I get the frame to update with the turbo template?
There are two ways you can do it. Setting a .turbo_stream
url extension:
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
change(event) {
const frame = document.getElementById("exercise_details")
frame.src = "/exercises/" + event.currentTarget.value + ".turbo_stream"
// frame.reload(); // there is no need to reload
}
}
or sending your own turbo stream fetch
request, you don't need turbo frame to do this:
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
change(event) {
const url = "/exercises/" + event.currentTarget.value
fetch(url, {
headers: {
"X-CSRF-Token": document.querySelector("[name='csrf-token']").content,
"Accept": "text/vnd.turbo-stream.html"
}
})
.then(response => response.text())
.then(text => Turbo.renderStreamMessage(text));
}
}