Skip to content

Commit

Permalink
Query all eager loading turbo frames and patch their innerHTML (#164)
Browse files Browse the repository at this point in the history
* Query all lazy loading turbo frames and patch their innerHTML

* Refactor to try/catch

* Extract graciouslyFetch

* Add recursion for nested eager loaded turbo frames
  • Loading branch information
julianrubisch authored Nov 22, 2021
1 parent 8da9d7c commit 2a77704
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 9 deletions.
51 changes: 43 additions & 8 deletions javascript/elements/updates_for_element.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import CableReady from '..'
import SubscribingElement from './subscribing_element'
import { shouldMorph } from '../morph_callbacks'
import activeElement from '../active_element'
import { debounce, assignFocus, dispatch, handleErrors } from '../utils'
import { debounce, assignFocus, dispatch, graciouslyFetch } from '../utils'

const template = `
<style>
Expand Down Expand Up @@ -61,18 +61,16 @@ export default class UpdatesForElement extends SubscribingElement {
blocks[i].setAttribute('updating', 'updating')

if (!html.hasOwnProperty(url(blocks[i]))) {
const response = await fetch(url(blocks[i]), {
headers: {
'X-Cable-Ready': 'update'
}
const response = await graciouslyFetch(url(blocks[i]), {
'X-Cable-Ready': 'update'
})
.then(handleErrors)
.catch(e => console.error(`Could not fetch ${url(blocks[i])}`))
if (response === undefined) return
html[url(blocks[i])] = await response.text()
}

template.innerHTML = String(html[url(blocks[i])]).trim()

await this.resolveTurboFrames(template.content)

const fragments = template.content.querySelectorAll(query)

if (fragments.length <= i) {
Expand All @@ -99,6 +97,43 @@ export default class UpdatesForElement extends SubscribingElement {
}
}

async resolveTurboFrames (documentFragment) {
const reloadingTurboFrames = [
...documentFragment.querySelectorAll(
'turbo-frame[src]:not([loading="lazy"])'
)
]

return Promise.all(
reloadingTurboFrames.map(frame => {
return new Promise(async resolve => {
const frameResponse = await graciouslyFetch(
frame.getAttribute('src'),
{
'Turbo-Frame': frame.id,
'X-Cable-Ready': 'update'
}
)

const frameTemplate = document.createElement('template')
frameTemplate.innerHTML = await frameResponse.text()

// recurse here to get all nested eager loaded frames
await this.resolveTurboFrames(frameTemplate.content)

documentFragment.querySelector(
`turbo-frame#${frame.id}`
).innerHTML = String(
frameTemplate.content.querySelector(`turbo-frame#${frame.id}`)
.innerHTML
).trim()

resolve()
})
})
)
}

get debounce () {
return this.hasAttribute('debounce')
? parseInt(this.getAttribute('debounce'))
Expand Down
26 changes: 25 additions & 1 deletion javascript/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,29 @@ function handleErrors (response) {
return response
}

// A proxy method to wrap a fetch call in error handling
//
// * url - the URL to fetch
// * additionalHeaders - an object of additional headers passed to fetch
//
async function graciouslyFetch (url, additionalHeaders) {
try {
const response = await fetch(url, {
headers: {
'X-REQUESTED-WITH': 'XmlHttpRequest',
...additionalHeaders
}
})
if (response == undefined) return

handleErrors(response)

return response
} catch (e) {
console.error(`Could not fetch ${url}`)
}
}

export {
isTextInput,
assignFocus,
Expand All @@ -125,5 +148,6 @@ export {
before,
after,
debounce,
handleErrors
handleErrors,
graciouslyFetch
}

0 comments on commit 2a77704

Please sign in to comment.