# @contentful/experiences-angular 中文文档

规范地址：https://npmcn.com/package/%40contentful/experiences-angular/

版本：0\.8\.9
npm 元数据更新时间：2026-11-06T17:17:26.610Z
中文译文更新时间：2026-11-10T17:29:09.207Z
软件包许可：MIT

本文由上游 README 翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/%40contentful/experiences-angular
原文对照：https://npmcn.com/package/%40contentful/experiences-angular/?lang=original
项目主页：https://github.com/contentful/experiences#readme
源码仓库：https://github.com/contentful/experiences

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：227 kB，75 个文件
- 模块格式：ES Module
- TypeScript 类型：包内自带
- 运行依赖：5 个
- peer 依赖：@angular/core ^20\.0\.0 \|\| ^21\.0\.0 \|\| ^22\.0\.0，@angular/common ^20\.0\.0 \|\| ^21\.0\.0 \|\| ^22\.0\.0
- 首次发布：2026-09-28T21:13:17.423Z
- 最新版本发布：2026-11-06T17:17:26.339Z
- 近 30 天下载量：4194（2026-10-11 至 2026-11-09，npm 官方下载统计）
- 已发布版本数：34（近 12 个月 34 个）

## 中文简介

内容体验的角适配器——服务器和客户端渲染器,覆盖一个可移植渲染器计划

## 安装

```sh
npm install @contentful/experiences-angular
```

也可以使用：`pnpm add @contentful/experiences-angular`、`yarn add @contentful/experiences-angular`、`bun add @contentful/experiences-angular`

## README

# @ contentful/ experiences- angular 语句

> ⚠️ **阿尔法.** 发布至npm. API是不稳定的,并将改变.

内容体验SDK的角适配器. 你带了自己独立的角构件;它使经验载荷从经验交付API(XDA)中与之相伴而来.

```sh
npm install @contentful/experiences-angular
```

支持角 **第20、21和22条**。该软件包将部分常春藤输出由最低支持的编译器编译,因此,您的应用程序中的角链接器可以在其中任何三个上消耗.

这是 **只安装 SDK 软件包**。它会再出口你需要的一切 `@contentful/experiences-sdk-core` 财务报告和审定财务报表 `@contentful/experiences-design`。其他软件包是工作空间-内部.

