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
Drop the templateOptions defer-and-hydrate API from fast-element so the core resolver pipeline no longer preserves the old deferred-template registration path.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copy file name to clipboardExpand all lines: packages/fast-element/DECLARATIVE_DESIGN.md
+4-4Lines changed: 4 additions & 4 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -533,21 +533,21 @@ tagged templates produce.
533
533
|`children(prop)`| FAST directive used for `f-children`|
534
534
|`ref(prop)`| FAST directive used for `f-ref`|
535
535
536
-
### Deferred template attachment via define
536
+
### Template attachment after define
537
537
538
-
Standard `FASTElement.define()` returns a `Promise` that resolves immediately when a template is provided at definition time. When `templateOptions` is `"defer-and-hydrate"` and no template is provided, the `Promise` resolves after a `<f-template>`supplies one via `register()`. This unified API replaces the previous `defineAsync()` / `composeAsync()` methods.
538
+
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.
539
539
540
540
---
541
541
542
542
## Hydration Model
543
543
544
-
When `templateOptions: "defer-and-hydrate"` is used, the server must render:
544
+
For declarative hydration, the server must render:
545
545
546
546
1. The custom element tag with its attributes and initial state.
547
547
2. A `<template shadowrootmode="open">` containing pre-rendered HTML annotated with FAST's hydration markers.
548
548
3. An `<f-template>` element somewhere in the page that carries the template definition.
549
549
550
-
Connection gating is handled by the template-pending guard in `ElementController.connect()`. When `templateOptions` is `"defer-and-hydrate"` and no template is available yet, `connect()` returns early. An Observable subscription on `"template"` retriggers `connect()` when the template arrives. The `defer-hydration` and `needs-hydration` attributes are no longer needed in server-rendered markup.
550
+
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.
Copy file name to clipboardExpand all lines: packages/fast-element/DECLARATIVE_RENDERING.md
+1-2Lines changed: 1 addition & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -91,11 +91,10 @@ export class MyComponent extends FASTElement {
91
91
92
92
MyComponent.define({
93
93
name: "my-component",
94
-
templateOptions: "defer-and-hydrate",
95
94
});
96
95
```
97
96
98
-
When the element connects, `ElementController` automatically detects the existing shadow root from SSR and sets `isPrerendered = true`. The template-pending guard in `ElementController.connect()` ensures the element waits for its template before hydrating. The `defer-hydration` and `needs-hydration` attributes are no longer needed — connection gating is handled internally by the template-pending guard.
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.
Copy file name to clipboardExpand all lines: packages/fast-element/DECLARATIVE_RENDERING_LIFECYCLE.md
+16-13Lines changed: 16 additions & 13 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -13,9 +13,9 @@ core runtime and its declarative entrypoint:
13
13
-**`@microsoft/fast-element/declarative.js`**: Provides the `f-template`
14
14
custom element that processes HTML templates and attaches them to FAST
15
15
elements as a `ViewTemplate` in lieu of an `html` template created during
16
-
`FASTElement.define()`. When using `f-template` the `FASTElement.define()`
17
-
method is called with `templateOptions: "defer-and-hydrate"` to defer
18
-
template attachment.
16
+
`FASTElement.define()`. When using `f-template`, the host element can be
17
+
defined without an initial template and the declarative runtime attaches the
18
+
template later.
19
19
20
20
## Lifecycle Phases
21
21
@@ -39,23 +39,24 @@ The following phases will then be kicked off once the JavaScript is parsed.
39
39
40
40
### Phase 1: Partial Element Registration
41
41
42
-
Custom elements begin their lifecycle by registering as partial definitions with the FAST Element Registry using the `define()` method with `templateOptions: "defer-and-hydrate"`. This allows the element to be registered before its template is available.
42
+
Custom elements begin their lifecycle by registering with FAST via the
43
+
`define()` method before their declarative template is available.
43
44
44
45
```typescript
45
46
// Custom element class definition
46
47
classMyComponentextendsFASTElement {
47
48
@attr text:string="";
48
49
}
49
50
50
-
// Register as partial definition - element is registered but incomplete
51
+
// Register the host element before the declarative template is attached
51
52
MyComponent.define({
52
53
name: "my-component",
53
54
});
54
55
```
55
56
56
57
Key characteristics of this phase:
57
-
- Element is in a "partial" state waiting for template attachment
58
-
-`templateOptions` allows for hydration options to be provided. TBD see [this issue](https://github.com/orgs/microsoft/projects/240/views/17?pane=issue&itemId=127653173&issue=microsoft%7Cfast%7C7173).
58
+
- Element is registered before template attachment
59
+
-The definition is completed later when `<f-template>` assigns `definition.template`
59
60
60
61
### Phase 2: Template Element Definition
61
62
@@ -82,20 +83,22 @@ The lifecycle flow during this phase:
82
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
83
84
4.**Template Attachment**: Attaches the processed template to the partial element definition via `registeredFastElement.template = resolvedTemplate`
84
85
85
-
### Phase 4: Composition Completion
86
+
### Phase 4: Template Activation
86
87
87
-
Once the template is attached to the partial definition, the element completes its composition:
88
+
Once the template is attached to the registered definition, FAST activates it
89
+
for both future and already-connected elements:
88
90
89
-
1.**`compose()` Execution**: The element definition internally completes its composition process
90
-
2.**Platform Registration**: The completed element definition is fully registered with the platform's custom element registry
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
94
92
95
### Phase 5: Element Instantiation and Hydration
93
96
94
97
When custom elements are instantiated in the DOM, the following occurs:
95
98
96
99
1.**Element Creation**: The platform creates instances of the custom element
97
100
2.**Prerendered Content Detection**: `ElementController` detects the existing shadow root from SSR and sets `isPrerendered = true`
98
-
3.**Template-Pending Guard**: If `templateOptions` is `"defer-and-hydrate"` and no template is available yet, `connect()` returns early. An Observable subscription on `"template"` retriggers `connect()` when the template arrives.
101
+
3.**Late Template Attachment**: If an element connected before its template was attached, the observable `template` change recreates its controller.
99
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
103
101
104
The DOM after hydration should look like this:
@@ -123,7 +126,7 @@ The `fastElementRegistry` serves as the central coordination point between the t
123
126
Both packages use the Observable pattern for coordination:
124
127
125
128
-`FASTElementDefinition.register()` uses `Observable.getNotifier()` to notify when elements are registered
126
-
- Template attachment triggers observable notifications to complete the lifecycle
129
+
- Template attachment triggers observable `template`notifications so connected elements can complete rendering or hydration
Copy file name to clipboardExpand all lines: packages/fast-element/DESIGN.md
+5-7Lines changed: 5 additions & 7 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -94,7 +94,7 @@ registry.
94
94
- Holds the element's `FASTElementDefinition` (name, template, styles, observed attributes).
95
95
- Manages a `Stages` state machine: `disconnected → connecting → connected → disconnecting → disconnected`.
96
96
- Exposes `isPrerendered: Promise<boolean>` which resolves to `true` after prerendered content has been hydrated, or `false` when the component is client-side rendered. The `ViewController` interface also exposes `isPrerendered` as `Promise<boolean>` for custom directives. Attribute-skip logic during the hydration bind uses an internal `_skipAttrUpdates` flag that is never exposed as a public boolean.
97
-
- On `connect()`: restores pre-upgrade observable values, calls `connectedCallback` on all `HostBehavior`s, renders the template into the shadow root, and applies styles. When `templateOptions`is `"defer-and-hydrate"` and no template is available yet, `connect()` returns early; an Observable subscription on `"template"` retriggers `connect()` when the template arrives (template-pending guard).
97
+
- On `connect()`: restores pre-upgrade observable values, calls `connectedCallback` on all `HostBehavior`s, renders the current template into the shadow root when one is available, and applies styles.
98
98
- Rendering is split into two modular paths via `renderPrerendered()` and `renderClientSide()`:
99
99
-**Prerendered**: `renderPrerendered()` registers the element in the static hydration tracker, swaps `onAttributeChangedCallback` to a no-op so the upgrade-time burst of callbacks is discarded, hydrates the existing DOM via `template.hydrate()`, then restores the standard handler and removes the element from the tracker. After this point, all future attribute changes flow through the real handler with zero overhead.
100
100
-**Client-side**: `renderClientSide()` clones the compiled fragment, binds, and appends to the host — the standard path with no prerender logic.
@@ -103,7 +103,7 @@ registry.
103
103
-`onAttributeChangedCallback()` is the standard handler that processes attribute changes. During the prerendered bind, it is temporarily swapped to a no-op (see above) to avoid redundant processing of server-rendered attribute values.
104
104
- Exposes `addBehavior` / `removeBehavior` for dynamic `HostBehavior` management (used by `ElementStyles`).
105
105
106
-
`FASTElementDefinition` wraps all the metadata for a custom element class: its tag name, template, styles, and observed attribute list. It is created by `FASTElement.compose()` (which returns `Promise<FASTElementDefinition>`, always resolving immediately) and registered globally via `fastElementRegistry`. `PartialFASTElementDefinition.template` may be either a concrete `ElementViewTemplate` or a `FASTElementTemplateResolver` function that receives the composed definition and returns the concrete template (sync or async). `FASTElement.define()` returns `Promise<TType>` — resolving immediately for complete definitions, deferring when `templateOptions` is `"defer-and-hydrate"` and no template is provided, and resolving template resolver functions only after extensions have had a chance to update the definition. `FASTElementDefinition.register()` returns `Promise<Function>` — resolving when a definition with the given name has been registered.
106
+
`FASTElementDefinition` wraps all the metadata for a custom element class: its tag name, template, styles, and observed attribute list. It is created by `FASTElement.compose()` (which returns `Promise<FASTElementDefinition>`, always resolving immediately) and registered globally via `fastElementRegistry`. `PartialFASTElementDefinition.template` may be either a concrete `ElementViewTemplate` or a `FASTElementTemplateResolver` function that receives the composed definition and returns the concrete template (sync or async). `FASTElement.define()` returns `Promise<TType>` — resolving immediately for complete definitions or definitions without an initial template, and resolving async template resolver functions only after extensions have had a chance to update the definition. `FASTElementDefinition.register()` returns `Promise<Function>` — resolving when a definition with the given name has been registered.
107
107
108
108
#### Extensions
109
109
@@ -375,9 +375,7 @@ flowchart TD
375
375
CTOR[constructor] --> EC[ElementController.forCustomElement creates or locates the controller]
376
376
EC --> ATTACH[Controller captures element + definition, sets $fastController]
377
377
378
-
CONN[connectedCallback] --> TGUARD{templateOptions = defer-and-hydrate\nAND no template yet?}
379
-
TGUARD -->|yes| WAIT[Return early — Observable subscription\non 'template' retriggers connect]
@@ -499,7 +497,7 @@ Below is a conceptual map of the major subsystems and their relationships:
499
497
1. Developer writes a class extending `FASTElement`, decorates properties with `@observable` / `@attr`, and calls `FASTElement.define({ name, template, styles })`.
500
498
2.`FASTElement.define` → `FASTElementDefinition.compose(...).define()` registers the element with the Custom Element Registry.
501
499
3. When the browser upgrades the element, `ElementController.forCustomElement(element)` is called in the constructor.
502
-
4. On `connectedCallback`, the controller renders the template into the shadow root. If the element already has a shadow root from SSR (prerendered content), `renderPrerendered()` uses `template.hydrate()` to map existing DOM nodes to binding targets instead of cloning new DOM. If `templateOptions` is `"defer-and-hydrate"` and no template is available yet, `connect()` returns early and retriggers when the template arrives. Compilation is lazy: the first render call triggers `Compiler.compile()`, subsequent calls clone the already-compiled `DocumentFragment`.
500
+
4. On `connectedCallback`, the controller renders the template into the shadow root. If the element already has a shadow root from SSR (prerendered content), `renderPrerendered()` uses `template.hydrate()` to map existing DOM nodes to binding targets instead of cloning new DOM. If no template is available yet, the element connects without rendering until a later `definition.template` update recreates the controller. Compilation is lazy: the first render call triggers `Compiler.compile()`, subsequent calls clone the already-compiled `DocumentFragment`.
503
501
5.`HTMLView.bind(source)` wires up each `ViewBehavior`. `oneWay` bindings create `ExpressionNotifier`s that track observable dependencies automatically.
504
502
6. When an observed property changes, its notifier fans out to all subscribers. Each binding enqueues a DOM update via `Updates`. The next animation frame drains the queue and applies the mutations.
505
503
7. On `disconnectedCallback`, `HTMLView.unbind()` tears down all bindings; behaviors disconnect; styles are removed.
0 commit comments