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.
Basic integration
Section titled “Basic integration”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; }}Reactive state with signals
Section titled “Reactive state with signals”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; }}Widget as an injectable service
Section titled “Widget as an injectable service”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' }); }}Cobrowsing consent
Section titled “Cobrowsing consent”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 (SSR)
Section titled “Angular Universal (SSR)”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; }}