Skip to content

Conversation

@seanpdoyle
Copy link
Contributor

@seanpdoyle seanpdoyle commented Sep 14, 2023

The problem

If a <turbo-frame> element is rendered without a [src] attribute,
calls to .reload() will have no effect. If a <turbo-frame> is to be
its own browsing context, it should be able to apply its current
location (that is, it's owning document's current location) to its
browsing context.

For example, if a page has a <turbo-frame> element that contains text
that's typically updated by a Web Socket-delivered <turbo-stream>, it
might be useful to gracefully degrade to periodic long-polling if
that Web Socket connection were to fail. That might involve something
like a reload Stimulus controller with a delay:

<script type="module">
import { Application, Controller } from "@hotwired/stimulus"

const application = // boot up a Stimulus application

application.register("reload", class extends Controller {
  static values = { frequency: Number }

  disconnect() {
    this.#reset()
  }

  frequencyValueChanged(frequencyInMilliseconds) {
    this.#reset()

    if (frequencyInMilliseconds) {
      this.intervalID = setInterval(() => this.element.reload(), frequencyInMilliseconds)
    }
  }

  #reset() {
    if (this.intervalID) clearInterval(this.intervalID)
  }
})
</script>

<turbo-frame id="dynamic-data" data-controller="reload" data-reload-frequency-value="30000">
  <h1>This data will refresh every 30 seconds</h1>
</turbo-frame>

The fact that the <turbo-frame id="dynamic-data"> element doesn't have
a [src] attribute shouldn't prevent the page from being able to
re-fetch its content.

The solution

When FrameElement.reload() is invoked, it delegates to its delegate
instance's sourceURLReloaded() method. In all cases,
FrameElement.delegate is an instance of FrameController.

This commit extends the FrameController.sourceURLReloaded()
implementation to set the element's [src] attribute to the element's
baseURI value, which sets off the usual attribute change listeners
and <turbo-frame> navigation logic.

@seanpdoyle seanpdoyle force-pushed the turbo-frame-reload-without-src-attribute branch from f83dcff to 3582ccf Compare September 14, 2023 18:12
@afcapel
Copy link
Collaborator

afcapel commented Sep 28, 2023

In the specific example in the PR, you could also set the turbo frame src attribute before the reload:

    if (frequencyInMilliseconds) {
      this.element.src ||= this.element.baseURI
      this.intervalID = setInterval(() => this.element.reload(), frequencyInMilliseconds)
    }

I feel that setting the src makes it easier to see that the frame is a remote frame and where it came from. For example, we have some changes in the pipeline that rely on the presence of the src attribute to identify remote frames.

@seanpdoyle
Copy link
Contributor Author

@afcapel that's the exact solution we're using to work around the current constraints!

Is the separation between "remote" and "local" frames something that we'll need to document?

If we aren't interested in this change, would it be worth adding this solution to be part of the Turbo Frame reload() documentation?

@seanpdoyle seanpdoyle mentioned this pull request Oct 5, 2023
2 tasks
@seanpdoyle seanpdoyle force-pushed the turbo-frame-reload-without-src-attribute branch from 3582ccf to 9a05f66 Compare November 17, 2023 14:36
@brunoprietog
Copy link
Collaborator

I actually agree with this one. It is quite useful for polling.

The problem
---

If a `<turbo-frame>` element is rendered without a `[src]` attribute,
calls to `.reload()` will have no effect. If a `<turbo-frame>` is to be
its own browsing context, it should be able to apply its current
location (that is, it's owning document's current location) to its
browsing context.

For example, if a page has a `<turbo-frame>` element that contains text
that's typically updated by a Web Socket-delivered `<turbo-stream>`, it
might be useful to [gracefully degrade][] to periodic long-polling if
that Web Socket connection were to fail. That might involve something
like a `reload` Stimulus controller with a delay:

```html
<script type="module">
import { Application, Controller } from "@hotwired/stimulus"

const application = // boot up a Stimulus application

application.register("reload", class extends Controller {
  static values = { frequency: Number }

  disconnect() {
    this.#reset()
  }

  frequencyValueChanged(frequencyInMilliseconds) {
    this.#reset()

    if (frequencyInMilliseconds) {
      this.intervalID = setInterval(() => this.element.reload(), frequencyInMilliseconds)
    }
  }

  #reset() {
    if (this.intervalID) clearInterval(this.intervalID)
  }
})
</script>

<turbo-frame id="dynamic-data" data-controller="reload" data-reload-frequency-value="30000">
  <h1>This data will refresh every 30 seconds</h1>
</turbo-frame>
```

The fact that the `<turbo-frame id="dynamic-data">` element doesn't have
a `[src]` attribute shouldn't prevent the page from being able to
re-fetch its content.

The solution
---

When `FrameElement.reload()` is invoked, it delegates to its delegate
instance's `sourceURLReloaded()` method. In all cases,
`FrameElement.delegate` is an instance of `FrameController`.

This commit extends the `FrameController.sourceURLReloaded()`
implementation to set the element's `[src]` attribute to the element's
[baseURI][] value, which sets off the usual attribute change listeners
and `<turbo-frame>` navigation logic.

[baseURI]: https://developer.mozilla.org/en-US/docs/Web/API/Node/baseURI
[gracefully degrade]: https://developer.mozilla.org/en-US/docs/Glossary/Graceful_degradation
@seanpdoyle seanpdoyle force-pushed the turbo-frame-reload-without-src-attribute branch from 9a05f66 to e4da725 Compare November 17, 2025 15:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants