Skip to content

Angular integration

CDP is a React micro frontend. In an Angular host you do not rewrite it — you embed the pre-built remote and drive it through the @segmentify/common host bridge. Two paths are supported:

Path Recommendation
iframe + postMessage Recommended for most Angular hosts. Works with any Angular version, strong isolation, no React in your bundle.
Module Federation mount Advanced. Deeper UI integration, but you load a React remote into the Angular runtime and must align shared deps.
cdp-embed.component.html
<iframe #cdpFrame [src]="safeSrc" style="width: 100%; height: 800px; border: 0"></iframe>
cdp-embed.component.ts
import { Component, ElementRef, OnDestroy, OnInit, ViewChild } from "@angular/core";
import { DomSanitizer, SafeResourceUrl } from "@angular/platform-browser";
import { Router } from "@angular/router";
import { bindPostMessageHost } from "@segmentify/common";
const CDP_ORIGIN = "https://cdp.segmentify.com";
@Component({
selector: "app-cdp-embed",
templateUrl: "./cdp-embed.component.html",
})
export class CdpEmbedComponent implements OnInit, OnDestroy {
@ViewChild("cdpFrame", { static: true }) frame!: ElementRef<HTMLIFrameElement>;
safeSrc: SafeResourceUrl;
private controller?: ReturnType<typeof bindPostMessageHost>;
constructor(
private sanitizer: DomSanitizer,
private router: Router,
private auth: AuthService // your token/account source
) {
this.safeSrc = this.sanitizer.bypassSecurityTrustResourceUrl(`${CDP_ORIGIN}/embed.html`);
}
ngOnInit(): void {
this.controller = bindPostMessageHost(
this.frame.nativeElement,
{
environment: "production",
hostBaseUrl: "https://sauron.segmentify.com",
cdpBaseUrl: "https://api.segmentify.com", // required for CDP
authToken: this.auth.getBearerToken(),
hostData: {
user: {
user: {
account: {
apiKey: this.auth.getApiKey(),
dataCenter: "gc",
mainCurrency: { code: "USD", symbol: "$" },
},
lang: "en",
},
},
},
requestHeaders: {
"X-Switch-User": this.auth.getSwitchUser(),
"X-Switch-Region": "ALL",
},
},
{
targetOrigin: CDP_ORIGIN,
onReady: () => console.log("CDP ready"),
onNavigate: (path) => this.router.navigateByUrl(path),
onError: (message) => console.error("CDP error:", message),
}
);
}
// Call when the user's token is refreshed
refreshToken(token: string): void {
this.controller?.updateAuthToken(token);
}
// Call when the active account changes
updateAccount(hostData: unknown): void {
this.controller?.updateHostData(hostData as never);
}
ngOnDestroy(): void {
this.controller?.dispose();
}
}

Notes:

  • DomSanitizer.bypassSecurityTrustResourceUrl is only needed because Angular sanitizes [src] bindings. A static src attribute in the template does not require it.
  • Wire onNavigate to Router.navigateByUrl so CDP deep links update the Angular URL.
  • Always dispose() in ngOnDestroy to remove message listeners.

Path B — Module Federation mount (advanced)

Section titled “Path B — Module Federation mount (advanced)”

Use this only if you need CDP composited directly into the Angular DOM (no iframe). The CDP remote is React; your Angular host loads remoteEntry.js and calls mount() into a container element.

With @angular-architects/native-federation (or webpack Module Federation), register the CDP remote:

// federation.config.js (native-federation) — conceptual
{
remotes: {
cdpApp: "https://cdp.segmentify.com/assets/remoteEntry.js",
},
shared: {
react: { singleton: true, requiredVersion: "18.2.0" },
"react-dom": { singleton: true, requiredVersion: "18.2.0" },
"react-intl": { singleton: true },
"@tanstack/react-query": { singleton: true },
"react-hook-form": { singleton: true },
},
}
import { AfterViewInit, Component, ElementRef, OnDestroy, ViewChild } from "@angular/core";
import { Router } from "@angular/router";
import { createEmbedApiClient } from "@segmentify/common";
@Component({
selector: "app-cdp-federated",
template: `<div #cdpRoot></div>`,
})
export class CdpFederatedComponent implements AfterViewInit, OnDestroy {
@ViewChild("cdpRoot", { static: true }) root!: ElementRef<HTMLDivElement>;
private handle?: { unmount: () => void };
constructor(
private router: Router,
private auth: AuthService
) {}
async ngAfterViewInit(): Promise<void> {
// Dynamic import resolved by your federation runtime
const { mount } = await import("cdpApp/mount");
this.handle = await mount(this.root.nativeElement, {
basename: "/app/segmentify/cdp",
host: {
environment: "production",
hostBaseUrl: "https://sauron.segmentify.com",
cdpBaseUrl: "https://api.segmentify.com",
cdpApi: createEmbedApiClient({
baseURL: "https://api.segmentify.com",
getAuthToken: () => this.auth.getBearerToken(),
defaultHeaders: {
"X-Switch-User": this.auth.getSwitchUser(),
"X-Switch-Region": "ALL",
"X-Api-Version": "1.0",
},
}),
hostData: this.auth.getHostData(),
onNavigate: (path: string) => this.router.navigateByUrl(path),
},
});
}
ngOnDestroy(): void {
this.handle?.unmount();
}
}

CDP renders inside a #cdp-fr-app container and sets data-cdp-fr-app on <body>. Do not remove that markup — CDP’s scoped styles rely on it.

Whichever path you choose, the host init requires:

{
environment: "production",
hostBaseUrl: "https://sauron.segmentify.com",
cdpBaseUrl: "https://api.segmentify.com", // required for CDP
authToken: bearerToken,
hostData: { /* apiKey, dataCenter, mainCurrency */ },
requestHeaders: { "X-Switch-User": "", "X-Switch-Region": "ALL" },
}
  • Start with the iframe. It is the lowest-risk integration and isolates React from Angular entirely.
  • Only move to Module Federation if you need CDP inline in the Angular DOM (shared layout chrome, no iframe scroll/resize handling) and can maintain the shared React singleton contract.

See Troubleshooting for shared-dependency and blank-screen issues, and Security for CSP and origin requirements.