Skip to content

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 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';

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.

app.config.ts
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.

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.

conversations.component.ts
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.

The typed errors of the package page arrive on the Observable’s error channel, so catchError works as usual.

message-sender.ts
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;
}),
);
}
}
  • 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, an InjectionToken and 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.