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

Better developer experience when lazy loading turbo-frame doesnt find a matching frame to replace #31

Closed
swanson opened this issue Dec 23, 2020 · 11 comments

Comments

@swanson
Copy link

swanson commented Dec 23, 2020

I got tripped up for a bit when trying to add a turbo-frame with lazy loading. It seemed like it wasn't working correctly, but the issue was a mistake on my part: I didn't properly add the frame to the response I was loading. There are instructions for doing so in the guide, but I can see this tripping up people.

My workflow went like this:

  • Build a page
  • Go to add a turbo-frame to lazy load the page somewhere else in the app
  • Expected: to lazy load the page with the application layout (nested nav bar, etc), then I would know it was working and add the frame to slice out the content I didn't want duplicated
  • Actual: the lazy loading frame did nothing and seemed to not work

Until I looked at the Rails logs to see the request was being made, I had no indication of what was wrong.

Would it be possible to console error or warn the developer if Turbo wasn't able to find a suitable replacement frame?

@Intrepidd
Copy link
Contributor

Not to mention it looks quite easy to push a production breaking bug by unknowingly changing a link to something that won't return a frame. Without extensive integration testing this could go unnoticed.

I suggest to throw an error when an expected frame is not found so it can at least be reported through error monitoring.

@JulianFeinauer
Copy link

The easiest solution would be to push an error log in the js console, right? As soon as we get a 404 / 500 response on the AJAX request, or?

@swanson
Copy link
Author

swanson commented Jan 8, 2021

Related #94

@sstephenson
Copy link
Contributor

(Sorry for not chiming in here as well! ✌️)

@swanson
Copy link
Author

swanson commented Jan 8, 2021

All good ✌️

@sstephenson
Copy link
Contributor

As of 3263389 Turbo empties the frame and logs an error to the console when a matching frame is missing.

Thank you for the suggestion!

@brunodrugowick
Copy link

I had a working sample that stopped after this v7.0.0-beta.3 release. I'm using the latest over CDN so I noticed.

If I change back to any prior v7.0.0-beta version my code works. On the v7.0.0-beta.3 I get:

Response has no matching <turbo-frame id="beverages_list"> element turbo.js:740:13
    extractForeignFrameElement https://cdn.skypack.dev/-/@hotwired/turbo@v7.0.0-beta.3-tvFsO8IW4m2YjmlcLZZq/dist=es2020/@hotwired/turbo.js:740
    loadResponse https://cdn.skypack.dev/-/@hotwired/turbo@v7.0.0-beta.3-tvFsO8IW4m2YjmlcLZZq/dist=es2020/@hotwired/turbo.js:641
    formSubmissionSucceededWithResponse https://cdn.skypack.dev/-/@hotwired/turbo@v7.0.0-beta.3-tvFsO8IW4m2YjmlcLZZq/dist=es2020/@hotwired/turbo.js:700
    requestSucceededWithResponse https://cdn.skypack.dev/-/@hotwired/turbo@v7.0.0-beta.3-tvFsO8IW4m2YjmlcLZZq/dist=es2020/@hotwired/turbo.js:483
    receive https://cdn.skypack.dev/-/@hotwired/turbo@v7.0.0-beta.3-tvFsO8IW4m2YjmlcLZZq/dist=es2020/@hotwired/turbo.js:351
    perform https://cdn.skypack.dev/-/@hotwired/turbo@v7.0.0-beta.3-tvFsO8IW4m2YjmlcLZZq/dist=es2020/@hotwired/turbo.js:337
    start https://cdn.skypack.dev/-/@hotwired/turbo@v7.0.0-beta.3-tvFsO8IW4m2YjmlcLZZq/dist=es2020/@hotwired/turbo.js:445
    formSubmissionIntercepted https://cdn.skypack.dev/-/@hotwired/turbo@v7.0.0-beta.3-tvFsO8IW4m2YjmlcLZZq/dist=es2020/@hotwired/turbo.js:669
    submitBubbled https://cdn.skypack.dev/-/@hotwired/turbo@v7.0.0-beta.3-tvFsO8IW4m2YjmlcLZZq/dist=es2020/@hotwired/turbo.js:538
    (Async: EventListener.handleEvent)
    start https://cdn.skypack.dev/-/@hotwired/turbo@v7.0.0-beta.3-tvFsO8IW4m2YjmlcLZZq/dist=es2020/@hotwired/turbo.js:546
    connect https://cdn.skypack.dev/-/@hotwired/turbo@v7.0.0-beta.3-tvFsO8IW4m2YjmlcLZZq/dist=es2020/@hotwired/turbo.js:606
    connectedCallback https://cdn.skypack.dev/-/@hotwired/turbo@v7.0.0-beta.3-tvFsO8IW4m2YjmlcLZZq/dist=es2020/@hotwired/turbo.js:55
    <anonymous> https://cdn.skypack.dev/-/@hotwired/turbo@v7.0.0-beta.3-tvFsO8IW4m2YjmlcLZZq/dist=es2020/@hotwired/turbo.js:931

