Angular Integration
This guide shows a safe Angular-first integration pattern:
- Load SDK after consent
- Initialize widget in a lifecycle-safe place
- Handle events in application code
- Destroy widget on component teardown
1) Install SDK (npm mode)
2) Consent-aware script loading (if you load from script/GTM)
If your team loads the SDK via GTM, keep this rule:
- GTM: loads SDK script after consent
- Angular app: initializes widget and handles events
Warning
Do not store production secrets in GTM tags or static frontend files.
3) Angular service example
Create a small service to own widget lifecycle.
import { Injectable } from "@angular/core";
import { SophiWidget } from "@usesophi/sophi-web-sdk";
@Injectable({ providedIn: "root" })
export class SophiWidgetService {
private widget: SophiWidget | null = null;
async init(container: string, userId?: string, userName?: string): Promise<void> {
this.widget = new SophiWidget();
await this.widget.init({
apiKey: "YOUR_SOPHI_API_KEY",
container,
userId,
userName,
environment: "production",
onError: (error) => console.error("Sophi init error", error),
});
}
onAddToCart(handler: (data: { products: Array<{ productId: string; variantId?: string }> }) => void) {
this.widget?.on("add_to_cart", handler);
}
onSendToCheckout(handler: () => void) {
this.widget?.on("send_to_checkout", handler);
}
onError(handler: (error: Error) => void) {
this.widget?.on("error", handler);
}
async destroy(): Promise<void> {
if (!this.widget) return;
await this.widget.destroy();
this.widget = null;
}
}
4) Angular component example
Use ngAfterViewInit so the container exists before init().
import { AfterViewInit, Component, OnDestroy } from "@angular/core";
import { Router } from "@angular/router";
import { SophiWidgetService } from "./sophi-widget.service";
@Component({
selector: "app-sophi-chat",
template: `<div id="sophi-widget-container" style="width: 100%; height: 600px;"></div>`,
})
export class SophiChatComponent implements AfterViewInit, OnDestroy {
constructor(
private readonly sophiWidgetService: SophiWidgetService,
private readonly router: Router,
) {}
async ngAfterViewInit(): Promise<void> {
// Optional: gate by your consent manager state before init.
await this.sophiWidgetService.init("#sophi-widget-container", "user-123", "Jane");
this.sophiWidgetService.onAddToCart(async (data) => {
for (const product of data.products) {
// Call your own cart action/API here
console.log("Add to cart", product.productId, product.variantId);
}
});
this.sophiWidgetService.onSendToCheckout(() => {
this.router.navigateByUrl("/checkout");
});
this.sophiWidgetService.onError((error) => {
console.error("Sophi widget error", error);
});
}
async ngOnDestroy(): Promise<void> {
await this.sophiWidgetService.destroy();
}
}
5) If using browser global in Angular
If SDK is loaded via script/GTM, instantiate through window.SophiWebSDK:
You can keep the same lifecycle and event pattern shown above.