ccs-chat-user-client for Angular
The /angular entry point of ccs-chat-user-client wraps ChatUserClient for Angular. Every method returns an Observable, and a provider function and an injection token register the client. It needs Angular 17 or later and RxJS 7.
Install
Section titled “Install”Install the package as described on the package page. @angular/core and rxjs are optional peer dependencies of the package, so your Angular app already provides them. Import from the sub-path:
import { provideChatUserClient, CHAT_USER_CLIENT, createChatUserClient } from '@central-code-station/ccs-chat-user-client/angular';Provide the client
Section titled “Provide the client”provideChatUserClient(factory) registers a ChatUserClientRx under CHAT_USER_CLIENT. The factory returns the same options as ChatUserClient, or an existing ChatUserClient. It runs in an injection context, so it can inject() your own services.
import { inject, Injectable, type ApplicationConfig } from '@angular/core';import { provideChatUserClient } from '@central-code-station/ccs-chat-user-client/angular';import { getChatAccessToken } from './fe-guide-chat-token';
@Injectable({ providedIn: 'root' })export class TenantService { readonly tenantId = 'fitness'; // in a multi-tenant app: from the signed-in user}
export const appConfig: ApplicationConfig = { providers: [ provideChatUserClient(() => ({ chatApiUrl: 'https://chat.centralcodestation.com/api/v1', tenantId: inject(TenantService).tenantId, // the factory runs in an injection context applicationId: 'fitness-app', getAccessToken: getChatAccessToken, })), ],};Put it in a route’s or component’s providers when the tenant or application varies there. To switch tenant or application, create a new client.
Use it
Section titled “Use it”Inject CHAT_USER_CLIENT. Each method of conversations, messages, users and presence, plus me() and capabilities(), returns a cold Observable like HttpClient. Nothing is sent until you subscribe, and each subscription sends the request again. toSignal and the async pipe subscribe for you.
import { Component, inject } from '@angular/core';import { toSignal } from '@angular/core/rxjs-interop';import { CHAT_USER_CLIENT } from '@central-code-station/ccs-chat-user-client/angular';
@Component({ selector: 'app-conversations', standalone: true, template: ` @for (conversation of conversations()?.items; track conversation.conversation_id) { <button (click)="send(conversation.conversation_id)">{{ conversation.name ?? conversation.type }}</button> } `,})export class ConversationsComponent { private readonly chat = inject(CHAT_USER_CLIENT);
// Cold Observable: the request is sent when toSignal subscribes. readonly conversations = toSignal(this.chat.conversations.list({ limit: 30 }));
send(conversationId: string): void { this.chat.messages.send(conversationId, { body: 'See you at 9!' }).subscribe(); }}| Member | Description |
|---|---|
client |
The promise-based ChatUserClient underneath. |
tenantId, applicationId |
The context this client is bound to. |
me(), capabilities() |
Observables of the profile and the user’s Chat permissions. |
conversations, messages, users, presence |
The same methods as the promise client, returning Observables. |
createChatUserClient(options) builds the same wrapper without dependency injection.
Errors
Section titled “Errors”The typed errors of the package page arrive on the Observable’s error channel, so catchError works as usual.
import { inject, Injectable } from '@angular/core';import { catchError, EMPTY, type Observable } from 'rxjs';import { ChatUnprocessableError, type ChatMessage } from '@central-code-station/ccs-chat-user-client';import { CHAT_USER_CLIENT } from '@central-code-station/ccs-chat-user-client/angular';
@Injectable({ providedIn: 'root' })export class MessageSender { private readonly chat = inject(CHAT_USER_CLIENT);
/** Typed errors arrive on the Observable's error channel. */ send(conversationId: string, body: string): Observable<ChatMessage> { return this.chat.messages.send(conversationId, { body }).pipe( catchError((error: unknown) => { if (error instanceof ChatUnprocessableError && error.code === 'conversation_archived') return EMPTY; throw error; }), ); }}What the adapter does not do
Section titled “What the adapter does not do”- It has no
@Injectable()classes. The package ships plain JavaScript with no Angular compiler pass, so a decorated class would lack Ivy metadata. You get a provider function, anInjectionTokenand a factory instead. - It does not cache. Every subscription makes a request. Share results with
shareReplay, a Signal or your own store. - It has no live updates. Pair it with
createChatNotifications. See ccs-chat-notifications-client for Angular. - It does not get the token for you. See Get the user’s Chat token.