用途
该技能是 CometChat Angular UI Kit 的“项目形态”胶水层,用于解决接入时的项目结构问题,而不是 CometChat API 本身的问题。当阻塞点在于 monorepo、NgModule、SSR 或配置存放位置时,应使用本技能。CometChat 的 API 表面属于 cometchat-angular-v5-core,任何符号可通过 cometchat-angular-v5-core/references/docs-map.md 获取;聊天页面的路由放置属于 cometchat-angular-v5-placement。
先检测项目形态
技能通过以下信号判断项目形态并给出差异点:
- 存在 nx.json:Nx 工作区,差异在 env 文件、项目级样式、路径别名。
- 存在 angular.json + src/main.ts:Angular CLI,默认形态,core 的配方可直接套用。
- 存在 app.module.ts:NgModule 应用,组件需放入模块的 imports 数组。
- 存在 server.ts / @angular/ssr:SSR / Universal,需要浏览器端守卫。
NgModule 而非 standalone
UI Kit 的组件是 standalone 的,因此 NgModule 应用需要把它们导入到模块的 imports 数组中,而不是 declarations。示例:
// app.module.ts
import { NgModule } from '@angular/core';
import { CometChatConversationsComponent, CometChatMessageListComponent } from '@cometchat/chat-uikit-angular';
import { ChatComponent } from './chat/chat.component'; // YOUR (non-standalone) component
@NgModule({
declarations: [ChatComponent],
imports: [CometChatConversationsComponent, CometChatMessageListComponent],
})
export class AppModule {}
不要把它们加入 declarations——它们不属于你声明,Angular 会报错。初始化也从 main.ts 移到模块上的 APP_INITIALIZER provider。
Nx 工作区
有三处变化:
- 环境文件位于应用项目下,例如 apps/<app>/src/environments/environment.ts。fileReplacements 放在该项目的 project.json 中,而不是根 angular.json。
- UI Kit 样式表在应用项目的 build styles 数组中注册,路径相对于工作区根:node_modules/@cometchat/chat-uikit-angular/styles/css-variables.css。
- 共享包装库可能重新导出 UI Kit 组件;重新导出的库也必须在其自身 imports 中列出这些组件,否则消费者会遇到静默不渲染。
Nx 完全不改变 CometChat API,只改变配置存放位置。
SSR / Angular Universal
UI Kit 是浏览器客户端。SDK 会接触 window、localStorage 和 WebSockets,这些在服务端都不存在。示例:
import { Component, inject, PLATFORM_ID } from '@angular/core';
import { isPlatformBrowser } from '@angular/common';
import { CometChatUIKit } from '@cometchat/chat-uikit-angular';
import { environment } from '../environments/environment';
@Component({ selector: 'app-root', standalone: true, template: '<router-outlet />' })
export class AppComponent {
constructor() {
if (isPlatformBrowser(inject(PLATFORM_ID))) {
const { appId, region, authKey } = environment.cometchat;
// init only in the browser — on the server this throws "window is not defined"
CometChatUIKit.initFromSettings({
appId, region,
credentials: { authKey },
chatSDK: { presenceSubscription: { type: 'ALL_USERS' } },
});
}
}
}
规则:
- 每个 init/login 调用都要用 isPlatformBrowser 守卫。
- 聊天组件只在客户端渲染(hydration 上用 @defer,或基于浏览器标志的 *ngIf)。服务端渲染它们会产生立即不匹配的标记。
- ThemeService 已经是 SSR 安全的——它通过 DOCUMENT token 写入 data-theme,而不是直接接触 document。
- 不要尝试预渲染聊天路由;没有有意义的内容可预渲染。
注意:Angular UI Kit 文档未覆盖 SSR / Angular Universal。上述守卫是根据 SDK 所接触的内容(window、localStorage、WebSockets)推导出来的,属于本技能包自身的指导——文档中未提及并不代表它们不必要。
懒加载聊天路由
{ path: 'chat', loadComponent: () => import('./chat/chat.component').then(m => m.ChatComponent) }
懒加载路由是可行且推荐的——UI Kit 体积较大。初始化仍在 bootstrap 时执行一次,而不是在懒加载 chunk 中,否则首次导航会付出代价,第二次导航会重复执行。
状态——signals 或 RxJS,二选一
ChatStateService 同时暴露两者(activeUser signal、activeUser$ observable)。每个组件选择一种风格并保持一致;混用会导致同一值出现两条更新路径。
- 模板中使用 signals:无需订阅、无需清理、无需 async pipe。
- 与其他流组合时使用 observables:始终在 ngOnDestroy 中 takeUntil(destroy$)。
- 不要把任一者镜像到自己的字段中——参见 placement 的“两个真相来源”陷阱。
常见陷阱
- UI Kit 组件放入 declarations → Angular 报错;它们属于 imports。
- SSR 下未守卫的 init → 构建或首次请求时 window is not defined。
- init 放在懒加载 chunk 内 → 执行过晚,或执行两次。
- Nx:在 工作区根编辑 env/styles → 静默无效;它们属于应用项目。
- 同一值同时使用 signals 和 observables → 两条更新路径,UI 漂移。
验证是否生效
ng build(以及适用时的 ng build --ssr)通过 · 聊天在浏览器中渲染 · 服务端无 window is not defined · 导航到懒加载路由一次只初始化一次 · 配置在你构建的每种 configuration 中都能解析。