-
Notifications
You must be signed in to change notification settings - Fork 430
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
Comments
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. |
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? |
Related #94 |
(Sorry for not chiming in here as well! ✌️) |
All good ✌️ |
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! |
I had a working sample that stopped after this If I change back to any prior
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> |
@sstephenson , this is a sample of the situation I mentioned above: https://hotwire-v7b3-error.herokuapp.com/ |
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. |
Since this is closed and @viktorvan is having the same problem I opened a new issue. |
@viktorvan , hope this helps >>> #144 (comment) |
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
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
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
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
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
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
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
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
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
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
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
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
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
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
* 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>
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:
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?
The text was updated successfully, but these errors were encountered: