Skip to content

Could turbo-frame elements dispatch turbo events during their navigation lifecycle? #54

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

Closed
seanpdoyle opened this issue Dec 29, 2020 · 1 comment

Comments

@seanpdoyle
Copy link
Contributor

seanpdoyle commented Dec 29, 2020

As discussed in Are transitions and animations on Hotwire roadmap?, @sstephenson mentions that there would be utility in exposing appropriate seams to consumer applications.

One potential solution could involve dispatching the existing turbo:-prefixed events as a <turbo-frame> element progresses through a navigation. Those events could be dispatched and bubble up from the turbo-frame element, making the frame available as the CustomEvent.target when appropriate. Page-level turbo:-prefixed events would still dispatch off the document element.

In addition to the value it would bring for ancestors to be notified of various lifecycle hooks, it could also enable the cancelation of turbo-frame visitations, and could expose timing metrics as part of frame-scoped turbo:load events.

From a technical perspective, the events could be dispatched from the various empty FrameController hooks:

requestStarted(request: FetchRequest) {
this.element.setAttribute("busy", "")
}
requestPreventedHandlingResponse(request: FetchRequest, response: FetchResponse) {
this.resolveVisitPromise()
}
async requestSucceededWithResponse(request: FetchRequest, response: FetchResponse) {
await this.loadResponse(response)
this.resolveVisitPromise()
}
requestFailedWithResponse(request: FetchRequest, response: FetchResponse) {
console.error(response)
this.resolveVisitPromise()
}
requestErrored(request: FetchRequest, error: Error) {
console.error(error)
this.resolveVisitPromise()
}
requestFinished(request: FetchRequest) {
this.element.removeAttribute("busy")
}
formSubmissionStarted(formSubmission: FormSubmission) {
}
formSubmissionSucceededWithResponse(formSubmission: FormSubmission, response: FetchResponse) {
const frame = this.findFrameElement(formSubmission.formElement)
frame.controller.loadResponse(response)
}
formSubmissionFailedWithResponse(formSubmission: FormSubmission, fetchResponse: FetchResponse) {
}
formSubmissionErrored(formSubmission: FormSubmission, error: Error) {
}
formSubmissionFinished(formSubmission: FormSubmission) {
}

Alternatively, a FrameController could be provided with its own Session instance scoped to the <turbo-frame> element (as opposed to the current implementation which operates on the document.documentElement element).

In theory, there could be a lot of value in unifying the implementations of how a page and a frame navigate, but might involve a larger set of architectural changes. Are there conceptual mismatches between a frame-scoped Session / Navigator pairing and the current implementation which relies on FormInterceptor and LinkInterceptor instances?

@jonsgreen
Copy link

@seanpdoyle Yes, and now I see that hotwired/turbo would have been the right place to have looked for similar issues before posting my issue. We can close this issue though I am still curious about the best way to get involved with any of the hotwire projects.

seanpdoyle added a commit that referenced this issue Dec 30, 2020
Closes #54
Closes hotwired/turbo-rails#56

Dispatch life cycle events from within the `FrameController` and
`FrameRedirector` when a `<turbo-frame>` element is navigated. The
events _do not bubble_ since the conventional (and historical) style of
`turbolinks:` and `turbo:` event listeners has been to declare them on
the page's `document.documentElement`. If the `<turbo-frame>` events
were to bubble, either _all_ application-land event listeners would be
required to change, or else they would fire multiple times as the event
bubbled up the document.

Some tangential changes:

* Change `dispatch` default `{ bubbles: true }` value to be an argument
  instead
* Delete `TimingData` in favor of `TimingMetrics` and `TimingMetric`
seanpdoyle added a commit that referenced this issue Dec 30, 2020
Closes #54
Closes hotwired/turbo-rails#56

Dispatch life cycle events from within the `FrameController` and
`FrameRedirector` when a `<turbo-frame>` element is navigated. The
events _do not bubble_ since the conventional (and historical) style of
`turbolinks:` and `turbo:` event listeners has been to declare them on
the page's `document.documentElement`. If the `<turbo-frame>` events
were to bubble, either _all_ application-land event listeners would be
required to change, or else they would fire multiple times as the event
bubbled up the document.

