From e0f3e8a0eceb0410c33a9c830658c595a8c2a486 Mon Sep 17 00:00:00 2001 From: Philipp Kunz Date: Sat, 31 May 2025 22:18:34 +0000 Subject: [PATCH] fix(lifecycle-component): support 'once' option for event listeners --- certs/static-route/meta.json | 6 +++--- test/core/utils/test.lifecycle-component.ts | 4 ++-- ts/core/utils/lifecycle-component.ts | 24 ++++++++++++++++++--- 3 files changed, 26 insertions(+), 8 deletions(-) diff --git a/certs/static-route/meta.json b/certs/static-route/meta.json index dadce34..dac82ca 100644 --- a/certs/static-route/meta.json +++ b/certs/static-route/meta.json @@ -1,5 +1,5 @@ { - "expiryDate": "2025-08-27T14:28:53.471Z", - "issueDate": "2025-05-29T14:28:53.471Z", - "savedAt": "2025-05-29T14:28:53.473Z" + "expiryDate": "2025-08-29T18:29:48.329Z", + "issueDate": "2025-05-31T18:29:48.329Z", + "savedAt": "2025-05-31T18:29:48.330Z" } \ No newline at end of file diff --git a/test/core/utils/test.lifecycle-component.ts b/test/core/utils/test.lifecycle-component.ts index 2955e97..3bccbdd 100644 --- a/test/core/utils/test.lifecycle-component.ts +++ b/test/core/utils/test.lifecycle-component.ts @@ -55,8 +55,8 @@ class TestComponent extends LifecycleComponent { return this.clearInterval(timer); } - public testAddEventListener(target: any, event: string, handler: Function): void { - return this.addEventListener(target, event, handler); + public testAddEventListener(target: any, event: string, handler: Function, options?: { once?: boolean }): void { + return this.addEventListener(target, event, handler, options); } public testIsShuttingDown(): boolean { diff --git a/ts/core/utils/lifecycle-component.ts b/ts/core/utils/lifecycle-component.ts index bc5dba0..0ee8b30 100644 --- a/ts/core/utils/lifecycle-component.ts +++ b/ts/core/utils/lifecycle-component.ts @@ -91,19 +91,37 @@ export abstract class LifecycleComponent { return; } + // For 'once' listeners, we need to wrap the handler to remove it from our tracking + let actualHandler = handler; + if (options?.once) { + actualHandler = (...args: any[]) => { + // Call the original handler + handler(...args); + + // Remove from our internal tracking + const index = this.listeners.findIndex( + l => l.target === target && l.event === event && l.handler === handler + ); + if (index !== -1) { + this.listeners.splice(index, 1); + } + }; + } + // Support both EventEmitter and DOM-style event targets if (typeof target.on === 'function') { if (options?.once) { - target.once(event, handler); + target.once(event, actualHandler); } else { - target.on(event, handler); + target.on(event, actualHandler); } } else if (typeof target.addEventListener === 'function') { - target.addEventListener(event, handler, options); + target.addEventListener(event, actualHandler, options); } else { throw new Error('Target must support on() or addEventListener()'); } + // Store the original handler in our tracking (not the wrapped one) this.listeners.push({ target, event,