公共API镜像 `@contentful/experiences-react` 财务报告和审定财务报表 `@contentful/experiences-svelte` 形状; 只有渲染原始物会改变( 独立) `Type<unknown>` 组件, `PortableRenderNode[]` 位置输入代替 `children: ReactNode`,以及 `injectDesignValues()` 改为 `useDesignValues()`) (中文(简体) ). 见 [均等表](https://npmcn.com/package/%40contentful/experiences-angular/#readme-parity-table) 整个地图.

* * *

## 公共 API

### 撰写

```ts
defineComponent<Props>(config); // Type-narrowing identity for component-type configs
defineExperienceTemplate<Props>(config); // Same shape, for coded Experience Template configs
```

### 获取

```ts
fetchExperience(experienceOptions, clientOptions, resolveOptions); // Async; fetches by Destination path/node id or Experience id and resolves in one call
fetchDestinationSitemap(destinationOptions, clientOptions, paginationOptions?); // Async; lists every routable path in a Destination
fetchPreviewSession(previewSessionOptions, clientOptions, resolveOptions); // Async; fetches and resolves a Preview Session Experience
```

从目标服务页面: URL 路径是目标路径 。 重定向节点作为值而不是错误返回. 在目标上不支持预览( 它抛出) `DestinationPreviewNotSupportedError`; 预览时使用代号表.

```ts
const result = await fetchExperience(
  { spaceId, destinationId, path: '/demo/landing' },
  { accessToken },
  { config: experienceConfig }
);
if ('redirect' in result) {
  // follow it: respond with a redirect to result.redirect.path
}
// otherwise `result` is a PortableRenderPlan

// Enumerate every routable path, for example to prerender
const sitemap = await fetchDestinationSitemap({ spaceId, destinationId }, { accessToken });
const paths = sitemap.items.map((item) => item.path); // follow sitemap.pages.next for more
```

使用 `fetchPreviewSession` 内容应用程序时的初始渲染 页:1 `preview_session_id`预览会话 {\\fn黑体\\fs22\\bord1\\shad0\\3ahbe\\4ah00\\fscx67\\fscy66\\2chffffff\\3ch808080}快照并解析成 `PortableRenderPlan`启用 `injectLivePreview` 以接收随后的 WebSocket 更新:

```ts
const plan = await fetchPreviewSession(
  { spaceId, environmentId, sessionId },
  { previewToken },
  { config: experienceConfig }
);
```

如果预览会话引用了来自其他空格的资源,则通过同样的 编码 `resourceResolution` 数值 `previewSessionOptions` 转至 `fetchPreviewSession` 财务报告和审定财务报表 `injectLivePreview`。 。 。 .

### 解析器

```ts
resolveExperience(payload, config, opts?); // Async; walks payload, runs resolveData, returns a PortableRenderPlan
```

### 现场预览

使用 `injectLivePreview()` 当应用程序订阅预览会话时 \* 更新、解决每个有效载荷,并将由此产生的计划从 `data`数字 :

```ts
readonly livePreview = injectLivePreview(() => ({
  previewSessionOptions,
  initialPayload,
  initialPlan,
  resolveOptions: { config: experienceConfig },
}));
```

单独获取原始有效载荷和制作计划,使用 `injectLivePreviewExperience()` 财务报告和审定财务报表 `injectExperiencePlan()`。第一个返回 原始经验载荷。 第二次将有效载荷传递给 `resolveExperience()` 返回 `PortableRenderPlan` 消耗量 变形器。 保持数值分离让应用程序使用原始有效载荷 需要这个.

两位帮手都拿了一把 角质在注射前可以叫注射助手 指定组件输入,因此当 Angular 时,获取器读取当前值 运行帮助者。 每个助手返回带有一个对象 `data` 信号,匹配 其他框架适配器.

```ts
// page.component.ts
import { Component } from '@angular/core';
import {
  ClientExperienceRenderer,
  injectLivePreviewExperience,
  injectExperiencePlan,
  type PortableRenderPlan,
} from '@contentful/experiences-angular';
import { experienceConfig } from './experience-config';

@Component({
  selector: 'app-page',
  imports: [ClientExperienceRenderer],
  template: `<cf-experience [experience]="plan.data()" [config]="experienceConfig" />`,
})
export class PageComponent {
  readonly livePreviewOptions = {
    previewSessionOptions: {
      spaceId: 'space-id',
      environmentId: 'environment-id',
      previewToken: 'preview-token',
      sessionId: 'session-id',
    },
  };
  readonly initialPlan: PortableRenderPlan | undefined = undefined;

  readonly livePreview = injectLivePreviewExperience(() => this.livePreviewOptions);
  readonly plan = injectExperiencePlan(() => ({
    payload: this.livePreview.data(),
    initialPlan: this.initialPlan,
    resolveOptions: { config: experienceConfig },
  }));

  protected readonly experienceConfig = experienceConfig;
}
```

`injectLivePreviewExperience()` 在第一个浏览器渲染后开始订阅 . 设置两者 `previewToken` 财务报告和审定财务报表 `sessionId` 预览会话更新。 这个 初始有效载荷在更新到达时仍然可用。 浏览器 SDK 发送 作为 WebSocket URL 的符号 `access_token` 参数.

`injectExperiencePlan()` 电话 `resolveExperience()` 每个新生 载荷。 内容更新 `plan.data()` 解析器返回完整后 计划 现行计划要坚持到新计划制定完毕.

现场审查的结果也暴露了 `error` 预览会话时的信号 连接失败 。 最后一个有效 `data` 现保留.

### 渲染器

每个渲染器都是独立的;添加到自己的组件中 `imports`。 。 。 .

```ts
ServerExperienceRenderer; // <cf-server-experience>  SSR-safe renderer
ClientExperienceRenderer; // <cf-experience>         Client renderer
MissingComponent; // <cf-missing-component>  Default fallback for unregistered component types
NodesRenderer; // *cfNodes                Renders a slot's raw nodes (see Slot children)
NodeRenderer; // *cfNode                 Renders one node; NodesRenderer loops over it
DebugExperience; // <cf-debug-experience>   Auto-mounted by the renderers when debug is set
injectLivePreviewExperience; // Signal-backed raw Experience payload
injectExperiencePlan; // Signal-backed PortableRenderPlan
```

`NodesRenderer` 财务报告和审定财务报表 `NodeRenderer` 为: **结构指令**,而不是组件,所以它们不增加自己的元素——见 [间隔儿童](https://npmcn.com/package/%40contentful/experiences-angular/#readme-slot-children)。 。 。 .

每个也都在其Angular-sindex类名下被导出(`ServerExperienceRendererComponent`, (中文(简体) ), `NodesRendererDirective`,等等),以及 `ExperienceRenderer` 是用于 `ClientExperienceRenderer`。 。 。 .

### Styling + 运行时上下文 (帮助)

四个人都是 `inject()`\- 基数:从字段初始化器或构造器中调用它们,与其他角注射API一样.

```ts
injectDesignValues<T>(); // Signal of the resolved design record that auto-fills inputs
toCss(design, options?); // Turns a design record into a plain style object for [ngStyle]
injectExperience(); // Signal<RenderContext>: debug, metadata
injectContentfulComponent(); // Signal of the raw payload for the enclosing node (or undefined), including `attribution`
injectContentfulExperienceTemplate(); // Same, for an enclosing coded Experience Template node
getTrackingAttributes(attribution); // Attributes to bind on a node's outermost element for interaction tracking
type ToCssOptions;
```

确定的设计值为 **自动填入组件输入** 和内容一起。 宣布一个 `@Input()` 每个您使用的设计键是推荐的路径——在Angular语中,它也是使键到达的原因,因为绑定一个未宣布的输入是一个错误. `injectDesignValues()` 返回与 a 相同的记录 `Signal`作为逃生舱门 作为读取钥匙的方法 你的部件没有声明 **丢弃** 而不是通过(见 [均等表](https://npmcn.com/package/%40contentful/experiences-angular/#readme-parity-table)) (中文(简体) ). 仅针对并非登记成分的被窝儿童,或用于在渲染道外所需的设计(一种效应,一种必须测量)——见 [样式组件](https://npmcn.com/package/README.md#styling-components)选项 `resolveToken` 在你身边 `Config` (单位:千美元)`type ResolveToken`) (中文(简体) ).

### 再出口类型和公用事业

```ts
// From core
type Config, Components, ExperienceTemplates, Registration, ExperienceTemplateRegistration,
type ComponentConfig, ExperienceTemplateConfig,
type ContentfulComponent, ContentfulExperienceTemplate,
type RenderContext, RenderUnknown, ResolveToken, SlotNodes,
type ExperiencePayload, ExperienceNode, ComponentNode, ExperienceTemplateNode,
type ComponentRef, ExperienceTemplateRef, ExperienceSys,
type PortableRenderPlan, PortableRenderNode, PortableRegistration,
type DesignPropValue, ManualDesignValue, DesignToken, ExperienceContext, ResolveContext,
type ResolverConfig, ResolveExperienceOptions

// From live preview and the Angular adapter
type PreviewSessionOptions, LivePreviewClient,
type InjectLivePreviewExperienceOptions, InjectLivePreviewExperienceResult,
type ExperiencePlanResolveOptions, InjectExperiencePlanOptions,
type InjectExperiencePlanResult

// From design
isCssProperty, toCssKey, CSS_PROPERTIES

// From client
createClient, fetchExperience, fetchDestinationSitemap, fetchPreviewSession, ContentfulViewDelivery,
ContentfulViewDeliveryClient, NotFoundError, DestinationPreviewNotSupportedError, PreviewSessionFetchError,
LivePreviewConnectionError,
DELIVERY_HOST, PREVIEW_HOST, PREVIEW_WEBSOCKET_HOST

type ExperienceOptions, ByIdExperienceOptions, ByDestinationPathExperienceOptions,
  ByDestinationNodeIdExperienceOptions, DestinationRedirectResult, DestinationSitemapResult,
  FetchDestinationSitemapOptions, FetchDestinationSitemapPaginationOptions, SitemapPathItem,
  PreviewSessionExperienceOptions, PreviewSessionClientOptions,
  PreviewSessionResolveOptions, ClientOptions, ResolveOptions, CreateClientOptions
```

* * *

## 快速引用

```ts
// button.component.ts — content + resolved design both arrive as inputs
import { Component, Input, signal } from '@angular/core';

@Component({
  selector: 'app-button',
  template: `
    @if (urlValue()) {
      <a [href]="urlValue()" [style.background]="bgValue()" [style.color]="colorValue()">
        {{ labelValue() }}
      </a>
    } @else {
      <button type="button" [style.background]="bgValue()" [style.color]="colorValue()">
        {{ labelValue() }}
      </button>
    }
  `,
})
export class ButtonComponent {
  protected readonly labelValue = signal('Button');
  protected readonly urlValue = signal<string | undefined>(undefined);
  protected readonly bgValue = signal<string | undefined>(undefined);
  protected readonly colorValue = signal<string | undefined>(undefined);

  // Decorator inputs bridged into signals, not signal input(): see the parity
  // table. The input name is what the payload key binds to; the readable signal
  // needs a different name, and must be `protected`, not `private`, to stay
  // template-readable under strictTemplates.
  @Input() set label(value: string | undefined) {
    this.labelValue.set(value ?? 'Button');
  }
  @Input() set url(value: string | undefined) {
    this.urlValue.set(value);
  }
  @Input() set backgroundColor(value: string | undefined) {
    this.bgValue.set(value);
  }
  @Input() set color(value: string | undefined) {
    this.colorValue.set(value);
  }
}
```

```ts
// experience-config.ts
import {
  defineComponent,
  type Components,
  type Config,
  type ResolveToken,
} from '@contentful/experiences-angular';
import { ButtonComponent } from './components/button.component';

interface ButtonProps {
  label?: string;
  url?: string;
  backgroundColor?: string; // resolved design, auto-filled
  color?: string;
}

const components: Components = {
  // Bare component class, or defineComponent({...}) when you need defaults/resolveData.
  Button: defineComponent<ButtonProps>({
    resolveData: ({ content }) => ({ url: ensureScheme(content.url) }),
    component: ButtonComponent,
  }),
};

const resolveToken: ResolveToken = (token) => designTokens[token.value];

export const experienceConfig: Config = { components, resolveToken };
```

```ts
// page.component.ts
import { Component, Input } from '@angular/core';
import { ServerExperienceRenderer, type PortableRenderPlan } from '@contentful/experiences-angular';
import { experienceConfig } from './experience-config';

@Component({
  selector: 'app-page',
  imports: [ServerExperienceRenderer],
  template: `<cf-server-experience [experience]="experience" [config]="config" />`,
})
export class PageComponent {
  @Input() experience!: PortableRenderPlan;
  protected readonly config = experienceConfig;
}
```

### 间隔儿童

每一个插槽都作为以插槽命名的输入到达,并持有 **节点数组** (单位:千美元)`PortableRenderNode[]`,别名 `SlotNodes`()——不能生产的儿童。 把阵列交给 `*cfNodes` \* 将阵列自行循环,使每个阵列与 `*cfNode`。 。 。 .

```ts
// section.component.ts
import { Component, Input, signal } from '@angular/core';
import { NodesRenderer, type SlotNodes } from '@contentful/experiences-angular';

@Component({
  selector: 'app-section',
  imports: [NodesRenderer],
  // Common case — render them all:
  template: `<div><ng-container *cfNodes="nodes()"></ng-container></div>`,
  // Or take control of each child, with NodeRenderer in imports instead:
  //   <div>
  //     @for (child of nodes() ?? []; track $index) {
  //       <div class="cell"><ng-container *cfNode="child"></ng-container></div>
  //     }
  //   </div>
})
export class SectionComponent {
  protected readonly nodes = signal<SlotNodes | undefined>(undefined);

  @Input() set children(value: SlotNodes | undefined) {
    this.nodes.set(value);
  }
}
```

孩子们留下 **懒惰**:一个从不制作插槽输入的组件,永远不会对子树进行即兴播放.

两者都是 **结构指令**,所以适配器在你和你的孩子之间没有它自己的元素:它们将你所包裹的元素作为直接的孩子,就像React和Svelte中一样. `display: grid` 与 `gap` 编辑 `<div>` 以上列出 **时间档子**· ; `> .card`, (中文(简体) ), `:nth-child(2)`, (中文(简体) ), `:first-child`,则 `+`编号 :`~` 组合器全部工作。 (每个指令都留下了评论锚,就像斯维尔特所做的那样——评论不是元素,所以它们既不影响布局,也不影响这些选择者. )

`children` 不是特别的——它只是默认槽的常规名称. **每个** 有效载荷中的槽变成同名的 `SlotNodes` 输入,因此一个组件带有 `header` 位置刚刚宣布 `header` 并让它同样的方式。 这同样适用于编码的经验模板:带有 `content` 槽收到一个 `content` 输入.

同样的节点也在有效载荷上 `injectContentfulComponent()().slots` 联合国 `Record<string, PortableRenderNode[]>`) (中文(简体) ).

* * *

## 均等表

下面的一切都是与React和Svelte的刻意分化,由Angular原始人所强迫. 语义学——合并优先,退化行为,和上下文走行部——在所有三个适配器上都是一样的,并被同一端口测试套件所覆盖.

| 关切 | 反应 | 丝绒 | 角质 | 为什么 |
| --- | --- | --- | --- | --- |
| 运行时间存取器 | `useExperience()`, (中文(简体) ), `useDesignValues()` | `getExperience()`, (中文(简体) ), `getDesignValues()` | `injectExperience()`, (中文(简体) ), `injectDesignValues()` | (原始内容存档于2019-10-12). Angular's DI idom. 必须从注射语境中调用. |
| 访问器返回类型 | 纯值( 重新提交) | 纯值( 在 `$derived`(中文(简体) ) | `Signal<T>` - 叫它读 | 信号是Angular的活性原始. |
| 渲染器的使用 | `<ServerExperienceRenderer …/>` | `<ServerExperienceRenderer …/>` | `<cf-server-experience …/>` 添加后 `ServerExperienceRenderer` 改为 `imports` | 角构件由选择器引用,按类导入. |
| 间隔儿童 | `children: ReactNode` 直接执行 | `Snippet[]` —— 说 `{@render child()}` | `PortableRenderNode[]` - 与 `*cfNodes` | 角质没有懒惰的名地原始; `projectableNodes` 立场和渴望. |
| `NodesRenderer` | 未导出 | 导出( 逃出舱口) | 出口和出口 **负载** | 只有这样才有时间. |
| 未声明的合并密钥 | 作为道具经过 | 作为道具经过 | **丢弃** | 绑定一个输入,一个组件不声明是一个错误,所以合并记录是通过 `reflectComponentType`。仍然可以通过 `injectDesignValues()`。 。 。 . |
| 读取丢弃的密钥 | 无 | 无 | `injectDesignValues()` | 完全解析的设计记录,不论申报的投入如何,一律备有. |
| 构成部分投入 | 道具 | `$props()` | `@Input()` 设置 `signal` | 信号 `input()` 仅 AOT ; JIT 消费者报告为零,将中断输入 `reflectComponentType` 过滤中. |
| 输入命名 | 任意 | 任意 | setter取出有效载荷键;可读信号需要一个明显的名称 | 类不能以一个名称并按 `useDefineForClassFields: false` 现场初始化器会直接通过定点器分配. |
| 缺少组件警告 | 效果 | 效果 | `ngOnInit` | 因此,在服务器渲染过程中,诊断也会起火. |
| 平面形状类型 | 推断 | 单独 `*.ts` 每个构成部分 | 无需 | 角构件为 `.ts`这样 `tsc --noEmit` 已经解决了. |
| 样式助手输出 | `CSSProperties` | 纯记录 | 简单记录 `[ngStyle]` | 只有斯卡拉,和斯维尔特一样. |
| `component-render-error` 在安全部门改革项下 | 已捕获( 内部 `<Suspense>` 优雅地贬低了遗产和流出物) | **未捕获** —— 说 `<svelte:boundary>` 不运行服务器侧 | 和公司社会责任一样 | Angular没有单独的服务器渲染器—— `createComponent` 无论是哪种方式,都一样。 见根 README 的 [错误处理部分](https://npmcn.com/package/README.md#error-handling--troubleshooting)。 。 。 . |
| `component-render-error` 稍后再次交接后 | 已捕获( 标准类边界) | 捕获(标准) `<svelte:boundary>`(中文(简体) ) | **未捕获** ——只创建时间 | `ApplicationRef` 决定 `ErrorHandler` 一次从根注射器, 所以一个每个节点的覆盖 无法捕捉到一个后来的改变-检测抛出。 已记录缺口; 参见根 README . |

**没有** a 差异:DOM环绕子档. 反应使它们通过一个片段,斯维尔特通过没有元素,而Angular通过结构指令——在任何三个中都没有适配器元素. a. 调度故意不使用组件,因为Angular组件总是有一个主机元素,没有配置可以移除; `display: contents` 会隐藏这样的包装 从布局,但不是从 `> .card`, (中文(简体) ), `:nth-child(n)`或者兄弟会的组合者.

关于全面开始的走过、合并优先规则和设计原理,见 [根读取](https://npmcn.com/package/README.md) 财务报告和审定财务报表 [`AGENTS.md`](https://npmcn.com/package/AGENTS.md)。 。 。 .

* * *

## 许可证

麻省理工学院. 见寄存器 [`LICENSE`](https://npmcn.com/package/LICENSE) 财务报告和审定财务报表 [`NOTICE`](https://npmcn.com/package/NOTICE) 完全归属.

## 运行时间参数

位码 `fetchExperience` 接受 `parameters`,例如 `{ featuredPosts: { limit: 6, order: '-fields.title' } }`斯卡拉尔是弦或 数字; 记录叶有一个关卡。 命令是签字的 `fields.<apiName>` 或者说 `sys.<id>` 路径。 以已改变的值进行回溯,然后替换出质器的计划. 出口包括 `ExperienceParameters`, (中文(简体) ), `ExperienceParameterValue`, (中文(简体) ), `ExperienceParameterRecord`,以及 `ExperienceParameterOrder`。 。 。 . 见 [收藏/比较示例](https://npmcn.com/package/README.md#runtime-experience-parameters)。 。 。 .

## 常见问题

### @contentful/experiences-angular 是什么？

内容体验的角适配器——服务器和客户端渲染器,覆盖一个可移植渲染器计划

### 如何安装 @contentful/experiences-angular？

在项目目录运行 npm install @contentful/experiences-angular，或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖：@angular/core（^20\.0\.0 \|\| ^21\.0\.0 \|\| ^22\.0\.0）、@angular/common（^20\.0\.0 \|\| ^21\.0\.0 \|\| ^22\.0\.0）。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖，Yarn 需要手动安装。

### @contentful/experiences-angular 支持 TypeScript 吗？

支持。包内自带 TypeScript 类型声明（package\.json 声明了 types 入口），安装后即可获得类型提示，无需再安装 @types 包。

### @contentful/experiences-angular 支持 ESM 还是 CommonJS？

以 ES Module 形式发布，推荐使用 import 引入。在 CommonJS 项目中，Node\.js 20\.19\+ / 22\.12\+ 可以直接 require\(\) 不含顶层 await 的 ES Module，更早的版本需要改用动态 import\(\)。

### @contentful/experiences-angular 可以商用吗？

@contentful/experiences-angular 使用 MIT 许可。宽松许可：允许商业使用、修改和再分发，通常只需在分发时保留原版权与许可声明。以上为概括，不构成法律意见，请以许可原文为准。

### @contentful/experiences-angular 的下载量有多少？

根据 npm 官方下载统计，@contentful/experiences-angular 在 2026/10/11 – 2026/11/09 这 30 天内被下载 4,194 次，日均约 140 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入，适合横向比较热度，不能直接等同于使用人数。

### @contentful/experiences-angular 还在维护吗？

最新版本 0\.8\.9 发布于 2026/11/07（4 天前）；至今共发布 34 个版本，其中过去 12 个月发布了 34 个；首次发布于 2026/09/29。发布频率只是参考信号：功能稳定的工具可能很少需要更新，建议结合源码仓库的 issue 和提交记录判断。

