Angular Integration (`@angular/core` v14–18+)
Complete guide for integrating ParityEdge localized purchasing power parity pricing into Angular applications using **Standalone Components**, **Angular Signals (`toSignal`)**, **RxJS `HttpClient` Services**, and **Shadow DOM Web Components**.
1. Enable `CUSTOM_ELEMENTS_SCHEMA` & Load Global CDN
Because the ParityEdge banner is rendered in an isolated Shadow DOM custom element (<parityedge-banner>), configure your Angular component or module with CUSTOM_ELEMENTS_SCHEMA:
Step A: Load CDN Script in `src/index.html` (or `angular.json`)
<!-- src/index.html -->
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>My Angular App</title>
<base href="/">
<!-- ParityEdge Sub-2ms Edge CDN Script -->
<script
src="https://parityedge-edge-api.g-saichakri.workers.dev/v1/parity.global.js"
async
></script>
</head>
<body>
<app-root></app-root>
</body>
</html>Step B: Configure Standalone Angular Component (`pricing.component.ts`)
import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { CommonModule } from '@angular/common';
@Component({
selector: 'app-pricing',
standalone: true,
imports: [CommonModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA], // Enables <parityedge-banner> web component
template: `
<div class="pricing-container">
<!-- 0-CLS Host-Isolated Shadow DOM Banner -->
<parityedge-banner
[attr.project-id]="projectId"
theme="dark"
position="bottom-pill">
</parityedge-banner>
<h1>Simple, Predictable Pricing</h1>
<!-- Pricing cards here -->
</div>
`,
styles: [`
.pricing-container {
min-height: 100vh;
background-color: #0a0e17;
color: #ffffff;
padding: 3rem 1rem;
}
`]
})
export class PricingComponent {
projectId = 'prj_live_YOUR_PROJECT_ID';
}2. Type-Safe `ParityEdgeService` with Angular Signals
For full UI control, inject an Angular HTTP Service that communicates with the ParityEdge Edge API, exposing reactive state via **Angular Signals (`toSignal`)**:
`src/app/services/parity-edge.service.ts`
import { Injectable, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable, shareReplay, catchError, of } from 'rxjs';
export interface ParityResolution {
eligible: boolean;
countryCode: string;
countryName: string;
discountPercentage: number;
couponCode: string;
currency: string;
isVpn: boolean;
ui?: {
theme: string;
position: string;
message: string;
};
}
@Injectable({
providedIn: 'root'
})
export class ParityEdgeService {
private http = inject(HttpClient);
private edgeApiUrl = 'https://parityedge-edge-api.g-saichakri.workers.dev/v1/resolve';
resolveParity(projectId: string): Observable<ParityResolution | null> {
return this.http.get<ParityResolution>(`${this.edgeApiUrl}?projectId=${projectId}`).pipe(
shareReplay(1),
catchError((err) => {
console.warn('[ParityEdge] Resolution fallback to default USD:', err);
return of(null);
})
);
}
}`src/app/components/pricing-table.component.ts` (Angular 16+)
import { Component, inject, computed } from '@angular/core';
import { toSignal } from '@angular/core/rxjs-interop';
import { CommonModule } from '@angular/common';
import { ParityEdgeService } from '../services/parity-edge.service';
@Component({
selector: 'app-pricing-table',
standalone: true,
imports: [CommonModule],
template: `
<div class="card">
<h3>Pro Tier</h3>
<!-- Dynamic Price Calculation with Signal -->
<div class="price">
${{ discountedPrice() }} <span>/ month</span>
</div>
<!-- Discount Notification Pill -->
@if (parity()?.eligible) {
<div class="discount-badge">
<span>{{ parity()?.discountPercentage }}% Parity Discount applied for {{ parity()?.countryName }}!</span>
<code>Code: {{ parity()?.couponCode }}</code>
</div>
}
<button (click)="checkout()">Subscribe Now</button>
</div>
`
})
export class PricingTableComponent {
private parityService = inject(ParityEdgeService);
private baseUsdPrice = 49;
// Reactive Signal directly from Edge API
readonly parity = toSignal(
this.parityService.resolveParity('prj_live_YOUR_PROJECT_ID')
);
// Computed signal for discounted price
readonly discountedPrice = computed(() => {
const data = this.parity();
if (!data || !data.eligible) return this.baseUsdPrice;
return (this.baseUsdPrice * (1 - data.discountPercentage / 100)).toFixed(2);
});
checkout() {
const coupon = this.parity()?.couponCode;
window.location.href = coupon
? `https://buy.stripe.com/test_123?prefilled_promo_code=${coupon}`
: 'https://buy.stripe.com/test_123';
}
}3. Legacy NgModule Support (`app.module.ts`)
If your project uses classic Angular NgModules, declare CUSTOM_ELEMENTS_SCHEMA in your module definition:
// app.module.ts
import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HttpClientModule } from '@angular/common/http';
import { AppComponent } from './app.component';
@NgModule({
declarations: [AppComponent],
imports: [BrowserModule, HttpClientModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
bootstrap: [AppComponent]
})
export class AppModule {}