Skip to content

Commit 3664cd6

Browse files
iterianiatscott
authored andcommitted
refactor(core): Allow manual renderer listens to contribute to event delegation as well. (#56799)
There are existing usages that inject the renderer to manualy listen (often for event delegation purposes). These should contribute as well. PR Close #56799
1 parent 4e6a42b commit 3664cd6

File tree

3 files changed

+46
-9
lines changed

3 files changed

+46
-9
lines changed

packages/core/src/event_delegation_utils.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ export function setJSActionAttributes(nativeElement: Element, eventTypes: string
4646
nativeElement.setAttribute(Attribute.JSACTION, `${existingAttr ?? ''}${parts}`);
4747
}
4848

49-
export const sharedStashFunction = (rEl: RElement, eventType: string, listenerFn: () => void) => {
49+
export const sharedStashFunction = (rEl: RElement, eventType: string, listenerFn: Function) => {
5050
const el = rEl as unknown as Element;
5151
const eventListenerMap = el.__jsaction_fns ?? new Map();
5252
const eventListeners = eventListenerMap.get(eventType) ?? [];
@@ -93,10 +93,11 @@ export class GlobalEventDelegation implements OnDestroy {
9393
return isEarlyEventType(eventType);
9494
}
9595

96-
addEventListener(element: HTMLElement, eventType: string, handler: Function): Function {
97-
this.eventContractDetails.instance!.addEvent(eventType);
98-
getActionCache(element)[eventType] = '';
99-
return () => this.removeEventListener(element, eventType, handler);
96+
addEventListener(element: HTMLElement, eventName: string, handler: Function): Function {
97+
this.eventContractDetails.instance!.addEvent(eventName);
98+
sharedStashFunction(element, eventName, handler);
99+
getActionCache(element)[eventName] = '';
100+
return () => this.removeEventListener(element, eventName, handler);
100101
}
101102

102103
removeEventListener(element: HTMLElement, eventType: string, callback: Function): void {

packages/core/src/event_dispatch/event_delegation.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,11 @@
99
import {ENVIRONMENT_INITIALIZER, Injector} from '../di';
1010
import {inject} from '../di/injector_compatibility';
1111
import {Provider} from '../di/interface/provider';
12-
import {setStashFn} from '../render3/instructions/listener';
1312
import {
1413
GLOBAL_EVENT_DELEGATION,
1514
GlobalEventDelegation,
1615
JSACTION_EVENT_CONTRACT,
1716
initGlobalEventDelegation,
18-
sharedStashFunction,
1917
} from '../event_delegation_utils';
2018

2119
import {IS_GLOBAL_EVENT_DELEGATION_ENABLED} from '../hydration/tokens';
@@ -35,7 +33,6 @@ export function provideGlobalEventDelegation(): Provider[] {
3533
const injector = inject(Injector);
3634
const eventContractDetails = injector.get(JSACTION_EVENT_CONTRACT);
3735
initGlobalEventDelegation(eventContractDetails, injector);
38-
setStashFn(sharedStashFunction);
3936
},
4037
multi: true,
4138
},

packages/core/test/event_dispatch/event_dispatch_spec.ts

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,14 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
import {Component, ɵJSACTION_EVENT_CONTRACT, ɵprovideGlobalEventDelegation} from '@angular/core';
9+
import {
10+
Component,
11+
ElementRef,
12+
Renderer2,
13+
ViewChild,
14+
inject,
15+
ɵprovideGlobalEventDelegation,
16+
} from '@angular/core';
1017
import {ComponentFixture, TestBed} from '@angular/core/testing';
1118

1219
function configureTestingModule(components: unknown[]) {
@@ -183,4 +190,36 @@ describe('event dispatch', () => {
183190
expect(onClickSpy).toHaveBeenCalledTimes(1);
184191
});
185192
});
193+
194+
describe('manual listening', () => {
195+
it('should trigger events when manually registered', async () => {
196+
const onClickSpy = jasmine.createSpy();
197+
@Component({
198+
standalone: true,
199+
selector: 'app',
200+
template: `
201+
<div id="top">
202+
<div id="bottom"></div>
203+
</div>
204+
`,
205+
})
206+
class SimpleComponent {
207+
renderer = inject(Renderer2);
208+
destroy!: Function;
209+
listen(el: Element) {
210+
this.destroy = this.renderer.listen(el, 'click', onClickSpy);
211+
}
212+
}
213+
configureTestingModule([SimpleComponent]);
214+
fixture = TestBed.createComponent(SimpleComponent);
215+
const nativeElement = fixture.debugElement.nativeElement;
216+
(fixture.componentInstance as SimpleComponent).listen(nativeElement);
217+
const bottomEl = nativeElement.querySelector('#bottom')!;
218+
bottomEl.click();
219+
expect(onClickSpy).toHaveBeenCalledTimes(1);
220+
(fixture.componentInstance as SimpleComponent).destroy();
221+
bottomEl.click();
222+
expect(onClickSpy).toHaveBeenCalledTimes(1);
223+
});
224+
});
186225
});

0 commit comments

Comments
 (0)