Skip to content

Commit

Permalink
Render across <template> element boundaries
Browse files Browse the repository at this point in the history
Take, for example, a `<template>` that nests other `<template>`
elements:

```html
<template>
  <template>
    <div>{{x}}</div>
  </template>
</template>

<script type="module">
  import { TemplateInstance } from "@github/template-parts"

  const template = document.querySelector("template")
  const instance = new TemplateInstance(template, { x: "Hello world" })

  document.body.append(instance)
</script>
```

Prior to this change, the inner `<template>` element (and its child
`<div>`) are unchanged and still render `{{x}}` as a text node.

This change aims to bring support for templating across `<template>`
boundaries. To achieve this behavior, add explicit
[HTMLTemplateElement][] handling to the tree walking. When handling a
`<template>` element, walk its content `DocumentFragment`, treating
variables in the same as way as the outer `<template>` element.

[HTMLTemplateElement]: https://developer.mozilla.org/en-US/docs/Web/API/HTMLTemplateElement
  • Loading branch information
seanpdoyle committed Nov 4, 2024
1 parent 80a59f2 commit e14ddf4
Show file tree
Hide file tree
Showing 2 changed files with 16 additions and 1 deletion.
6 changes: 5 additions & 1 deletion src/template-instance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@ function* collectParts(el: DocumentFragment): Generator<TemplatePart> {
const walker = el.ownerDocument.createTreeWalker(el, NodeFilter.SHOW_TEXT | NodeFilter.SHOW_ELEMENT, null)
let node
while ((node = walker.nextNode())) {
if (node instanceof Element && node.hasAttributes()) {
if (node instanceof HTMLTemplateElement) {
for (const part of collectParts(node.content)) {
yield part
}
} else if (node instanceof Element && node.hasAttributes()) {
for (let i = 0; i < node.attributes.length; i += 1) {
const attr = node.attributes.item(i)
if (attr && attr.value.includes('{{')) {
Expand Down
11 changes: 11 additions & 0 deletions test/template-instance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,17 @@ describe('template-instance', () => {
root.appendChild(instance)
expect(root.innerHTML).to.equal(`<div>Hello world</div>`)
})
it('applies data to nested templated element nodes', () => {
const root = document.createElement('div')
const template = document.createElement('template')
const nestedTemplate = Object.assign(document.createElement('template'), {
innerHTML: '{{x}}'

Check failure on line 34 in test/template-instance.ts

View workflow job for this annotation

GitHub Actions / Test on Node.js

Insert `,`
})
template.append(nestedTemplate)
root.appendChild(new TemplateInstance(template, {x: 'Hello world'}))

expect(root.innerHTML).to.equal('Hello world')
})
it('can render into partial text nodes', () => {
const template = document.createElement('template')
const originalHTML = `Hello {{x}}!`
Expand Down

0 comments on commit e14ddf4

Please sign in to comment.