@contentful/experiences-angular 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +189 -0
- package/README.md +368 -0
- package/dist/client-experience-renderer.component.d.ts +42 -0
- package/dist/client-experience-renderer.component.d.ts.map +1 -0
- package/dist/client-experience-renderer.component.js +155 -0
- package/dist/client-experience-renderer.component.js.map +1 -0
- package/dist/component-error.component.d.ts +14 -0
- package/dist/component-error.component.d.ts.map +1 -0
- package/dist/component-error.component.js +81 -0
- package/dist/component-error.component.js.map +1 -0
- package/dist/context.d.ts +21 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +54 -0
- package/dist/context.js.map +1 -0
- package/dist/debug-experience.component.d.ts +17 -0
- package/dist/debug-experience.component.d.ts.map +1 -0
- package/dist/debug-experience.component.js +143 -0
- package/dist/debug-experience.component.js.map +1 -0
- package/dist/design-utils.d.ts +22 -0
- package/dist/design-utils.d.ts.map +1 -0
- package/dist/design-utils.js +40 -0
- package/dist/design-utils.js.map +1 -0
- package/dist/experience-defaults.d.ts +16 -0
- package/dist/experience-defaults.d.ts.map +1 -0
- package/dist/experience-defaults.js +27 -0
- package/dist/experience-defaults.js.map +1 -0
- package/dist/experience-scope.d.ts +33 -0
- package/dist/experience-scope.d.ts.map +1 -0
- package/dist/experience-scope.js +90 -0
- package/dist/experience-scope.js.map +1 -0
- package/dist/index.d.ts +40 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +62 -0
- package/dist/index.js.map +1 -0
- package/dist/inject-active-viewport.d.ts +32 -0
- package/dist/inject-active-viewport.d.ts.map +1 -0
- package/dist/inject-active-viewport.js +78 -0
- package/dist/inject-active-viewport.js.map +1 -0
- package/dist/inject-design-values.d.ts +14 -0
- package/dist/inject-design-values.d.ts.map +1 -0
- package/dist/inject-design-values.js +24 -0
- package/dist/inject-design-values.js.map +1 -0
- package/dist/inject-experience-plan.d.ts +15 -0
- package/dist/inject-experience-plan.d.ts.map +1 -0
- package/dist/inject-experience-plan.js +39 -0
- package/dist/inject-experience-plan.js.map +1 -0
- package/dist/inject-live-preview-experience.d.ts +13 -0
- package/dist/inject-live-preview-experience.d.ts.map +1 -0
- package/dist/inject-live-preview-experience.js +47 -0
- package/dist/inject-live-preview-experience.js.map +1 -0
- package/dist/inject-live-preview.d.ts +16 -0
- package/dist/inject-live-preview.d.ts.map +1 -0
- package/dist/inject-live-preview.js +14 -0
- package/dist/inject-live-preview.js.map +1 -0
- package/dist/missing-component.component.d.ts +14 -0
- package/dist/missing-component.component.d.ts.map +1 -0
- package/dist/missing-component.component.js +83 -0
- package/dist/missing-component.component.js.map +1 -0
- package/dist/node-render-engine.d.ts +109 -0
- package/dist/node-render-engine.d.ts.map +1 -0
- package/dist/node-render-engine.js +528 -0
- package/dist/node-render-engine.js.map +1 -0
- package/dist/node-renderer.directive.d.ts +46 -0
- package/dist/node-renderer.directive.d.ts.map +1 -0
- package/dist/node-renderer.directive.js +96 -0
- package/dist/node-renderer.directive.js.map +1 -0
- package/dist/node-scopes.d.ts +39 -0
- package/dist/node-scopes.d.ts.map +1 -0
- package/dist/node-scopes.js +75 -0
- package/dist/node-scopes.js.map +1 -0
- package/dist/server-experience-renderer.component.d.ts +46 -0
- package/dist/server-experience-renderer.component.d.ts.map +1 -0
- package/dist/server-experience-renderer.component.js +159 -0
- package/dist/server-experience-renderer.component.js.map +1 -0
- package/dist/types.d.ts +137 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +44 -0
- package/dist/types.js.map +1 -0
- package/package.json +57 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
## 0.2.0 (2026-09-28)
|
|
2
|
+
|
|
3
|
+
### đź§± Updated Dependencies
|
|
4
|
+
|
|
5
|
+
- Updated live-preview to 0.2.0
|
|
6
|
+
- Updated client to 0.5.0
|
|
7
|
+
- Updated design to 0.9.0
|
|
8
|
+
- Updated core to 0.9.0
|
|
9
|
+
|
|
10
|
+
## 0.1.11 (2026-09-28)
|
|
11
|
+
|
|
12
|
+
### 🚀 Features
|
|
13
|
+
|
|
14
|
+
- add shared Experiences client runtime ([#206](https://github.com/contentful/experiences/pull/206))
|
|
15
|
+
- add ExO event builder ([caf2772](https://github.com/contentful/experiences/commit/caf2772))
|
|
16
|
+
|
|
17
|
+
### đź§± Updated Dependencies
|
|
18
|
+
|
|
19
|
+
- Updated live-preview to 0.1.9
|
|
20
|
+
- Updated client to 0.4.9
|
|
21
|
+
- Updated design to 0.8.7
|
|
22
|
+
- Updated core to 0.8.7
|
|
23
|
+
|
|
24
|
+
## 0.1.10 (2026-09-22)
|
|
25
|
+
|
|
26
|
+
### đź§± Updated Dependencies
|
|
27
|
+
|
|
28
|
+
- Updated live-preview to 0.1.8
|
|
29
|
+
- Updated client to 0.4.8
|
|
30
|
+
|
|
31
|
+
## 0.1.9 (2026-09-22)
|
|
32
|
+
|
|
33
|
+
### 🚀 Features
|
|
34
|
+
|
|
35
|
+
- add destination node ID and path resolution to fetchExperience [AIS-567] ([#200](https://github.com/contentful/experiences/pull/200))
|
|
36
|
+
|
|
37
|
+
### đź§± Updated Dependencies
|
|
38
|
+
|
|
39
|
+
- Updated live-preview to 0.1.7
|
|
40
|
+
- Updated client to 0.4.7
|
|
41
|
+
- Updated design to 0.8.6
|
|
42
|
+
- Updated core to 0.8.6
|
|
43
|
+
|
|
44
|
+
## 0.1.8 (2026-09-22)
|
|
45
|
+
|
|
46
|
+
### đź§± Updated Dependencies
|
|
47
|
+
|
|
48
|
+
- Updated live-preview to 0.1.6
|
|
49
|
+
- Updated client to 0.4.6
|
|
50
|
+
- Updated design to 0.8.5
|
|
51
|
+
- Updated core to 0.8.5
|
|
52
|
+
|
|
53
|
+
## 0.1.7 (2026-09-22)
|
|
54
|
+
|
|
55
|
+
### 🚀 Features
|
|
56
|
+
|
|
57
|
+
- **live-preview:** expose connection errors [SPA-5151] ([#198](https://github.com/contentful/experiences/pull/198))
|
|
58
|
+
|
|
59
|
+
### đź§± Updated Dependencies
|
|
60
|
+
|
|
61
|
+
- Updated live-preview to 0.1.5
|
|
62
|
+
|
|
63
|
+
## 0.1.6 (2026-09-21)
|
|
64
|
+
|
|
65
|
+
### 🚀 Features
|
|
66
|
+
|
|
67
|
+
- deprecate the SDK viewport surface, guard absent viewports [SPA-5272] ([4a74473](https://github.com/contentful/experiences/commit/4a74473))
|
|
68
|
+
|
|
69
|
+
### 🩹 Fixes
|
|
70
|
+
|
|
71
|
+
- reword deprecation notices, drop internal ticket link [SPA-5272] ([8361460](https://github.com/contentful/experiences/commit/8361460))
|
|
72
|
+
|
|
73
|
+
### đź§± Updated Dependencies
|
|
74
|
+
|
|
75
|
+
- Updated live-preview to 0.1.4
|
|
76
|
+
- Updated client to 0.4.5
|
|
77
|
+
- Updated design to 0.8.4
|
|
78
|
+
- Updated core to 0.8.4
|
|
79
|
+
|
|
80
|
+
## 0.1.5 (2026-09-21)
|
|
81
|
+
|
|
82
|
+
### đź§± Updated Dependencies
|
|
83
|
+
|
|
84
|
+
- Updated live-preview to 0.1.3
|
|
85
|
+
- Updated client to 0.4.4
|
|
86
|
+
|
|
87
|
+
## 0.1.4 (2026-09-18)
|
|
88
|
+
|
|
89
|
+
### 🚀 Features
|
|
90
|
+
|
|
91
|
+
- **live-preview:** support resource resolution [SPA-5369] ([#193](https://github.com/contentful/experiences/pull/193))
|
|
92
|
+
|
|
93
|
+
### đź§± Updated Dependencies
|
|
94
|
+
|
|
95
|
+
- Updated live-preview to 0.1.2
|
|
96
|
+
|
|
97
|
+
## 0.1.3 (2026-09-11)
|
|
98
|
+
|
|
99
|
+
### 🚀 Features
|
|
100
|
+
|
|
101
|
+
- **live-preview:** expose preview status helper [SPA-5289] ([#170](https://github.com/contentful/experiences/pull/170))
|
|
102
|
+
- **live-preview:** add framework adapters [SPA-5161] ([#165](https://github.com/contentful/experiences/pull/165))
|
|
103
|
+
|
|
104
|
+
### đź§± Updated Dependencies
|
|
105
|
+
|
|
106
|
+
- Updated live-preview to 0.1.1
|
|
107
|
+
- Updated client to 0.4.3
|
|
108
|
+
- Updated design to 0.8.3
|
|
109
|
+
- Updated core to 0.8.3
|
|
110
|
+
|
|
111
|
+
## 0.1.2 (2026-09-10)
|
|
112
|
+
|
|
113
|
+
### đź§± Updated Dependencies
|
|
114
|
+
|
|
115
|
+
- Updated client to 0.4.2
|
|
116
|
+
- Updated design to 0.8.2
|
|
117
|
+
- Updated core to 0.8.2
|
|
118
|
+
|
|
119
|
+
## 0.1.1 (2026-09-02)
|
|
120
|
+
|
|
121
|
+
### đź§± Updated Dependencies
|
|
122
|
+
|
|
123
|
+
- Updated client to 0.4.1
|
|
124
|
+
- Updated design to 0.8.1
|
|
125
|
+
- Updated core to 0.8.1
|
|
126
|
+
|
|
127
|
+
## 0.1.0 (2026-08-28)
|
|
128
|
+
|
|
129
|
+
### đź§± Updated Dependencies
|
|
130
|
+
|
|
131
|
+
- Updated client to 0.4.0
|
|
132
|
+
- Updated design to 0.8.0
|
|
133
|
+
- Updated core to 0.8.0
|
|
134
|
+
|
|
135
|
+
## 0.0.6 (2026-08-28)
|
|
136
|
+
|
|
137
|
+
### 🚀 Features
|
|
138
|
+
|
|
139
|
+
- error handling and sensible fallbacks for non-happy-path rendering [AIS-364] ([#156](https://github.com/contentful/experiences/pull/156))
|
|
140
|
+
|
|
141
|
+
### đź§± Updated Dependencies
|
|
142
|
+
|
|
143
|
+
- Updated client to 0.3.11
|
|
144
|
+
- Updated design to 0.7.11
|
|
145
|
+
- Updated core to 0.7.11
|
|
146
|
+
|
|
147
|
+
## 0.0.5 (2026-08-27)
|
|
148
|
+
|
|
149
|
+
### đź§± Updated Dependencies
|
|
150
|
+
|
|
151
|
+
- Updated client to 0.3.10
|
|
152
|
+
- Updated design to 0.7.10
|
|
153
|
+
- Updated core to 0.7.10
|
|
154
|
+
|
|
155
|
+
## 0.0.4 (2026-08-26)
|
|
156
|
+
|
|
157
|
+
### đź§± Updated Dependencies
|
|
158
|
+
|
|
159
|
+
- Updated client to 0.3.9
|
|
160
|
+
- Updated design to 0.7.9
|
|
161
|
+
- Updated core to 0.7.9
|
|
162
|
+
|
|
163
|
+
## 0.0.3 (2026-08-24)
|
|
164
|
+
|
|
165
|
+
### đź§± Updated Dependencies
|
|
166
|
+
|
|
167
|
+
- Updated client to 0.3.8
|
|
168
|
+
- Updated design to 0.7.8
|
|
169
|
+
- Updated core to 0.7.8
|
|
170
|
+
|
|
171
|
+
## 0.0.2 (2026-08-21)
|
|
172
|
+
|
|
173
|
+
### đź§± Updated Dependencies
|
|
174
|
+
|
|
175
|
+
- Updated client to 0.3.7
|
|
176
|
+
- Updated design to 0.7.7
|
|
177
|
+
- Updated core to 0.7.7
|
|
178
|
+
|
|
179
|
+
## 0.0.1 (2026-08-20)
|
|
180
|
+
|
|
181
|
+
### 🚀 Features
|
|
182
|
+
|
|
183
|
+
- **adapter-angular:** scaffold @contentful/experiences-angular [AIS-361] ([#134](https://github.com/contentful/experiences/pull/134))
|
|
184
|
+
|
|
185
|
+
### đź§± Updated Dependencies
|
|
186
|
+
|
|
187
|
+
- Updated client to 0.3.6
|
|
188
|
+
- Updated design to 0.7.6
|
|
189
|
+
- Updated core to 0.7.6
|
package/README.md
ADDED
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
# @contentful/experiences-angular
|
|
2
|
+
|
|
3
|
+
> ⚠️ **Alpha.** Published to npm. APIs are unstable and will change.
|
|
4
|
+
|
|
5
|
+
The Angular adapter for the Contentful Experiences SDK. You bring your own standalone Angular components; it renders Experience payloads from the Experience Delivery API (XDA) with them.
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
npm install @contentful/experiences-angular
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Supports Angular **20, 21, and 22**. The package ships partial-Ivy output compiled by the lowest supported compiler, so the Angular linker in your app can consume it on any of the three.
|
|
12
|
+
|
|
13
|
+
This is the **only SDK package you install**. It re-exports everything you need from `@contentful/experiences-sdk-core` and `@contentful/experiences-design`. The other packages are workspace-internal.
|
|
14
|
+
|
|
15
|
+
The public API mirrors `@contentful/experiences-react` and `@contentful/experiences-svelte` in shape; only the rendering primitives change (standalone `Type<unknown>` components, `PortableRenderNode[]` slot inputs instead of `children: ReactNode`, and `injectDesignValues()` instead of `useDesignValues()`). See [Parity table](#parity-table) for the full mapping.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Public API
|
|
20
|
+
|
|
21
|
+
### Authoring
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
defineComponent<Props>(config); // Type-narrowing identity for component-type configs
|
|
25
|
+
defineExperienceTemplate<Props>(config); // Same shape, for coded Experience Template configs
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Fetching
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
fetchExperience(experienceOptions, clientOptions, resolveOptions); // Async; fetches from XDA and resolves in one call
|
|
32
|
+
fetchPreviewSession(previewSessionOptions, clientOptions, resolveOptions); // Async; fetches and resolves a Preview Session Experience
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Use `fetchPreviewSession` for the initial render when the Contentful app
|
|
36
|
+
provides a `preview_session_id`. It fetches the current Preview Session
|
|
37
|
+
snapshot and resolves it into a `PortableRenderPlan`. Use `injectLivePreview`
|
|
38
|
+
to receive subsequent WebSocket updates:
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
const plan = await fetchPreviewSession(
|
|
42
|
+
{ spaceId, environmentId, sessionId },
|
|
43
|
+
{ previewToken },
|
|
44
|
+
{ config: experienceConfig }
|
|
45
|
+
);
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
If the Preview Session references resources from other spaces, pass the same
|
|
49
|
+
encoded `resourceResolution` value in the `previewSessionOptions` passed to
|
|
50
|
+
`fetchPreviewSession` and `injectLivePreview`.
|
|
51
|
+
|
|
52
|
+
### Resolver
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
resolveExperience(payload, config, opts?); // Async; walks payload, runs resolveData, returns a PortableRenderPlan
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Live preview
|
|
59
|
+
|
|
60
|
+
Use `injectLivePreview()` when the app should subscribe to Preview Session
|
|
61
|
+
updates, resolve each payload, and render the resulting plan from `data`:
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
readonly livePreview = injectLivePreview(() => ({
|
|
65
|
+
previewSessionOptions,
|
|
66
|
+
initialPayload,
|
|
67
|
+
initialPlan,
|
|
68
|
+
resolveOptions: { config: experienceConfig },
|
|
69
|
+
}));
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
For separate access to the raw payload and rendered plan, use
|
|
73
|
+
`injectLivePreviewExperience()` and `injectExperiencePlan()`. The first returns
|
|
74
|
+
the raw Experience payload. The second passes that payload to
|
|
75
|
+
`resolveExperience()` and returns the `PortableRenderPlan` consumed by the
|
|
76
|
+
renderer. Keeping the values separate lets an app use the raw payload when it
|
|
77
|
+
needs it.
|
|
78
|
+
|
|
79
|
+
Both helpers take a getter. Angular can call an injection helper before it has
|
|
80
|
+
assigned component inputs, so the getter reads the current values when Angular
|
|
81
|
+
runs the helper. Each helper returns an object with a `data` signal, matching
|
|
82
|
+
the other framework adapters.
|
|
83
|
+
|
|
84
|
+
```ts
|
|
85
|
+
// page.component.ts
|
|
86
|
+
import { Component } from '@angular/core';
|
|
87
|
+
import {
|
|
88
|
+
ClientExperienceRenderer,
|
|
89
|
+
injectLivePreviewExperience,
|
|
90
|
+
injectExperiencePlan,
|
|
91
|
+
type PortableRenderPlan,
|
|
92
|
+
} from '@contentful/experiences-angular';
|
|
93
|
+
import { experienceConfig } from './experience-config';
|
|
94
|
+
|
|
95
|
+
@Component({
|
|
96
|
+
selector: 'app-page',
|
|
97
|
+
imports: [ClientExperienceRenderer],
|
|
98
|
+
template: `<cf-experience [experience]="plan.data()" [config]="experienceConfig" />`,
|
|
99
|
+
})
|
|
100
|
+
export class PageComponent {
|
|
101
|
+
readonly livePreviewOptions = {
|
|
102
|
+
previewSessionOptions: {
|
|
103
|
+
spaceId: 'space-id',
|
|
104
|
+
environmentId: 'environment-id',
|
|
105
|
+
previewToken: 'preview-token',
|
|
106
|
+
sessionId: 'session-id',
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
readonly initialPlan: PortableRenderPlan | undefined = undefined;
|
|
110
|
+
|
|
111
|
+
readonly livePreview = injectLivePreviewExperience(() => this.livePreviewOptions);
|
|
112
|
+
readonly plan = injectExperiencePlan(() => ({
|
|
113
|
+
payload: this.livePreview.data(),
|
|
114
|
+
initialPlan: this.initialPlan,
|
|
115
|
+
resolveOptions: { config: experienceConfig },
|
|
116
|
+
}));
|
|
117
|
+
|
|
118
|
+
protected readonly experienceConfig = experienceConfig;
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
`injectLivePreviewExperience()` starts its subscription after the first browser render.
|
|
123
|
+
Set both `previewToken` and `sessionId` to receive Preview Session updates. The
|
|
124
|
+
initial payload remains available while updates arrive. The browser SDK sends
|
|
125
|
+
the token as the WebSocket URL's `access_token` parameter.
|
|
126
|
+
|
|
127
|
+
`injectExperiencePlan()` calls `resolveExperience()` for each new raw
|
|
128
|
+
payload. It updates `plan.data()` after the resolver returns a complete
|
|
129
|
+
plan. The current plan stays in place until the new plan is ready.
|
|
130
|
+
|
|
131
|
+
The live-preview results also expose an `error` signal when the Preview Session
|
|
132
|
+
connection fails. The last valid `data` remains available.
|
|
133
|
+
|
|
134
|
+
### Renderers
|
|
135
|
+
|
|
136
|
+
Every renderer is standalone; add it to your own component's `imports`.
|
|
137
|
+
|
|
138
|
+
```ts
|
|
139
|
+
ServerExperienceRenderer; // <cf-server-experience> SSR-safe; active viewport seeded from initialViewportId
|
|
140
|
+
ClientExperienceRenderer; // <cf-experience> Subscribes to window.matchMedia
|
|
141
|
+
MissingComponent; // <cf-missing-component> Default fallback for unregistered component types
|
|
142
|
+
NodesRenderer; // *cfNodes Renders a slot's raw nodes (see Slot children)
|
|
143
|
+
NodeRenderer; // *cfNode Renders one node; NodesRenderer loops over it
|
|
144
|
+
DebugExperience; // <cf-debug-experience> Auto-mounted by the renderers when debug is set
|
|
145
|
+
injectActiveViewport; // Signal-backed viewport index; you'll rarely need it directly
|
|
146
|
+
injectLivePreviewExperience; // Signal-backed raw Experience payload
|
|
147
|
+
injectExperiencePlan; // Signal-backed PortableRenderPlan
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
`NodesRenderer` and `NodeRenderer` are **structural directives**, not components, so they add no element of their own — see [Slot children](#slot-children).
|
|
151
|
+
|
|
152
|
+
Each is also exported under its Angular-suffixed class name (`ServerExperienceRendererComponent`, `NodesRendererDirective`, and so on), and `ExperienceRenderer` is an alias for `ClientExperienceRenderer`.
|
|
153
|
+
|
|
154
|
+
### Styling + runtime context (helpers)
|
|
155
|
+
|
|
156
|
+
All four are `inject()`-based: call them from a field initializer or a constructor, like any other Angular injection API.
|
|
157
|
+
|
|
158
|
+
```ts
|
|
159
|
+
injectDesignValues<T>(); // Signal of the resolved design record that auto-fills inputs
|
|
160
|
+
toCss(design, options?); // Turns a design record into a plain style object for [ngStyle]
|
|
161
|
+
injectExperience(); // Signal<RenderContext>: debug, metadata, viewports, activeViewport
|
|
162
|
+
injectContentfulComponent(); // Signal of the raw payload for the enclosing node (or undefined)
|
|
163
|
+
injectContentfulExperienceTemplate(); // Same, for an enclosing coded Experience Template node
|
|
164
|
+
type ToCssOptions;
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Resolved design values (viewport-cascaded + token-resolved server-side) are **auto-filled onto your component's inputs** by key, alongside content. Declaring an `@Input()` per design key you style with is the one recommended path — and in Angular it is also what makes the key arrive, since binding an undeclared input is an error. `injectDesignValues()` returns the same record as a `Signal`, as an escape hatch and as the way to read keys your component didn't declare, which are **dropped** rather than passed (see [Parity table](#parity-table)). Reach for it only for a nested child that isn't itself a registered component, or for design needed outside the render path (an effect, an imperative measurement) — see [Styling components](../../README.md#styling-components). Token resolution is configured with `resolveToken` on your `Config` (`type ResolveToken`).
|
|
168
|
+
|
|
169
|
+
### Re-exported types and utilities
|
|
170
|
+
|
|
171
|
+
```ts
|
|
172
|
+
// From core
|
|
173
|
+
type Config, Components, ExperienceTemplates, Registration, ExperienceTemplateRegistration,
|
|
174
|
+
type ComponentConfig, ExperienceTemplateConfig,
|
|
175
|
+
type ContentfulComponent, ContentfulExperienceTemplate,
|
|
176
|
+
type RenderContext, RenderUnknown, ResolveToken, SlotNodes,
|
|
177
|
+
type ExperiencePayload, ExperienceNode, ComponentNode, ExperienceTemplateNode,
|
|
178
|
+
type ComponentRef, ExperienceTemplateRef, ExperienceSys,
|
|
179
|
+
type PortableRenderPlan, PortableRenderNode, PortableRegistration,
|
|
180
|
+
type DesignPropValue, ManualDesignValue, DesignToken, ValuesByViewport,
|
|
181
|
+
type ViewportDef, ExperienceContext, ResolveContext,
|
|
182
|
+
type ResolverConfig, ResolveExperienceOptions
|
|
183
|
+
|
|
184
|
+
// From live preview and the Angular adapter
|
|
185
|
+
type PreviewSessionOptions, LivePreviewClient,
|
|
186
|
+
type InjectLivePreviewExperienceOptions, InjectLivePreviewExperienceResult,
|
|
187
|
+
type ExperiencePlanResolveOptions, InjectExperiencePlanOptions,
|
|
188
|
+
type InjectExperiencePlanResult
|
|
189
|
+
|
|
190
|
+
// From design (if you want to do your own viewport-aware resolution)
|
|
191
|
+
getValueForViewport, getViewportIndex, resolveDesignProperties, toCssMediaQuery,
|
|
192
|
+
isCssProperty, toCssKey, CSS_PROPERTIES
|
|
193
|
+
|
|
194
|
+
// From client
|
|
195
|
+
createClient, fetchExperience, fetchPreviewSession, ContentfulViewDelivery,
|
|
196
|
+
ContentfulViewDeliveryClient, NotFoundError, PreviewSessionFetchError,
|
|
197
|
+
LivePreviewConnectionError,
|
|
198
|
+
DELIVERY_HOST, PREVIEW_HOST, PREVIEW_WEBSOCKET_HOST
|
|
199
|
+
|
|
200
|
+
type ExperienceOptions, PreviewSessionExperienceOptions, PreviewSessionClientOptions,
|
|
201
|
+
PreviewSessionResolveOptions, ClientOptions, ResolveOptions, CreateClientOptions
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
## Quick reference
|
|
207
|
+
|
|
208
|
+
```ts
|
|
209
|
+
// button.component.ts — content + resolved design both arrive as inputs
|
|
210
|
+
import { Component, Input, signal } from '@angular/core';
|
|
211
|
+
|
|
212
|
+
@Component({
|
|
213
|
+
selector: 'app-button',
|
|
214
|
+
template: `
|
|
215
|
+
@if (urlValue()) {
|
|
216
|
+
<a [href]="urlValue()" [style.background]="bgValue()" [style.color]="colorValue()">
|
|
217
|
+
{{ labelValue() }}
|
|
218
|
+
</a>
|
|
219
|
+
} @else {
|
|
220
|
+
<button type="button" [style.background]="bgValue()" [style.color]="colorValue()">
|
|
221
|
+
{{ labelValue() }}
|
|
222
|
+
</button>
|
|
223
|
+
}
|
|
224
|
+
`,
|
|
225
|
+
})
|
|
226
|
+
export class ButtonComponent {
|
|
227
|
+
protected readonly labelValue = signal('Button');
|
|
228
|
+
protected readonly urlValue = signal<string | undefined>(undefined);
|
|
229
|
+
protected readonly bgValue = signal<string | undefined>(undefined);
|
|
230
|
+
protected readonly colorValue = signal<string | undefined>(undefined);
|
|
231
|
+
|
|
232
|
+
// Decorator inputs bridged into signals, not signal input(): see the parity
|
|
233
|
+
// table. The input name is what the payload key binds to; the readable signal
|
|
234
|
+
// needs a different name, and must be `protected`, not `private`, to stay
|
|
235
|
+
// template-readable under strictTemplates.
|
|
236
|
+
@Input() set label(value: string | undefined) {
|
|
237
|
+
this.labelValue.set(value ?? 'Button');
|
|
238
|
+
}
|
|
239
|
+
@Input() set url(value: string | undefined) {
|
|
240
|
+
this.urlValue.set(value);
|
|
241
|
+
}
|
|
242
|
+
@Input() set backgroundColor(value: string | undefined) {
|
|
243
|
+
this.bgValue.set(value);
|
|
244
|
+
}
|
|
245
|
+
@Input() set color(value: string | undefined) {
|
|
246
|
+
this.colorValue.set(value);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
```ts
|
|
252
|
+
// experience-config.ts
|
|
253
|
+
import {
|
|
254
|
+
defineComponent,
|
|
255
|
+
type Components,
|
|
256
|
+
type Config,
|
|
257
|
+
type ResolveToken,
|
|
258
|
+
} from '@contentful/experiences-angular';
|
|
259
|
+
import { ButtonComponent } from './components/button.component';
|
|
260
|
+
|
|
261
|
+
interface ButtonProps {
|
|
262
|
+
label?: string;
|
|
263
|
+
url?: string;
|
|
264
|
+
backgroundColor?: string; // resolved design, auto-filled
|
|
265
|
+
color?: string;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const components: Components = {
|
|
269
|
+
// Bare component class, or defineComponent({...}) when you need defaults/resolveData.
|
|
270
|
+
Button: defineComponent<ButtonProps>({
|
|
271
|
+
resolveData: ({ content }) => ({ url: ensureScheme(content.url) }),
|
|
272
|
+
component: ButtonComponent,
|
|
273
|
+
}),
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
const resolveToken: ResolveToken = (token) => designTokens[token.value];
|
|
277
|
+
|
|
278
|
+
export const experienceConfig: Config = { components, resolveToken };
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
```ts
|
|
282
|
+
// page.component.ts
|
|
283
|
+
import { Component, Input } from '@angular/core';
|
|
284
|
+
import { ServerExperienceRenderer, type PortableRenderPlan } from '@contentful/experiences-angular';
|
|
285
|
+
import { experienceConfig } from './experience-config';
|
|
286
|
+
|
|
287
|
+
@Component({
|
|
288
|
+
selector: 'app-page',
|
|
289
|
+
imports: [ServerExperienceRenderer],
|
|
290
|
+
template: `<cf-server-experience [experience]="experience" [config]="config" />`,
|
|
291
|
+
})
|
|
292
|
+
export class PageComponent {
|
|
293
|
+
@Input() experience!: PortableRenderPlan;
|
|
294
|
+
protected readonly config = experienceConfig;
|
|
295
|
+
}
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
### Slot children
|
|
299
|
+
|
|
300
|
+
Every slot arrives as an input named after the slot, holding an **array of nodes** (`PortableRenderNode[]`, aliased `SlotNodes`) — not renderable children. Hand the array to `*cfNodes` to render it; to wrap, reorder, or drop children individually, loop the array yourself and render each with `*cfNode`.
|
|
301
|
+
|
|
302
|
+
```ts
|
|
303
|
+
// section.component.ts
|
|
304
|
+
import { Component, Input, signal } from '@angular/core';
|
|
305
|
+
import { NodesRenderer, type SlotNodes } from '@contentful/experiences-angular';
|
|
306
|
+
|
|
307
|
+
@Component({
|
|
308
|
+
selector: 'app-section',
|
|
309
|
+
imports: [NodesRenderer],
|
|
310
|
+
// Common case — render them all:
|
|
311
|
+
template: `<div><ng-container *cfNodes="nodes()"></ng-container></div>`,
|
|
312
|
+
// Or take control of each child, with NodeRenderer in imports instead:
|
|
313
|
+
// <div>
|
|
314
|
+
// @for (child of nodes() ?? []; track $index) {
|
|
315
|
+
// <div class="cell"><ng-container *cfNode="child"></ng-container></div>
|
|
316
|
+
// }
|
|
317
|
+
// </div>
|
|
318
|
+
})
|
|
319
|
+
export class SectionComponent {
|
|
320
|
+
protected readonly nodes = signal<SlotNodes | undefined>(undefined);
|
|
321
|
+
|
|
322
|
+
@Input() set children(value: SlotNodes | undefined) {
|
|
323
|
+
this.nodes.set(value);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
Slot children stay **lazy**: a component that never renders a slot input never instantiates those subtrees.
|
|
329
|
+
|
|
330
|
+
Both are **structural directives**, so the adapter puts no element of its own between you and your children: they render as direct children of the element you wrapped them in, exactly as in React and Svelte. `display: grid` with `gap` on the `<div>` above lays out **the slot children**; `> .card`, `:nth-child(2)`, `:first-child`, and the `+`/`~` combinators all work. (Each directive leaves a comment anchor, as Svelte does — comments are not elements, so they affect neither layout nor any of those selectors.)
|
|
331
|
+
|
|
332
|
+
`children` is not special — it is simply the conventional name for the default slot. **Every** slot in the payload becomes a same-named `SlotNodes` input, so a component with a `header` slot just declares `header` and renders it the same way. This applies identically to coded Experience Templates: a template with a `content` slot receives a `content` input.
|
|
333
|
+
|
|
334
|
+
The same nodes are also on the payload at `injectContentfulComponent()().slots` (a `Record<string, PortableRenderNode[]>`).
|
|
335
|
+
|
|
336
|
+
---
|
|
337
|
+
|
|
338
|
+
## Parity table
|
|
339
|
+
|
|
340
|
+
Everything below is a deliberate divergence from React and Svelte, forced by an Angular primitive. Semantics — merge precedence, the viewport cascade, degradation behaviour, context walk-up — are identical across all three adapters and covered by the same ported test suite.
|
|
341
|
+
|
|
342
|
+
| Concern | React | Svelte | Angular | Why |
|
|
343
|
+
| ------------------------------------------------ | -------------------------------------------------------------------------------------------- | ------------------------------------------------------------ | -------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
344
|
+
| Runtime accessors | `useExperience()`, `useDesignValues()` | `getExperience()`, `getDesignValues()` | `injectExperience()`, `injectDesignValues()` | Angular's DI idiom. Must be called from an injection context. |
|
|
345
|
+
| Accessor return type | plain value (re-renders) | plain value (read in `$derived`) | `Signal<T>` — call it to read | Signals are Angular's reactive primitive. |
|
|
346
|
+
| Renderer usage | `<ServerExperienceRenderer …/>` | `<ServerExperienceRenderer …/>` | `<cf-server-experience …/>` after adding `ServerExperienceRenderer` to `imports` | Angular components are referenced by selector, imported by class. |
|
|
347
|
+
| Slot children | `children: ReactNode` — render directly | `Snippet[]` — `{@render child()}` | `PortableRenderNode[]` — render with `*cfNodes` | Angular has no lazy named-slot primitive; `projectableNodes` is positional and eager. |
|
|
348
|
+
| `NodesRenderer` | not exported | exported (escape hatch) | exported and **load-bearing** | It is the only way to render a slot. |
|
|
349
|
+
| Undeclared merged keys | passed through as props | passed through as props | **dropped** | Binding an input a component does not declare is an error, so the merged record is filtered via `reflectComponentType`. Still readable through `injectDesignValues()`. |
|
|
350
|
+
| Reading dropped keys | n/a | n/a | `injectDesignValues()` | The full resolved design record is always available regardless of declared inputs. |
|
|
351
|
+
| Component inputs | props | `$props()` | `@Input()` setter → `signal` | Signal `input()` is AOT-only; a JIT consumer reports zero declared inputs, which would break `reflectComponentType` filtering. |
|
|
352
|
+
| Input naming | any | any | setter takes the payload key; the readable signal needs a distinct name | A class cannot declare a field and an accessor under one name, and under `useDefineForClassFields: false` the field initializer would assign straight through the setter. |
|
|
353
|
+
| `injectActiveViewport` args | values | values | **getters** (`() => viewports`) | An injection context runs before inputs are bound. Every `Signal` is already a getter, so passing one works unchanged. |
|
|
354
|
+
| Missing-component warning | effect | effect | `ngOnInit` | So the diagnostic also fires during server rendering. |
|
|
355
|
+
| Prop-shape types | inferred | separate `*.ts` per component | not needed | Angular components are `.ts`, so `tsc --noEmit` already resolves them. |
|
|
356
|
+
| Style helper output | `CSSProperties` | plain record | plain record for `[ngStyle]` | Scalar-only, same as Svelte. |
|
|
357
|
+
| `component-render-error` under SSR | caught (internal `<Suspense>` degrades gracefully under both legacy and streaming renderers) | **not caught** — `<svelte:boundary>` doesn't run server-side | caught, identically to CSR | Angular has no separate server renderer — `createComponent` is the same call either way. See the root README's [error-handling section](../../README.md#error-handling--troubleshooting). |
|
|
358
|
+
| `component-render-error` after a later re-render | caught (standard class boundary) | caught (standard `<svelte:boundary>`) | **not caught** — creation-time only | `ApplicationRef` resolves `ErrorHandler` once from the root injector, so a per-node override can't catch a later change-detection throw. Documented gap; see the root README. |
|
|
359
|
+
|
|
360
|
+
**Not** a divergence: the DOM around slot children. React renders them through a fragment, Svelte through no element, and Angular through structural directives — no adapter element in any of the three. Dispatch deliberately does not use components, because an Angular component always has a host element and no configuration removes it; `display: contents` would hide such a wrapper from layout but not from `> .card`, `:nth-child(n)`, or the sibling combinators.
|
|
361
|
+
|
|
362
|
+
For the full getting-started walkthrough, the merge-precedence rules, viewport handling, and design rationale, see the [root README](../../README.md) and [`AGENTS.md`](../../AGENTS.md).
|
|
363
|
+
|
|
364
|
+
---
|
|
365
|
+
|
|
366
|
+
## License
|
|
367
|
+
|
|
368
|
+
MIT. See the repository [`LICENSE`](../../LICENSE) and [`NOTICE`](../../NOTICE) for full attribution.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { type Type } from '@angular/core';
|
|
2
|
+
import type { PortableRenderPlan } from '@contentful/experiences-sdk-core';
|
|
3
|
+
import type { Config } from './types.js';
|
|
4
|
+
import * as i0 from "@angular/core";
|
|
5
|
+
export declare class ClientExperienceRendererComponent {
|
|
6
|
+
protected readonly experienceValue: import("@angular/core").WritableSignal<PortableRenderPlan | null>;
|
|
7
|
+
private readonly debugValue;
|
|
8
|
+
protected readonly resolvedDebug: import("@angular/core").Signal<boolean>;
|
|
9
|
+
private readonly configValue;
|
|
10
|
+
private readonly initialViewportIdValue;
|
|
11
|
+
private readonly metadataValue;
|
|
12
|
+
private readonly renderUnknownValue;
|
|
13
|
+
private readonly renderErrorValue;
|
|
14
|
+
/** A resolved render plan, or `null` while one is still being fetched. */
|
|
15
|
+
set experience(value: PortableRenderPlan | null | undefined);
|
|
16
|
+
set config(value: Config);
|
|
17
|
+
/**
|
|
18
|
+
* Viewport to render for until `matchMedia` takes over after the first render.
|
|
19
|
+
* Set it to whatever the server rendered with so hydration does not flicker.
|
|
20
|
+
*/
|
|
21
|
+
set initialViewportId(value: string | undefined);
|
|
22
|
+
/** Shallow-merges over the plan's `metadata`. Only needed to override it. */
|
|
23
|
+
set metadata(value: Record<string, unknown> | undefined);
|
|
24
|
+
/**
|
|
25
|
+
* Renders the resolved plan above the experience for inspection. Defaults to
|
|
26
|
+
* the plan's `debug`; `[debug]="false"` overrides a debug-on plan.
|
|
27
|
+
*/
|
|
28
|
+
set debug(value: boolean | undefined);
|
|
29
|
+
/** Replaces the default missing-component box. Receives `componentId` and `nodeId`. */
|
|
30
|
+
set renderUnknown(value: Type<unknown> | undefined);
|
|
31
|
+
/** Replaces the default error box rendered when a registered component throws. */
|
|
32
|
+
set renderError(value: Type<unknown> | undefined);
|
|
33
|
+
private readonly tracker;
|
|
34
|
+
private readonly renderContext;
|
|
35
|
+
private readonly scope;
|
|
36
|
+
/** Resolve-time + render-time diagnostics, merged for `<cf-debug-experience>`. */
|
|
37
|
+
protected readonly errors: import("@angular/core").Signal<Error[]>;
|
|
38
|
+
constructor();
|
|
39
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<ClientExperienceRendererComponent, never>;
|
|
40
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<ClientExperienceRendererComponent, "cf-experience", never, { "experience": { "alias": "experience"; "required": true; }; "config": { "alias": "config"; "required": true; }; "initialViewportId": { "alias": "initialViewportId"; "required": false; }; "metadata": { "alias": "metadata"; "required": false; }; "debug": { "alias": "debug"; "required": false; }; "renderUnknown": { "alias": "renderUnknown"; "required": false; }; "renderError": { "alias": "renderError"; "required": false; }; }, {}, never, never, true, never>;
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=client-experience-renderer.component.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client-experience-renderer.component.d.ts","sourceRoot":"","sources":["../src/client-experience-renderer.component.ts"],"names":[],"mappings":"AAaA,OAAO,EAIL,KAAK,IAAI,EAIV,MAAM,eAAe,CAAC;AAEvB,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AAS3E,OAAO,KAAK,EAAE,MAAM,EAAiB,MAAM,YAAY,CAAC;;AAExD,qBAqBa,iCAAiC;IAC5C,SAAS,CAAC,QAAQ,CAAC,eAAe,oEAA2C;IAE7E,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA0C;IACrE,SAAS,CAAC,QAAQ,CAAC,aAAa,0CAE9B;IACF,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA+B;IAC3D,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAyC;IAChF,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA0D;IACxF,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAoD;IACvF,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAkD;IAEnF,0EAA0E;IAC1E,IAA+B,UAAU,CAAC,KAAK,EAAE,kBAAkB,GAAG,IAAI,GAAG,SAAS,EAErF;IAED,IAA+B,MAAM,CAAC,KAAK,EAAE,MAAM,EAElD;IAED;;;OAGG;IACH,IAAa,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAEvD;IAED,6EAA6E;IAC7E,IAAa,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,EAE/D;IAED;;;OAGG;IACH,IAAa,KAAK,CAAC,KAAK,EAAE,OAAO,GAAG,SAAS,EAE5C;IAED,uFAAuF;IACvF,IAAa,aAAa,CAAC,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,SAAS,EAE1D;IAED,kFAAkF;IAClF,IAAa,WAAW,CAAC,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,SAAS,EAExD;IAID,OAAO,CAAC,QAAQ,CAAC,OAAO,CAStB;IAEF,OAAO,CAAC,QAAQ,CAAC,aAAa,CAgB3B;IAEH,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA2B;IAEjD,kFAAkF;IAClF,SAAS,CAAC,QAAQ,CAAC,MAAM,0CAGtB;;yCA1FQ,iCAAiC;2CAAjC,iCAAiC;CAkG7C"}
|