Some tangential changes:

* Change `dispatch` default `{ bubbles: true }` value to be an argument
  instead
* Delete `TimingData` in favor of `TimingMetrics` and `TimingMetric`
seanpdoyle added a commit that referenced this issue Feb 4, 2021
Closes #54
Closes hotwired/turbo-rails#56

Dispatch `turbo:frame-*` and `turbo:before-frame-*` life cycle events
from within the `FrameController` and `FrameRedirector` when a
`<turbo-frame>` element is navigated. The events bubble, and
`turbo:before-frame-visit` is cancellable.

Some tangential changes:
---

* Delete `TimingData` interface in favor of the pre-existing
  `TimingMetrics` and `TimingMetric`

Loose ends:
---

* In an effort to maintain symmetry with existing `turbo:` events, this
  change includes `turbo:before-frame-cache`, but since Turbo Frames
  does not have any caching infrastructure in place for `<turbo-frame>`
  elements, it fires immediately before `turbo:before-frame-render`.

* Does not dispatch `turbo:submit-start` or `turbo:submit-end` events,
  since they bubble up from the forms themselves.

* Does not dispatch `turbo:before-fetch-request` or
  `turbo:before-fetch-response`, since I couldn't find away to access
  the source element to serve as the dispatcher.

* Dispatches `turbo:frame-visit` and `turbo:frame-load` as part of the
  `visit` call's `resolveVisitPromise` chain. I had attempted to call
  them as part of the `FetchRequestDelegate.request{Started,Finished}`
  methods, but the asynchronicity involved there caused them to fire out
  of order
seanpdoyle added a commit that referenced this issue Feb 4, 2021
Closes #54
Closes hotwired/turbo-rails#56

Dispatch `turbo:frame-*` and `turbo:before-frame-*` life cycle events
from within the `FrameController` and `FrameRedirector` when a
`<turbo-frame>` element is navigated. The events bubble, and
`turbo:before-frame-visit` is cancellable.

Some tangential changes:
---

* Delete `TimingData` interface in favor of the pre-existing
  `TimingMetrics` and `TimingMetric`

Loose ends:
---

* In an effort to maintain symmetry with existing `turbo:` events, this
  change includes `turbo:before-frame-cache`, but since Turbo Frames
  does not have any caching infrastructure in place for `<turbo-frame>`
  elements, it fires immediately before `turbo:before-frame-render`.

* Does not dispatch `turbo:submit-start` or `turbo:submit-end` events,
  since they bubble up from the forms themselves.

* Does not dispatch `turbo:before-fetch-request` or
  `turbo:before-fetch-response`, since I couldn't find away to access
  the source element to serve as the dispatcher.

* Dispatches `turbo:frame-visit` and `turbo:frame-load` as part of the
  `visit` call's `resolveVisitPromise` chain. I had attempted to call
  them as part of the `FetchRequestDelegate.request{Started,Finished}`
  methods, but the asynchronicity involved there caused them to fire out
  of order
seanpdoyle added a commit that referenced this issue Feb 12, 2021
Closes #54
Closes hotwired/turbo-rails#56

Dispatch `turbo:frame-*` and `turbo:before-frame-*` life cycle events
from within the `FrameController` and `FrameRedirector` when a
`<turbo-frame>` element is navigated. The events bubble, and
`turbo:before-frame-visit` is cancellable.

Some tangential changes:
---

* Delete `TimingData` interface in favor of the pre-existing
  `TimingMetrics` and `TimingMetric`

Loose ends:
---

* In an effort to maintain symmetry with existing `turbo:` events, this
  change includes `turbo:before-frame-cache`, but since Turbo Frames
  does not have any caching infrastructure in place for `<turbo-frame>`
  elements, it fires immediately before `turbo:before-frame-render`.

* Does not dispatch `turbo:submit-start` or `turbo:submit-end` events,
  since they bubble up from the forms themselves.

