From 7314fc447483920182beeb7c811cccc18c8cf561 Mon Sep 17 00:00:00 2001 From: Maxim Belov Date: Wed, 12 Aug 2026 10:56:19 +0200 Subject: [PATCH] fix(angular): allow back button subscriptions to be tied to a DestroyRef subscribeWithPriority had no way to stop listening. The Platform it lives on is provided in root, so a subscription taken in a component outlives that component and keeps firing its callback after the component is gone -- for the lifetime of the application. Adds an optional destroyRef parameter. When passed, the stream is piped through takeUntilDestroyed so the subscription ends with the component that opened it. Omitted, behaviour is byte-for-byte what it was. takeUntilDestroyed is given an explicit DestroyRef rather than relying on an injection context, which is what makes it usable from the assignment inside the zone.run callback. --- .../angular/common/src/providers/platform.ts | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/packages/angular/common/src/providers/platform.ts b/packages/angular/common/src/providers/platform.ts index e01c6edb77b..b60981b185a 100644 --- a/packages/angular/common/src/providers/platform.ts +++ b/packages/angular/common/src/providers/platform.ts @@ -1,5 +1,7 @@ import { DOCUMENT } from '@angular/common'; import { NgZone, Inject, Injectable } from '@angular/core'; +import type { DestroyRef } from '@angular/core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { getPlatforms, isPlatform } from '@ionic/core/components'; import type { BackButtonEventDetail, KeyboardEventDetail, Platforms } from '@ionic/core/components'; import { Subscription, Subject } from 'rxjs'; @@ -9,7 +11,13 @@ import { Subscription, Subject } from 'rxjs'; export interface BackButtonEmitter extends Subject { subscribeWithPriority( priority: number, - callback: (processNextHandler: () => void) => Promise | void + callback: (processNextHandler: () => void) => Promise | void, + /** + * Pass a component's `DestroyRef` to have the subscription torn down with that + * component. Without it the subscription lives for the lifetime of the injector, + * which for a root-provided `Platform` means the lifetime of the application. + */ + destroyRef?: DestroyRef ): Subscription; } @@ -62,8 +70,10 @@ export class Platform { constructor(@Inject(DOCUMENT) private doc: any, zone: NgZone) { zone.run(() => { this.win = doc.defaultView; - this.backButton.subscribeWithPriority = function (priority, callback) { - return this.subscribe((ev) => { + this.backButton.subscribeWithPriority = function (priority, callback, destroyRef) { + const source$ = destroyRef ? this.pipe(takeUntilDestroyed(destroyRef)) : this; + + return source$.subscribe((ev) => { return ev.register(priority, (processNextHandler) => zone.run(() => callback(processNextHandler))); }); };