I'm not sure if I'm doing something wrong (I followed https://turbo.hotwire.dev/handbook/streams) that I didn't notice but I wanted to report early on.

My page looks like this:

<!-- omitted irrelevant stuff -->
<section>
    <h1>Showcase how to add to part of a page</h1>

    <turbo-frame id="beverages_list">
        <form action="/beverages/any" method="post">
            <button type="submit">Add beverage</button>
        </form>
        <div id="beverage_1"><p>1 - Coffee</p></div>
    </turbo-frame>

</section>

And my server response to the POST method is:

<!-- omitted irrelevant stuff -->
<body>
<h1>A Beverage (page or Turbo component)</h1>
<turbo-stream action="append" target="beverages_list">
    <template>
        <div id="beverage_8">
            <p>
                <span>8</span> - <span>Coffee</span>
            </p>
        </div>
    </template>
</turbo-stream>
</body>

@brunodrugowick
Copy link

@sstephenson , this is a sample of the situation I mentioned above: https://hotwire-v7b3-error.herokuapp.com/

@viktorvan
Copy link

I am having the same issue as @brunodrugowick, for any beta > v7.0.0-beta.2, I can no longer respond with a TurboStream. I get the same "Response has no matching element" error.

@brunodrugowick
Copy link

Since this is closed and @viktorvan is having the same problem I opened a new issue.

@brunodrugowick
Copy link

@viktorvan , hope this helps >>> #144 (comment)

seanpdoyle added a commit to seanpdoyle/turbo that referenced this issue Nov 14, 2021
Closes [hotwired#432][]
Follow-up to [hotwired#94][]
Follow-up to [hotwired#31][]

When a response from _within_ a frame is missing a matching frame, fire
the `turbo:frame-missing` event.

There is an existing [contract][] that dictates a request from within a
frame stays within a frame.

However, if an application is interested in reacting to a response
without a frame, dispatch a `turbo:frame-missing` event. The event's
`target` is the `FrameElement`, and the `detail` contains the `url:`
key, and the `response:` key containing:

* `redirected: boolean`
* `responseHTML: string`
* `statusCode: number`

Event listeners for `turbo:frame-missing` can forward the `detail`
directly to a `Turbo.visit` call:

```js
addEventListener("turbo:frame-missing", ({ target, detail: { url, response } }) => {
  // the details of `shouldRedirectOnMissingFrame(element: FrameElement)`
  // are up to the application to decide
  if (shouldRedirectOnMissingFrame(target)) {
    const visitOptions = response
    Turbo.visit(url, visitOptions)
  }
})

[contract]: hotwired#94 (comment)
[hotwired#432]: hotwired#432
[hotwired#94]: hotwired#94
[hotwired#31]: hotwired#31
seanpdoyle added a commit to seanpdoyle/turbo that referenced this issue Nov 14, 2021
Closes [hotwired#432][]
Follow-up to [hotwired#94][]
Follow-up to [hotwired#31][]

When a response from _within_ a frame is missing a matching frame, fire
the `turbo:frame-missing` event.

There is an existing [contract][] that dictates a request from within a
frame stays within a frame.

However, if an application is interested in reacting to a response
without a frame, dispatch a `turbo:frame-missing` event. The event's
`target` is the `FrameElement`, and the `detail` contains the `url:`
key, and the `response:` key containing:

* `redirected: boolean`
* `responseHTML: string`
* `statusCode: number`

Event listeners for `turbo:frame-missing` can forward the `detail`
directly to a `Turbo.visit` call:

```js
addEventListener("turbo:frame-missing", ({ target, detail: { url, response } }) => {
  // the details of `shouldRedirectOnMissingFrame(element: FrameElement)`
  // are up to the application to decide
  if (shouldRedirectOnMissingFrame(target)) {
    const visitOptions = response
    Turbo.visit(url, visitOptions)
  }
})
```

[contract]: hotwired#94 (comment)
[hotwired#432]: hotwired#432
[hotwired#94]: hotwired#94
[hotwired#31]: hotwired#31
seanpdoyle added a commit to seanpdoyle/turbo that referenced this issue Nov 14, 2021
Closes [hotwired#432][]
Follow-up to [hotwired#94][]
Follow-up to [hotwired#31][]

When a response from _within_ a frame is missing a matching frame, fire
the `turbo:frame-missing` event.

There is an existing [contract][] that dictates a request from within a
frame stays within a frame.

However, if an application is interested in reacting to a response
without a frame, dispatch a `turbo:frame-missing` event. The event's
`target` is the `FrameElement`, and the `detail` contains the `url:`
key, and the `response:` key containing:

* `redirected: boolean`
* `responseHTML: string`
* `statusCode: number`

Event listeners for `turbo:frame-missing` can forward the `detail`
directly to a `Turbo.visit` call:

```js
addEventListener("turbo:frame-missing", ({ target, detail: { url, response } }) => {
  // the details of `shouldRedirectOnMissingFrame(element: FrameElement)`
  // are up to the application to decide
  if (shouldRedirectOnMissingFrame(target)) {
    const visitOptions = response
    Turbo.visit(url, visitOptions)
  }
})
```

[contract]: hotwired#94 (comment)
[hotwired#432]: hotwired#432
[hotwired#94]: hotwired#94
[hotwired#31]: hotwired#31
seanpdoyle added a commit to seanpdoyle/turbo that referenced this issue Nov 14, 2021
Closes [hotwired#432][]
Follow-up to [hotwired#94][]
Follow-up to [hotwired#31][]

When a response from _within_ a frame is missing a matching frame, fire
the `turbo:frame-missing` event.

There is an existing [contract][] that dictates a request from within a
frame stays within a frame.

However, if an application is interested in reacting to a response
without a frame, dispatch a `turbo:frame-missing` event. The event's
`target` is the `FrameElement`, and the `detail` contains the `url:`
key, and the `response:` key containing:

* `redirected: boolean`
* `responseHTML: string`
* `statusCode: number`

Event listeners for `turbo:frame-missing` can forward the `detail`
directly to a `Turbo.visit` call:

```js
addEventListener("turbo:frame-missing", ({ target, detail: { url, response } }) => {
  // the details of `shouldRedirectOnMissingFrame(element: FrameElement)`
  // are up to the application to decide
  if (shouldRedirectOnMissingFrame(target)) {
    Turbo.visit(url, { response })
  }
})
```

[contract]: hotwired#94 (comment)
[hotwired#432]: hotwired#432
[hotwired#94]: hotwired#94
[hotwired#31]: hotwired#31
seanpdoyle added a commit to seanpdoyle/turbo that referenced this issue Nov 15, 2021
Closes [hotwired#432][]
Follow-up to [hotwired#94][]
Follow-up to [hotwired#31][]

When a response from _within_ a frame is missing a matching frame, fire
the `turbo:frame-missing` event.

There is an existing [contract][] that dictates a request from within a
frame stays within a frame.

However, if an application is interested in reacting to a response
without a frame, dispatch a `turbo:frame-missing` event. The event's
`target` is the `FrameElement`, and the `detail` contains the
`fetchResponse:` key. To transform the `FetchResponse` into a `Visit`,
clients can extract:

* `redirected: boolean`
* `statusCode: number`
* `responseHTML: Promise<string>`

Event listeners for `turbo:frame-missing` can forward the `detail`
directly to a `Turbo.visit` call:

```js
addEventListener("turbo:frame-missing", async ({ target, detail: { fetchResponse } }) => {
  // the details of `shouldRedirectOnMissingFrame(element: FrameElement)`
  // are up to the application to decide
  if (shouldRedirectOnMissingFrame(target)) {
    const { location, redirected, statusCode, responseHTML } = fetchResponse
    const response = { redirected, statusCode, responseHTML: await responseHTML }

    Turbo.visit(location, { response })
  }
})
```

[contract]: hotwired#94 (comment)
[hotwired#432]: hotwired#432
[hotwired#94]: hotwired#94
[hotwired#31]: hotwired#31
seanpdoyle added a commit to seanpdoyle/turbo that referenced this issue Nov 15, 2021
Closes [hotwired#432][]
Follow-up to [hotwired#94][]
Follow-up to [hotwired#31][]

When a response from _within_ a frame is missing a matching frame, fire
the `turbo:frame-missing` event.

There is an existing [contract][] that dictates a request from within a
frame stays within a frame.

However, if an application is interested in reacting to a response
without a frame, dispatch a `turbo:frame-missing` event. The event's
`target` is the `FrameElement`, and the `detail` contains the
`fetchResponse:` key. To transform the `FetchResponse` into a `Visit`,
clients can extract:

* `redirected: boolean`
* `statusCode: number`
* `responseHTML: Promise<string>`

Event listeners for `turbo:frame-missing` can forward the `detail`
directly to a `Turbo.visit` call:

```js
addEventListener("turbo:frame-missing", async ({ target, detail: { fetchResponse } }) => {
  // the details of `shouldRedirectOnMissingFrame(element: FrameElement)`
  // are up to the application to decide
  if (shouldRedirectOnMissingFrame(target)) {
    const { location, redirected, statusCode, responseHTML } = fetchResponse
    const response = { redirected, statusCode, responseHTML: await responseHTML }

    Turbo.visit(location, { response })
  }
})
```

[contract]: hotwired#94 (comment)
[hotwired#432]: hotwired#432
[hotwired#94]: hotwired#94
[hotwired#31]: hotwired#31
seanpdoyle added a commit to seanpdoyle/turbo that referenced this issue Nov 18, 2021
Closes [hotwired#432][]
Follow-up to [hotwired#94][]
Follow-up to [hotwired#31][]

When a response from _within_ a frame is missing a matching frame, fire
the `turbo:frame-missing` event.

There is an existing [contract][] that dictates a request from within a
frame stays within a frame.

However, if an application is interested in reacting to a response
without a frame, dispatch a `turbo:frame-missing` event. The event's
`target` is the `FrameElement`, and the `detail` contains the
`fetchResponse:` key.

The `event.detail.visit` key provides handlers with a way to transform
the `fetchResponse` into a `Turbo.visit()` call without any knowledge of
the internal structure or logic necessary to do so. Event listeners for
`turbo:frame-missing` can invoke the `event.detail.visit` directly to
invoke `Turbo.visit()` behind the scenes. The yielded `visit()` function
accepts `Partial<VisitOptions>` as an optional argument:

```js
addEventListener("turbo:frame-missing", async ({ target, detail: { fetchResponse, visit } }) => {
  // the details of `shouldRedirectOnMissingFrame(element: FrameElement)`
  // are up to the application to decide
  if (shouldRedirectOnMissingFrame(target)) {
    visit({ action: "replace" })
  }
})
```

[contract]: hotwired#94 (comment)
[hotwired#432]: hotwired#432
[hotwired#94]: hotwired#94
[hotwired#31]: hotwired#31
seanpdoyle added a commit to seanpdoyle/turbo that referenced this issue Nov 18, 2021
Closes [hotwired#432][]
Follow-up to [hotwired#94][]
Follow-up to [hotwired#31][]

When a response from _within_ a frame is missing a matching frame, fire
the `turbo:frame-missing` event.

There is an existing [contract][] that dictates a request from within a
frame stays within a frame.

However, if an application is interested in reacting to a response
without a frame, dispatch a `turbo:frame-missing` event. The event's
`target` is the `FrameElement`, and the `detail` contains the
`fetchResponse:` key.

The `event.detail.visit` key provides handlers with a way to transform
the `fetchResponse` into a `Turbo.visit()` call without any knowledge of
the internal structure or logic necessary to do so. Event listeners for
`turbo:frame-missing` can invoke the `event.detail.visit` directly to
invoke `Turbo.visit()` behind the scenes. The yielded `visit()` function
accepts `Partial<VisitOptions>` as an optional argument:

```js
addEventListener("turbo:frame-missing", (event) => {
  // the details of `shouldRedirectOnMissingFrame(element: FrameElement)`
  // are up to the application to decide
  if (shouldRedirectOnMissingFrame(event.target)) {
    const { detail: { fetchResponse, visit } } = event

    event.preventDefault()
    visit()
  }
})
```

The event listener is also a good opportunity to change the
`<turbo-frame>` element itself to prevent future missing responses.

For example, if the reason the frame is missing is access (an expired
session, for example), the call to `visit()` can be made with `{ action:
"replace" }` to remove the current page from Turbo's page history.

Similarly, if the reason for the missing frame is particular to the page
referenced by the element's `[src]` attribute, this is an opportunity to
change that attribute (calling `event.target.removeAttribute("src")`,
for example) before navigating away so that re-visiting the page by
navigating backward in the Browser's history doesn't automatically load
the frame and re-trigger another `turbo:frame-missing` event.

[contract]: hotwired#94 (comment)
[hotwired#432]: hotwired#432
[hotwired#94]: hotwired#94
[hotwired#31]: hotwired#31

cancelable event
seanpdoyle added a commit to seanpdoyle/turbo that referenced this issue Nov 18, 2021
Closes [hotwired#432][]
Follow-up to [hotwired#94][]
Follow-up to [hotwired#31][]

When a response from _within_ a frame is missing a matching frame, fire
the `turbo:frame-missing` event.

There is an existing [contract][] that dictates a request from within a
frame stays within a frame.

However, if an application is interested in reacting to a response
without a frame, dispatch a `turbo:frame-missing` event. The event's
`target` is the `FrameElement`, and the `detail` contains the
`fetchResponse:` key.

The `event.detail.visit` key provides handlers with a way to transform
the `fetchResponse` into a `Turbo.visit()` call without any knowledge of
the internal structure or logic necessary to do so. Event listeners for
`turbo:frame-missing` can invoke the `event.detail.visit` directly to
invoke `Turbo.visit()` behind the scenes. The yielded `visit()` function
accepts `Partial<VisitOptions>` as an optional argument:

```js
addEventListener("turbo:frame-missing", (event) => {
  // the details of `shouldRedirectOnMissingFrame(element: FrameElement)`
  // are up to the application to decide
  if (shouldRedirectOnMissingFrame(event.target)) {
    const { detail: { fetchResponse, visit } } = event

    event.preventDefault()
    visit()
  }
})
```

The event listener is also a good opportunity to change the
`<turbo-frame>` element itself to prevent future missing responses.

For example, if the reason the frame is missing is access (an expired
session, for example), the call to `visit()` can be made with `{ action:
"replace" }` to remove the current page from Turbo's page history.

Similarly, if the reason for the missing frame is particular to the page
referenced by the element's `[src]` attribute, this is an opportunity to
change that attribute (calling `event.target.removeAttribute("src")`,
for example) before navigating away so that re-visiting the page by
navigating backward in the Browser's history doesn't automatically load
the frame and re-trigger another `turbo:frame-missing` event.

[contract]: hotwired#94 (comment)
[hotwired#432]: hotwired#432
[hotwired#94]: hotwired#94
[hotwired#31]: hotwired#31
seanpdoyle added a commit to seanpdoyle/turbo that referenced this issue Nov 18, 2021
Closes [hotwired#432][]
Follow-up to [hotwired#94][]
Follow-up to [hotwired#31][]

When a response from _within_ a frame is missing a matching frame, fire
the `turbo:frame-missing` event.

There is an existing [contract][] that dictates a request from within a
frame stays within a frame.

However, if an application is interested in reacting to a response
without a frame, dispatch a `turbo:frame-missing` event. The event's
`target` is the `FrameElement`, and the `detail` contains the
`fetchResponse:` key.

The `event.detail.visit` key provides handlers with a way to transform
the `fetchResponse` into a `Turbo.visit()` call without any knowledge of
the internal structure or logic necessary to do so. Event listeners for
`turbo:frame-missing` can invoke the `event.detail.visit` directly to
invoke `Turbo.visit()` behind the scenes. The yielded `visit()` function
accepts `Partial<VisitOptions>` as an optional argument:

```js
addEventListener("turbo:frame-missing", (event) => {
  // the details of `shouldRedirectOnMissingFrame(element: FrameElement)`
  // are up to the application to decide
  if (shouldRedirectOnMissingFrame(event.target)) {
    const { detail: { fetchResponse, visit } } = event

    event.preventDefault()
    visit()
  }
})
```

The event listener is also a good opportunity to change the
`<turbo-frame>` element itself to prevent future missing responses.

For example, if the reason the frame is missing is access (an expired
session, for example), the call to `visit()` can be made with `{ action:
"replace" }` to remove the current page from Turbo's page history.

Similarly, if the reason for the missing frame is particular to the page
referenced by the element's `[src]` attribute, this is an opportunity to
change that attribute (calling `event.target.removeAttribute("src")`,
for example) before navigating away so that re-visiting the page by
navigating backward in the Browser's history doesn't automatically load
the frame and re-trigger another `turbo:frame-missing` event.

[contract]: hotwired#94 (comment)
[hotwired#432]: hotwired#432
[hotwired#94]: hotwired#94
[hotwired#31]: hotwired#31
seanpdoyle added a commit to seanpdoyle/turbo that referenced this issue Nov 18, 2021
Closes [hotwired#432][]
Follow-up to [hotwired#94][]
Follow-up to [hotwired#31][]

When a response from _within_ a frame is missing a matching frame, fire
the `turbo:frame-missing` event.

There is an existing [contract][] that dictates a request from within a
frame stays within a frame.

However, if an application is interested in reacting to a response
without a frame, dispatch a `turbo:frame-missing` event. The event's
`target` is the `FrameElement`, and the `detail` contains the
`fetchResponse:` key.

The `event.detail.visit` key provides handlers with a way to transform
the `fetchResponse` into a `Turbo.visit()` call without any knowledge of
the internal structure or logic necessary to do so. Event listeners for
`turbo:frame-missing` can invoke the `event.detail.visit` directly to
invoke `Turbo.visit()` behind the scenes. The yielded `visit()` function
accepts `Partial<VisitOptions>` as an optional argument:

```js
addEventListener("turbo:frame-missing", (event) => {
  // the details of `shouldRedirectOnMissingFrame(element: FrameElement)`
  // are up to the application to decide
  if (shouldRedirectOnMissingFrame(event.target)) {
    const { detail: { fetchResponse, visit } } = event

    event.preventDefault()
    visit()
  }
})
```

The event listener is also a good opportunity to change the
`<turbo-frame>` element itself to prevent future missing responses.

For example, if the reason the frame is missing is access (an expired
session, for example), the call to `visit()` can be made with `{ action:
"replace" }` to remove the current page from Turbo's page history.

Similarly, if the reason for the missing frame is particular to the page
referenced by the element's `[src]` attribute, this is an opportunity to
change that attribute (calling `event.target.removeAttribute("src")`,
for example) before navigating away so that re-visiting the page by
navigating backward in the Browser's history doesn't automatically load
the frame and re-trigger another `turbo:frame-missing` event.

[contract]: hotwired#94 (comment)
[hotwired#432]: hotwired#432
[hotwired#94]: hotwired#94
[hotwired#31]: hotwired#31
seanpdoyle added a commit to seanpdoyle/turbo that referenced this issue Nov 21, 2021
Closes [hotwired#432][]
Follow-up to [hotwired#94][]
Follow-up to [hotwired#31][]

When a response from _within_ a frame is missing a matching frame, fire
the `turbo:frame-missing` event.

There is an existing [contract][] that dictates a request from within a
frame stays within a frame.

However, if an application is interested in reacting to a response
without a frame, dispatch a `turbo:frame-missing` event. The event's
`target` is the `FrameElement`, and the `detail` contains the
`fetchResponse:` key.

The `event.detail.visit` key provides handlers with a way to transform
the `fetchResponse` into a `Turbo.visit()` call without any knowledge of
the internal structure or logic necessary to do so. Event listeners for
`turbo:frame-missing` can invoke the `event.detail.visit` directly to
invoke `Turbo.visit()` behind the scenes. The yielded `visit()` function
accepts `Partial<VisitOptions>` as an optional argument:

```js
addEventListener("turbo:frame-missing", (event) => {
  // the details of `shouldRedirectOnMissingFrame(element: FrameElement)`
  // are up to the application to decide
  if (shouldRedirectOnMissingFrame(event.target)) {
    const { detail: { fetchResponse, visit } } = event

    event.preventDefault()
    visit()
  }
})
```

The event listener is also a good opportunity to change the
`<turbo-frame>` element itself to prevent future missing responses.

For example, if the reason the frame is missing is access (an expired
session, for example), the call to `visit()` can be made with `{ action:
"replace" }` to remove the current page from Turbo's page history.

Similarly, if the reason for the missing frame is particular to the page
referenced by the element's `[src]` attribute, this is an opportunity to
change that attribute (calling `event.target.removeAttribute("src")`,
for example) before navigating away so that re-visiting the page by
navigating backward in the Browser's history doesn't automatically load
the frame and re-trigger another `turbo:frame-missing` event.

[contract]: hotwired#94 (comment)
[hotwired#432]: hotwired#432
[hotwired#94]: hotwired#94
[hotwired#31]: hotwired#31
seanpdoyle added a commit to seanpdoyle/turbo that referenced this issue Aug 1, 2022
Closes [hotwired#432][]
Follow-up to [hotwired#94][]
Follow-up to [hotwired#31][]

When a response from _within_ a frame is missing a matching frame, fire
the `turbo:frame-missing` event.

There is an existing [contract][] that dictates a request from within a
frame stays within a frame.

However, if an application is interested in reacting to a response
without a frame, dispatch a `turbo:frame-missing` event. The event's
`target` is the `FrameElement`, and the `detail` contains the
`fetchResponse:` key. Unless it's canceled (by calling
`event.preventDefault()`), Turbo Drive will visit the frame's URL as a
full-page navigation.

The event listener is also a good opportunity to change the
`<turbo-frame>` element itself to prevent future missing responses.

For example, if the reason the frame is missing is access (an expired
session, for example), the call to `visit()` can be made with `{ action:
"replace" }` to remove the current page from Turbo's page history.

[contract]: hotwired#94 (comment)
[hotwired#432]: hotwired#432
[hotwired#94]: hotwired#94
[hotwired#31]: hotwired#31
seanpdoyle added a commit to seanpdoyle/turbo that referenced this issue Aug 1, 2022
Closes [hotwired#432][]
Follow-up to [hotwired#94][]
Follow-up to [hotwired#31][]

When a response from _within_ a frame is missing a matching frame, fire
the `turbo:frame-missing` event.

There is an existing [contract][] that dictates a request from within a
frame stays within a frame.

However, if an application is interested in reacting to a response
without a frame, dispatch a `turbo:frame-missing` event. The event's
`target` is the `FrameElement`, and the `detail` contains the
`fetchResponse:` key. Unless it's canceled (by calling
`event.preventDefault()`), Turbo Drive will visit the frame's URL as a
full-page navigation.

The event listener is also a good opportunity to change the
`<turbo-frame>` element itself to prevent future missing responses.

For example, if the reason the frame is missing is access (an expired
session, for example), the call to `visit()` can be made with `{ action:
"replace" }` to remove the current page from Turbo's page history.

[contract]: hotwired#94 (comment)
[hotwired#432]: hotwired#432
[hotwired#94]: hotwired#94
[hotwired#31]: hotwired#31
dhh added a commit that referenced this issue Aug 3, 2022
* Introduce `turbo:frame-missing` event

Closes [#432][]
Follow-up to [#94][]
Follow-up to [#31][]

When a response from _within_ a frame is missing a matching frame, fire
the `turbo:frame-missing` event.

There is an existing [contract][] that dictates a request from within a
frame stays within a frame.

However, if an application is interested in reacting to a response
without a frame, dispatch a `turbo:frame-missing` event. The event's
`target` is the `FrameElement`, and the `detail` contains the
`fetchResponse:` key. Unless it's canceled (by calling
`event.preventDefault()`), Turbo Drive will visit the frame's URL as a
full-page navigation.

The event listener is also a good opportunity to change the
`<turbo-frame>` element itself to prevent future missing responses.

For example, if the reason the frame is missing is access (an expired
session, for example), the call to `visit()` can be made with `{ action:
"replace" }` to remove the current page from Turbo's page history.

[contract]: #94 (comment)
[#432]: #432
[#94]: #94
[#31]: #31

* re-run CI

* issue a new request for the full page of content

* Add console warning if a full-page visit is triggered as a result of missing matching frame

Co-authored-by: David Heinemeier Hansson <david@hey.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

6 participants