Skip to content

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)

npm install @usesophi/sophi-web-sdk

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:

const widget = new window.SophiWebSDK.SophiWidget();

You can keep the same lifecycle and event pattern shown above.