Correctly updating turbo frame source via stimulus controller

146 views Asked by At

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?

1

There are 1 answers

0
Alex On BEST ANSWER

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));
  }
}