Skip to content

Angular — version compatibility

@mms/pdirect-pay is tested against Angular 17–21 with RxJS 7.8+. The package declares the full peer-dep range so npm won’t complain on any of them.

From package.json:

{
"peerDependencies": {
"@angular/common": "^17.0.0 || ^18.0.0 || ^19.0.0 || ^20.0.0 || ^21.0.0",
"@angular/core": "^17.0.0 || ^18.0.0 || ^19.0.0 || ^20.0.0 || ^21.0.0",
"@angular/forms": "^17.0.0 || ^18.0.0 || ^19.0.0 || ^20.0.0 || ^21.0.0",
"rxjs": "^7.8.0"
}
}
AngularNotes
17Standalone components are stable. Some signals helpers (input() factory, output() factory) are experimental — the SDK does not use them. Works fully.
18Signals stable. Defer blocks stable. Works fully.
19Linked signals, signal-based forms. Works fully.
20inject() improvements, deferrable views. Works fully.
21Latest tested. Works fully.

The SDK’s services return Observables, so RxJS is a peer dep, not a bundled dep. We use BehaviorSubject, map, catchError, and tap — all stable since RxJS 7.x.

If your app is on RxJS 8 (which became GA mid-Angular-21), the SDK works without changes.

The SDK supports both. Every component is standalone: true internally, so you can:

  • Import PdirectPayCheckoutComponent directly into your standalone app — no module imports needed beyond provideHttpClient() and the two injection tokens.
  • Or import PdirectPayModule.forRoot(...) once at the root for a classic module-based app.

There is no API difference between the two paths.

The SDK uses Observables, not signals, internally. Both modes work, but in zoneless mode you must not rely on Angular’s automatic change detection between subscription emissions — wrap state updates in signals on your end if you need them.

When the SDK migrates to signal-based outputs in v2.0.0, that will become the default and the Observable wrappers will remain as a deprecated layer.

The SDK is browser-only. The checkout component touches window.location for redirect handling and uses document for the hosted-checkout form. Wrap usage in isPlatformBrowser(this.platformId) checks if you’re running under @angular/ssr:

import { isPlatformBrowser } from "@angular/common";
import { Component, Inject, PLATFORM_ID } from "@angular/core";
@Component({ /* ... */ })
export class CheckoutComponent {
isBrowser: boolean;
constructor(@Inject(PLATFORM_ID) platformId: object) {
this.isBrowser = isPlatformBrowser(platformId);
}
}
@if (isBrowser) {
<pdirect-pay-checkout [configs]="configs" [paymentBody]="paymentBody" />
}

The component also won’t render meaningfully on the server (it needs DOM events to advance) so this is the right pattern anyway.

The package ships as fesm2022, which targets ES2022. Angular’s default build configuration ships ES2022 from Angular 18+; if you’re on Angular 17 with a custom legacy tsconfig that targets ES2020 or older, raise the target to ES2022:

tsconfig.json
{
"compilerOptions": {
"target": "ES2022",
"module": "ES2022",
// ...
}
}

sideEffects: false is set, so unused exports are eliminated by production builds. Importing PdirectPayApiResponseCode does not pull in the entire payment-status component tree.

The standalone-component approach gives you the smallest bundle — import only the components you actually use.