You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Introduce declarativeTemplate() and a registry-aware f-template publication bridge so FASTElement definitions can wait for DOM-authored templates without manual TemplateElement setup while preserving the legacy defer-and-hydrate fallback.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
EC->>EC: template-pending guard: wait if no template yet
503
+
EC->>EC: concrete template already attached
506
504
EC->>Callbacks: hydrationStarted()
507
505
EC->>Callbacks: elementWillHydrate(element)
508
506
EC->>EC: template.hydrate() — maps existing DOM to binding targets
@@ -550,21 +548,21 @@ tagged templates produce.
550
548
|`children(prop)`| FAST directive used for `f-children`|
551
549
|`ref(prop)`| FAST directive used for `f-ref`|
552
550
553
-
### Template attachment after define
551
+
### Deferred template attachment via define
554
552
555
-
Standard `FASTElement.define()` returns a `Promise` that resolves immediately once the definition has been composed and any async template resolver has settled. Declarative HTML can define a host element without an initial template and let `<f-template>`attach the template later through `FASTElementDefinition.template`. This unified API replaces the previous `defineAsync()` / `composeAsync()` methods.
553
+
Standard `FASTElement.define()` returns a `Promise` that resolves immediately when a concrete template is provided at definition time. When `template: declarativeTemplate()` is used, the `Promise` resolves after the matching `<f-template>`supplies a concrete template through the bridge. This unified API replaces the previous `defineAsync()` / `composeAsync()` methods.
556
554
557
555
---
558
556
559
557
## Hydration Model
560
558
561
-
For declarative hydration, the server must render:
559
+
When declarative templates are used, the server must render:
562
560
563
561
1. The custom element tag with its attributes and initial state.
564
562
2. A `<template shadowrootmode="open">` containing pre-rendered HTML annotated with FAST's hydration markers.
565
563
3. An `<f-template>` element somewhere in the page that carries the template definition.
566
564
567
-
If a template is attached after an element has already connected, the observable `template` update recreates the controller so hydration can proceed against the existing prerendered markup. The `defer-hydration` and `needs-hydration` attributes are no longer needed in server-rendered markup.
565
+
With `declarativeTemplate()`, connection gating happens before platform registration: the resolver waits for the matching `<f-template>` and keeps the definition concrete before elements can connect. Hydration can therefore start immediately when `ElementController.connect()` runs. The `defer-hydration` and `needs-hydration` attributes are no longer needed in server-rendered markup.
Copy file name to clipboardExpand all lines: packages/fast-element/DECLARATIVE_RENDERING.md
+7-1Lines changed: 7 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -91,10 +91,16 @@ export class MyComponent extends FASTElement {
91
91
92
92
MyComponent.define({
93
93
name: "my-component",
94
+
template: declarativeTemplate(),
94
95
});
95
96
```
96
97
97
-
When the element connects, `ElementController` automatically detects the existing shadow root from SSR and sets `isPrerendered = true`. If the template is attached after the element has already connected, the observable `template` update recreates the controller so hydration can proceed. The `defer-hydration` and `needs-hydration` attributes are no longer needed.
98
+
When the element connects, `ElementController` automatically detects the
99
+
existing shadow root from SSR and sets `isPrerendered = true`.
100
+
`declarativeTemplate()` keeps the definition template concrete before
101
+
registration completes, so the element can hydrate the prerendered shadow root
102
+
immediately. The `defer-hydration` and `needs-hydration` attributes are no
`declarativeTemplate()` from `@microsoft/fast-element/declarative.js`
66
+
automatically ensures that `f-template` is defined in the same registry as the
67
+
FAST element being composed.
74
68
75
69
### Phase 3: Template Processing and Attachment
76
70
77
71
When an `f-template` element is connected to the DOM, it initiates the template attachment process.
78
72
79
73
The lifecycle flow during this phase:
80
74
81
-
1.**Template Element Connection**: The `f-template` element's `connectedCallback()` is invoked
82
-
2.**Async Registration Lookup**: Uses `FASTElementDefinition.register(this.name)` to find the partial element definition
83
-
3.**Template Processing**: Processes the HTML template, resolving data bindings, directives, and other template features into the `ViewTemplate` model which is also used by the `@microsoft/fast-element``html` tag template
84
-
4.**Template Attachment**: Attaches the processed template to the partial element definition via `registeredFastElement.template = resolvedTemplate`
75
+
1.**Template Discovery**: The resolver waits for a matching
76
+
`<f-template name="...">` in the same registry as the element definition.
77
+
2.**Template Element Connection**: The matching `f-template` element's
78
+
`connectedCallback()` registers it with the declarative template bridge.
79
+
3.**Template Processing**: The bridge reads and transforms the markup, builds
80
+
the schema, applies `observerMap()` / `attributeMap()` behavior, and resolves
81
+
data bindings, directives, and other template features into the `ViewTemplate`
82
+
model which is also used by the `@microsoft/fast-element``html` tag template.
83
+
4.**Template Attachment**: The concrete `ViewTemplate` is returned to
84
+
`FASTElement.define()`, which assigns it to the definition before platform
85
+
registration completes.
85
86
86
-
### Phase 4: Template Activation
87
+
### Phase 4: Composition Completion
87
88
88
-
Once the template is attached to the registered definition, FAST activates it
89
-
for both future and already-connected elements:
89
+
Once the template is attached to the partial definition, the element completes its composition:
90
90
91
-
1.**Definition Update**: `TemplateElement` assigns the parsed `ViewTemplate` to `registeredFastElement.template`
92
-
2.**Observable Notification**: Connected elements observing the definition recreate their controller when the `template` property changes
93
-
3.**Future Connections**: New element instances use the attached template immediately
91
+
1.**`compose()` Execution**: The element definition internally completes its composition process
92
+
2.**Platform Registration**: The completed element definition is fully registered with the platform's custom element registry
94
93
95
94
### Phase 5: Element Instantiation and Hydration
96
95
97
96
When custom elements are instantiated in the DOM, the following occurs:
98
97
99
98
1.**Element Creation**: The platform creates instances of the custom element
100
99
2.**Prerendered Content Detection**: `ElementController` detects the existing shadow root from SSR and sets `isPrerendered = true`
101
-
3.**Late Template Attachment**: If an element connected before its template was attached, the observable `template` change recreates its controller.
102
-
4.**Hydration**: Once the template is available, `ElementController` uses `template.hydrate()` to create a `HydrationView` that maps existing DOM nodes to binding targets using `fe:b` / `fe:/b` markers
100
+
3.**Concrete Template Ready**: Because `declarativeTemplate()` resolved during
101
+
definition, `connect()` starts with the final template already attached.
102
+
4.**Hydration**: `ElementController` uses `template.hydrate()` to create a
103
+
`HydrationView` that maps existing DOM nodes to binding targets using `fe:b`
104
+
/ `fe:/b` markers
103
105
104
106
The DOM after hydration should look like this:
105
107
@@ -126,7 +128,7 @@ The `fastElementRegistry` serves as the central coordination point between the t
126
128
Both packages use the Observable pattern for coordination:
127
129
128
130
-`FASTElementDefinition.register()` uses `Observable.getNotifier()` to notify when elements are registered
129
-
- Template attachment triggers observable `template`notifications so connected elements can complete rendering or hydration
131
+
- Template attachment triggers observable notifications to complete the lifecycle
Copy file name to clipboardExpand all lines: packages/fast-element/MIGRATION.md
+9-3Lines changed: 9 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -125,7 +125,9 @@ available and is applied automatically by
125
125
### Changed behavior
126
126
127
127
-**`attributeChangedCallback` during upgrade**: When `isPrerendered` is true and the element has not yet connected, attribute change callbacks are suppressed. After connection, all attribute changes are processed normally.
128
-
-**Late template attachment**: Connected elements no longer rely on `templateOptions` to pause connection. If a definition receives a template later, the observable `template` update recreates the controller so rendering or hydration can continue. No `defer-hydration` attribute is needed.
128
+
-**Declarative template resolution**: `declarativeTemplate()` waits for the
129
+
matching `<f-template>` before `define()` completes, so connected elements
130
+
hydrate with a concrete template. No `defer-hydration` attribute is needed.
129
131
-**Binding evaluation with existing shadow root**: When an existing shadow root is detected, `attribute` and `booleanAttribute` bindings skip their initial DOM update. All other binding types (event, content, property, tokenList) run normally.
130
132
131
133
### New APIs
@@ -200,7 +202,10 @@ This is a **breaking change** for SSR output format. Any system that produces or
200
202
201
203
### Changed behavior
202
204
203
-
-**`FASTElement.define()`** now returns `Promise<TType>`. When a template is provided at definition time — or when no template is provided — the Promise resolves immediately. If the definition uses an async template resolver, the Promise resolves after that resolver settles.
205
+
-**`FASTElement.define()`** now returns `Promise<TType>`. When a concrete
206
+
template is provided at definition time, the Promise resolves immediately.
207
+
When `template: declarativeTemplate()` is used, the Promise resolves after
208
+
the matching `<f-template>` supplies the concrete template.
204
209
-**`FASTElement.compose()`** now returns `Promise<FASTElementDefinition>`. The Promise always resolves immediately.
205
210
-**`FASTElementDefinition.compose()`** now returns `Promise<FASTElementDefinition>`. The Promise always resolves immediately.
206
211
-**`@customElement` decorator** calls `define()` internally but does not return the Promise (fire-and-forget). For complete definitions with a template, the element is registered via a microtask.
@@ -217,8 +222,9 @@ This is a **breaking change** for SSR output format. Any system that produces or
0 commit comments