* Does not dispatch `turbo:before-fetch-request` or
  `turbo:before-fetch-response`, since I couldn't find away to access
  the source element to serve as the dispatcher.

* Dispatches `turbo:frame-visit` and `turbo:frame-load` as part of the
  `visit` call's `resolveVisitPromise` chain. I had attempted to call
  them as part of the `FetchRequestDelegate.request{Started,Finished}`
  methods, but the asynchronicity involved there caused them to fire out
  of order
seanpdoyle added a commit that referenced this issue Mar 6, 2021
Closes #54
Closes hotwired/turbo-rails#56

Dispatch `turbo:frame-*` and `turbo:before-frame-*` life cycle events
from within the `FrameController` and `FrameRedirector` when a
`<turbo-frame>` element is navigated. The events bubble, and
`turbo:before-frame-visit` is cancellable.

Some tangential changes:
---

* Delete `TimingData` interface in favor of the pre-existing
  `TimingMetrics` and `TimingMetric`

Loose ends:
---

* In an effort to maintain symmetry with existing `turbo:` events, this
  change includes `turbo:before-frame-cache`, but since Turbo Frames
  does not have any caching infrastructure in place for `<turbo-frame>`
  elements, it fires immediately before `turbo:before-frame-render`.

* Does not dispatch `turbo:submit-start` or `turbo:submit-end` events,
  since they bubble up from the forms themselves.

* Does not dispatch `turbo:before-fetch-request` or
  `turbo:before-fetch-response`, since I couldn't find away to access
  the source element to serve as the dispatcher.

* Dispatches `turbo:frame-visit` and `turbo:frame-load` as part of the
  `visit` call's `resolveVisitPromise` chain. I had attempted to call
  them as part of the `FetchRequestDelegate.request{Started,Finished}`
  methods, but the asynchronicity involved there caused them to fire out
  of order
seanpdoyle added a commit that referenced this issue Apr 14, 2021
Closes #54
Closes hotwired/turbo-rails#56

Dispatch `turbo:frame-load` lifecycle event when `<turbo-frame>` element
is navigated and finishes loading. The events bubble up, with the
`<turbo-frame>` element as the target.

Originally, this pull request involved numerous events, but in the
spirit of experimentation, we'll start with the one and see if others
are necessary.
kapantzak pushed a commit to kapantzak/turbo that referenced this issue Aug 25, 2021
Closes hotwired#54
Closes hotwired/turbo-rails#56

Dispatch `turbo:frame-load` lifecycle event when `<turbo-frame>` element
is navigated and finishes loading. The events bubble up, with the
`<turbo-frame>` element as the target.

Originally, this pull request involved numerous events, but in the
spirit of experimentation, we'll start with the one and see if others
are necessary.
@dhh dhh closed this as completed in 84b0a89 Aug 25, 2021
Challenge-Guy pushed a commit to Challenge-Guy/turbo-cfm1 that referenced this issue Mar 8, 2025
…327)

* Fire 'turbo:after-fetch-render' event after turbo frame renders the view

* fixup! Fire 'turbo:after-fetch-render' event after turbo frame renders the view

* Change 'turbo:after-fetch-render' event to 'turbo:frame-render'

* Implement notifyApplicationAfterFrameRender in order to dispatch the 'turbo:frame-render' event

* Dispatch `turbo:frame-load` on turbo-frame

Closes hotwired/turbo#54
Closes hotwired/turbo-rails#56

Dispatch `turbo:frame-load` lifecycle event when `<turbo-frame>` element
is navigated and finishes loading. The events bubble up, with the
`<turbo-frame>` element as the target.

Originally, this pull request involved numerous events, but in the
spirit of experimentation, we'll start with the one and see if others
are necessary.

* fixup! Dispatch `turbo:frame-load` on turbo-frame

Co-authored-by: John Kapantzakis <[email protected]>
Co-authored-by: David Heinemeier Hansson <[email protected]>
Co-authored-by: Sean Doyle <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging a pull request may close this issue.

2 participants