Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support FrameElement.reload() without an initial [src] attribute #1004

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

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
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 3582ccf to 9a05f66 Compare November 17, 2023 14:36
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.

2 participants