Skip to content

Commit c468223

Browse files
committed
Remove special treatment for HTMLTemplateElement
Now that `InnerTemplatePart` exists, the Node walker should yield instances of `InnerTemplatePart` instead of collecting nested `<template>` elements based on their attributes. Consumers are free to override the `TemplateInstance` processor to handle `<template>` elements in whichever way they please, while still being able to fall back to the built-in behavior.
1 parent 187af82 commit c468223

File tree

3 files changed

+40
-18
lines changed

3 files changed

+40
-18
lines changed

src/processors.ts

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type {TemplatePart, TemplateTypeInit} from './types.js'
2-
import type {TemplateInstance} from './template-instance.js'
2+
import {TemplateInstance} from './template-instance.js'
33
import {AttributeTemplatePart} from './attribute-template-part.js'
4+
import {InnerTemplatePart} from './inner-template-part.js'
45

56
type PartProcessor = (part: TemplatePart, value: unknown, state: unknown) => void
67

@@ -9,7 +10,9 @@ export function createProcessor(processPart: PartProcessor): TemplateTypeInit {
910
processCallback(_: TemplateInstance, parts: Iterable<TemplatePart>, state: unknown): void {
1011
if (typeof state !== 'object' || !state) return
1112
for (const part of parts) {
12-
if (part.expression in state) {
13+
if (part instanceof InnerTemplatePart) {
14+
processPart(part, part.expression, state)
15+
} else if (part.expression in state) {
1316
const value = (state as Record<string, unknown>)[part.expression] ?? ''
1417
processPart(part, value, state)
1518
}
@@ -18,8 +21,12 @@ export function createProcessor(processPart: PartProcessor): TemplateTypeInit {
1821
}
1922
}
2023

21-
export function processPropertyIdentity(part: TemplatePart, value: unknown): void {
22-
part.value = value instanceof Node ? value : String(value)
24+
export function processPropertyIdentity(part: TemplatePart, value: unknown, state: unknown): void {
25+
if (part instanceof InnerTemplatePart) {
26+
part.template.content.replaceChildren(new TemplateInstance(part.template, state))
27+
} else {
28+
part.value = value instanceof Node ? value : String(value)
29+
}
2330
}
2431

2532
export function processBooleanAttribute(part: TemplatePart, value: unknown): boolean {
@@ -35,8 +42,10 @@ export function processBooleanAttribute(part: TemplatePart, value: unknown): boo
3542
}
3643

3744
export const propertyIdentity = createProcessor(processPropertyIdentity)
38-
export const propertyIdentityOrBooleanAttribute = createProcessor((part: TemplatePart, value: unknown) => {
39-
if (!processBooleanAttribute(part, value)) {
40-
processPropertyIdentity(part, value)
41-
}
42-
})
45+
export const propertyIdentityOrBooleanAttribute = createProcessor(
46+
(part: TemplatePart, value: unknown, state: unknown) => {
47+
if (!processBooleanAttribute(part, value)) {
48+
processPropertyIdentity(part, value, state)
49+
}
50+
},
51+
)

src/template-instance.ts

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,7 @@ function* collectParts(el: DocumentFragment): Generator<TemplatePart> {
1010
let node
1111
while ((node = walker.nextNode())) {
1212
if (node instanceof HTMLTemplateElement) {
13-
if (node.hasAttribute('directive')) {
14-
yield new InnerTemplatePart(node)
15-
} else {
16-
for (const part of collectParts(node.content)) {
17-
yield part
18-
}
19-
}
13+
yield new InnerTemplatePart(node)
2014
} else if (node instanceof Element && node.hasAttributes()) {
2115
for (let i = 0; i < node.attributes.length; i += 1) {
2216
const attr = node.attributes.item(i)

test/template-instance.ts

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ describe('template-instance', () => {
3636
root.appendChild(new TemplateInstance(template, {x: 'Hello world'}))
3737

3838
expect(root.innerHTML).to.equal('<template><div>Hello world</div></template>')
39+
expect(template.innerHTML).to.equal('<template><div>{{x}}</div></template>')
3940
})
4041
it('applies data to templated DocumentFragment nodes', () => {
4142
const template = document.createElement('template')
@@ -357,7 +358,7 @@ describe('template-instance', () => {
357358
})
358359

359360
describe('handling InnerTemplatePart', () => {
360-
it('makes outer state available to inner parts', () => {
361+
it('makes outer state available to InnerTemplatePart elements with [directive]', () => {
361362
const processor = createProcessor((part, value, state) => {
362363
if (part instanceof InnerTemplatePart && part.directive === 'if') {
363364
if (typeof state === 'object' && (state as Record<string, unknown>)[part.expression]) {
@@ -366,7 +367,7 @@ describe('template-instance', () => {
366367
part.replace()
367368
}
368369
} else {
369-
processPropertyIdentity(part, value)
370+
processPropertyIdentity(part, value, state)
370371
}
371372
})
372373
const template = Object.assign(document.createElement('template'), {
@@ -380,6 +381,24 @@ describe('template-instance', () => {
380381
root.replaceChildren(new TemplateInstance(template, {x: 'x', y: false}, processor))
381382
expect(root.innerHTML).to.equal('x')
382383
})
384+
385+
it('makes outer state available to InnerTemplatePart elements without attributes', () => {
386+
let callCount = 0
387+
const processor = createProcessor((part, value, state) => {
388+
if (part instanceof InnerTemplatePart && value === part.expression) {
389+
callCount += 1
390+
processPropertyIdentity(part, value, state)
391+
}
392+
})
393+
const template = Object.assign(document.createElement('template'), {
394+
innerHTML: '<template>{{x}}</template>',
395+
})
396+
397+
const root = document.createElement('div')
398+
root.appendChild(new TemplateInstance(template, {x: 'Hello world'}, processor))
399+
expect(callCount).to.equal(1)
400+
expect(root.innerHTML).to.equal('<template>Hello world</template>')
401+
})
383402
})
384403
})
385404
})

0 commit comments

Comments
 (0)