Programmatically navigating in a frame

I have an app that shows a different word every 10 seconds.

I have this, which works, but I’m wondering if there’s a better way:

words/show.html.erb (which is on the / route):

<div data-controller="words">
  <turbo-frame id="word">
    <div class="screen-center">
      <div class="word">
        <%= @word.value %>
      </div>
      <div class="explanation">
        <%= @word.explanation %>
      </div>
    </div>
  </turbo-frame>
</div>
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  initialize() {
    setInterval(() => {
      this.change()
    }, 10000)
  }

  change() {
    this.element.querySelector('turbo-frame').src = '/'
  }
}

Is there maybe a better way to navigate a frame? Turbo.visit seems to navigate the main page.

You could add the path to the src attribute and reload the element with the FrameElement.reload() function instead of programmatically setting the src attribute (Turbo Reference)? I think something like this would work, but I haven’t tested it:

  <turbo-frame id="word" src="/" data-controller="words">
    <div class="screen-center">
      <div class="word">
        <%= @word.value %>
      </div>
      <div class="explanation">
        <%= @word.explanation %>
      </div>
    </div>
  </turbo-frame>
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  initialize() {
    setInterval(() => {
      this.element.reload()
    }, 10000)
  }
}

Or you could see what it would look like to stream the partial from the back end every 10 seconds?

1 Like

Thanks for the suggestions.

1.

The src="/" part doesn’t work, I get this error on the console: “Error: Matching element has a source URL which references itself”.

2.

Attaching the controller to the turbo-frame works, i.e.:

<turbo-frame id="word" data-controller="words">

this.element.src = '/'

3.

I also managed to do it with a stream:

words/show.html.erb:

<%= turbo_stream_from "main" %>

<%= render partial: 'words/word', locals: { word: @word } %>

words/_word.html.erb:

<div id="word-screen" class="screen-center">
  <div class="word">
    <%= word.value %>
  </div>
  <div class="explanation">
    <%= word.explanation %>
  </div>
</div>

Rails console:
(I guess I’ll need to put this in a background job)

loop do
  sleep 10
  Turbo::StreamsChannel.broadcast_replace_to(
    'main',
    target: 'word-screen',
    partial: 'words/word',
    locals: { word: Word.all.sample }
  )
end