Skip to content

Angular integration guide

This guide covers patterns for integrating the Univerx Client SDK into Angular applications, including reactive state management and Angular Universal (SSR) considerations.

Create and destroy the widget instance using the ngOnInit and ngOnDestroy lifecycle hooks. Implement OnDestroy to guarantee cleanup when the component is removed.

import { Component, OnInit, OnDestroy } from '@angular/core';
import { WidgetClient } from '@univerx/client-sdk';
@Component({
selector: 'app-support-widget',
template: '', // SDK renders its own UI
})
export class SupportWidgetComponent implements OnInit, OnDestroy {
private widget: WidgetClient | null = null;
ngOnInit(): void {
this.widget = new WidgetClient({
widgetKey: 'your-widget-key',
onAgentAssigned: (agent) => console.log(`Agent: ${agent.name}`),
onCallEnded: () => this.widget?.destroy(),
});
this.widget.init();
}
ngOnDestroy(): void {
this.widget?.destroy();
this.widget = null;
}
}

For Angular 17+ applications using signals, expose widget state as a Signal so your template reacts automatically:

import { Component, OnInit, OnDestroy, signal } from '@angular/core';
import { WidgetClient, WidgetState, Agent } from '@univerx/client-sdk';
@Component({
selector: 'app-support-widget',
standalone: true,
template: `
@if (state() === 'ready') {
<button (click)="joinQueue()">Get support</button>
}
@if (state() === 'queued') {
<p>Queue position: {{ queuePosition() }}</p>
}
@if (state() === 'in_call') {
<button (click)="endCall()">End call</button>
}
`,
})
export class SupportWidgetComponent implements OnInit, OnDestroy {
private widget: WidgetClient | null = null;
state = signal<WidgetState>('idle');
queuePosition = signal<number | null>(null);
currentAgent = signal<Agent | null>(null);
ngOnInit(): void {
this.widget = new WidgetClient({
widgetKey: 'your-widget-key',
onReady: () => this.state.set('ready'),
onCallStarted: () => this.state.set('in_call'),
onCallEnded: () => this.state.set('ended'),
});
this.widget.on('queue:update', (position) => this.queuePosition.set(position));
this.widget.on('queue:left', () => this.queuePosition.set(null));
this.widget.on('agent:assigned', (agent) => this.currentAgent.set(agent));
this.widget.init();
}
async joinQueue(): Promise<void> {
await this.widget?.joinQueue({ terms: true });
this.state.set('queued');
}
async endCall(): Promise<void> {
await this.widget?.endSession();
}
ngOnDestroy(): void {
this.widget?.destroy();
this.widget = null;
}
}

For applications that need to share the widget instance across multiple components, wrap it in an Angular service:

import { Injectable, OnDestroy } from '@angular/core';
import { WidgetClient, WidgetConfig, WidgetState } from '@univerx/client-sdk';
import { BehaviorSubject } from 'rxjs';
@Injectable({ providedIn: 'root' })
export class WidgetService implements OnDestroy {
private widget: WidgetClient | null = null;
readonly state$ = new BehaviorSubject<WidgetState>('idle');
readonly queuePosition$ = new BehaviorSubject<number | null>(null);
init(config: WidgetConfig): void {
if (this.widget) return; // already initialised
this.widget = new WidgetClient({
...config,
onReady: () => this.state$.next('ready'),
onCallStarted: () => this.state$.next('in_call'),
onCallEnded: () => this.state$.next('ended'),
});
this.widget.on('queue:update', (pos) => this.queuePosition$.next(pos));
this.widget.on('queue:left', () => this.queuePosition$.next(null));
this.widget.init();
}
async joinQueue(consents?: Record<string, boolean>) {
return this.widget?.joinQueue(consents);
}
async endSession() {
return this.widget?.endSession();
}
ngOnDestroy(): void {
this.widget?.destroy();
this.widget = null;
}
}

Inject the service where needed:

import { Component, OnInit } from '@angular/core';
import { WidgetService } from './widget.service';
@Component({
selector: 'app-root',
template: `...`,
})
export class AppComponent implements OnInit {
constructor(public widgetService: WidgetService) {}
ngOnInit(): void {
this.widgetService.init({ widgetKey: 'your-widget-key' });
}
}

Handle cobrowsing consent by listening for the event and updating component state:

this.widget.on('cobrowse:consent:required', ({ accept, decline }) => {
// Store handlers and show your consent dialog
this.cobrowseHandlers = { accept, decline };
this.showConsentDialog = true;
});

Angular Universal renders components on the server where window and document are not available. Guard all SDK instantiation with isPlatformBrowser:

import { Component, OnInit, OnDestroy, Inject, PLATFORM_ID } from '@angular/core';
import { isPlatformBrowser } from '@angular/common';
import { WidgetClient } from '@univerx/client-sdk';
@Component({
selector: 'app-support-widget',
template: '',
})
export class SupportWidgetComponent implements OnInit, OnDestroy {
private widget: WidgetClient | null = null;
constructor(@Inject(PLATFORM_ID) private platformId: object) {}
ngOnInit(): void {
if (!isPlatformBrowser(this.platformId)) return;
this.widget = new WidgetClient({ widgetKey: 'your-widget-key' });
this.widget.init();
}
ngOnDestroy(): void {
this.widget?.destroy();
this.widget = null;
}
}