From 2e7cfcb2efe415d693eab09484c1f14b288f82b8 Mon Sep 17 00:00:00 2001 From: Thomas Nguyen Date: Thu, 19 Sep 2024 15:50:40 -0700 Subject: [PATCH] refactor(core): Remove global event delegation code. (#57893) This is no longer needed since we are no longer experimenting with it. PR Close #57893 --- packages/core/src/core_private_export.ts | 6 +- packages/core/src/event_delegation_utils.ts | 70 +--- .../src/event_dispatch/event_delegation.ts | 58 ---- packages/core/src/hydration/event_replay.ts | 15 +- packages/core/src/hydration/tokens.ts | 7 - .../bundle.golden_symbols.json | 9 - .../animations/bundle.golden_symbols.json | 6 - .../cyclic_import/bundle.golden_symbols.json | 6 - .../bundling/defer/bundle.golden_symbols.json | 9 - .../forms_reactive/bundle.golden_symbols.json | 6 - .../bundle.golden_symbols.json | 6 - .../hydration/bundle.golden_symbols.json | 9 - .../router/bundle.golden_symbols.json | 9 - .../bundle.golden_symbols.json | 9 - .../bundling/todo/bundle.golden_symbols.json | 6 - packages/core/test/event_dispatch/BUILD.bazel | 20 -- .../event_dispatch/event_dispatch_spec.ts | 303 ------------------ packages/platform-browser/src/browser.ts | 6 - .../src/dom/events/event_delegation.ts | 33 -- 19 files changed, 5 insertions(+), 588 deletions(-) delete mode 100644 packages/core/src/event_dispatch/event_delegation.ts delete mode 100644 packages/core/test/event_dispatch/BUILD.bazel delete mode 100644 packages/core/test/event_dispatch/event_dispatch_spec.ts delete mode 100644 packages/platform-browser/src/dom/events/event_delegation.ts diff --git a/packages/core/src/core_private_export.ts b/packages/core/src/core_private_export.ts index 495f51dec44..d33defe8f29 100644 --- a/packages/core/src/core_private_export.ts +++ b/packages/core/src/core_private_export.ts @@ -72,11 +72,7 @@ export { withI18nSupport as ɵwithI18nSupport, } from './hydration/api'; export {withEventReplay as ɵwithEventReplay} from './hydration/event_replay'; -export { - GLOBAL_EVENT_DELEGATION as ɵGLOBAL_EVENT_DELEGATION, - JSACTION_EVENT_CONTRACT as ɵJSACTION_EVENT_CONTRACT, -} from './event_delegation_utils'; -export {provideGlobalEventDelegation as ɵprovideGlobalEventDelegation} from './event_dispatch/event_delegation'; +export {JSACTION_EVENT_CONTRACT as ɵJSACTION_EVENT_CONTRACT} from './event_delegation_utils'; export {IS_HYDRATION_DOM_REUSE_ENABLED as ɵIS_HYDRATION_DOM_REUSE_ENABLED} from './hydration/tokens'; export { HydratedNode as ɵHydratedNode, diff --git a/packages/core/src/event_delegation_utils.ts b/packages/core/src/event_delegation_utils.ts index c7f6c8db722..6041a505e4c 100644 --- a/packages/core/src/event_delegation_utils.ts +++ b/packages/core/src/event_delegation_utils.ts @@ -7,19 +7,10 @@ */ // tslint:disable:no-duplicate-imports -import { - EventContract, - EventContractContainer, - EventDispatcher, - isEarlyEventType, - getActionCache, - registerDispatcher, -} from '@angular/core/primitives/event-dispatch'; +import {EventContract} from '@angular/core/primitives/event-dispatch'; import {Attribute} from '@angular/core/primitives/event-dispatch'; -import {Injectable, InjectionToken, Injector, inject} from './di'; +import {InjectionToken} from './di'; import {RElement} from './render3/interfaces/renderer_dom'; -import {EVENT_REPLAY_ENABLED_DEFAULT, IS_EVENT_REPLAY_ENABLED} from './hydration/tokens'; -import {OnDestroy} from './interface/lifecycle_hooks'; declare global { interface Element { @@ -71,60 +62,3 @@ export const JSACTION_EVENT_CONTRACT = new InjectionToken( factory: () => ({}), }, ); - -export const GLOBAL_EVENT_DELEGATION = new InjectionToken( - ngDevMode ? 'GLOBAL_EVENT_DELEGATION' : '', -); - -/** - * This class is the delegate for `EventDelegationPlugin`. It represents the - * noop version of this class, with the enabled version set when - * `provideGlobalEventDelegation` is called. - */ -@Injectable() -export class GlobalEventDelegation implements OnDestroy { - private eventContractDetails = inject(JSACTION_EVENT_CONTRACT); - - ngOnDestroy() { - this.eventContractDetails.instance?.cleanUp(); - } - - supports(eventType: string): boolean { - return isEarlyEventType(eventType); - } - - addEventListener(element: HTMLElement, eventType: string, handler: Function): Function { - // Note: contrary to the type, Window and Document can be passed in - // as well. - if (element.nodeType === Node.ELEMENT_NODE) { - this.eventContractDetails.instance!.addEvent(eventType); - getActionCache(element)[eventType] = ''; - sharedStashFunction(element, eventType, handler); - } else { - element.addEventListener(eventType, handler as EventListener); - } - return () => this.removeEventListener(element, eventType, handler); - } - - removeEventListener(element: HTMLElement, eventType: string, callback: Function): void { - if (element.nodeType === Node.ELEMENT_NODE) { - getActionCache(element)[eventType] = undefined; - } else { - element.removeEventListener(eventType, callback as EventListener); - } - } -} - -export const initGlobalEventDelegation = ( - eventContractDetails: EventContractDetails, - injector: Injector, -) => { - if (injector.get(IS_EVENT_REPLAY_ENABLED, EVENT_REPLAY_ENABLED_DEFAULT)) { - return; - } - const eventContract = (eventContractDetails.instance = new EventContract( - new EventContractContainer(document.body), - )); - const dispatcher = new EventDispatcher(invokeRegisteredListeners, /** clickModSupport */ false); - registerDispatcher(eventContract, dispatcher); -}; diff --git a/packages/core/src/event_dispatch/event_delegation.ts b/packages/core/src/event_dispatch/event_delegation.ts deleted file mode 100644 index c1ce773df66..00000000000 --- a/packages/core/src/event_dispatch/event_delegation.ts +++ /dev/null @@ -1,58 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {EventContract} from '@angular/core/primitives/event-dispatch'; -import {ENVIRONMENT_INITIALIZER, Injector} from '../di'; -import {inject} from '../di/injector_compatibility'; -import {Provider} from '../di/interface/provider'; -import { - GLOBAL_EVENT_DELEGATION, - GlobalEventDelegation, - JSACTION_EVENT_CONTRACT, - initGlobalEventDelegation, -} from '../event_delegation_utils'; - -import {IS_GLOBAL_EVENT_DELEGATION_ENABLED} from '../hydration/tokens'; - -declare global { - interface Window { - __jsaction_contract: EventContract | undefined; - } -} - -/** - * Returns a set of providers required to setup support for event delegation. - * @param multiContract - Experimental support to provide one event contract - * when there are multiple binaries on the page. - */ -export function provideGlobalEventDelegation(multiContract = false): Provider[] { - return [ - { - provide: IS_GLOBAL_EVENT_DELEGATION_ENABLED, - useValue: true, - }, - { - provide: ENVIRONMENT_INITIALIZER, - useValue: () => { - const injector = inject(Injector); - const eventContractDetails = injector.get(JSACTION_EVENT_CONTRACT); - if (multiContract && window.__jsaction_contract) { - eventContractDetails.instance = window.__jsaction_contract; - return; - } - initGlobalEventDelegation(eventContractDetails, injector); - window.__jsaction_contract = eventContractDetails.instance; - }, - multi: true, - }, - { - provide: GLOBAL_EVENT_DELEGATION, - useClass: GlobalEventDelegation, - }, - ]; -} diff --git a/packages/core/src/hydration/event_replay.ts b/packages/core/src/hydration/event_replay.ts index 44cfe71f090..737371d0426 100644 --- a/packages/core/src/hydration/event_replay.ts +++ b/packages/core/src/hydration/event_replay.ts @@ -27,11 +27,7 @@ import {CLEANUP, LView, TView} from '../render3/interfaces/view'; import {isPlatformBrowser} from '../render3/util/misc_utils'; import {unwrapRNode} from '../render3/util/view_utils'; -import { - EVENT_REPLAY_ENABLED_DEFAULT, - IS_EVENT_REPLAY_ENABLED, - IS_GLOBAL_EVENT_DELEGATION_ENABLED, -} from './tokens'; +import {EVENT_REPLAY_ENABLED_DEFAULT, IS_EVENT_REPLAY_ENABLED} from './tokens'; import { sharedStashFunction, removeListeners, @@ -47,18 +43,11 @@ import {performanceMarkFeature} from '../util/performance'; */ const jsactionSet = new Set(); -function isGlobalEventDelegationEnabled(injector: Injector) { - return injector.get(IS_GLOBAL_EVENT_DELEGATION_ENABLED, false); -} - /** * Determines whether Event Replay feature should be activated on the client. */ function shouldEnableEventReplay(injector: Injector) { - return ( - injector.get(IS_EVENT_REPLAY_ENABLED, EVENT_REPLAY_ENABLED_DEFAULT) && - !isGlobalEventDelegationEnabled(injector) - ); + return injector.get(IS_EVENT_REPLAY_ENABLED, EVENT_REPLAY_ENABLED_DEFAULT); } /** diff --git a/packages/core/src/hydration/tokens.ts b/packages/core/src/hydration/tokens.ts index b14a5fb5bcb..f4854d837f4 100644 --- a/packages/core/src/hydration/tokens.ts +++ b/packages/core/src/hydration/tokens.ts @@ -49,10 +49,3 @@ export const IS_EVENT_REPLAY_ENABLED = new InjectionToken( ); export const EVENT_REPLAY_ENABLED_DEFAULT = false; - -/** - * Internal token that indicates whether global event delegation support is enabled. - */ -export const IS_GLOBAL_EVENT_DELEGATION_ENABLED = new InjectionToken( - typeof ngDevMode === 'undefined' || !!ngDevMode ? 'IS_GLOBAL_EVENT_DELEGATION_ENABLED' : '', -); diff --git a/packages/core/test/bundling/animations-standalone/bundle.golden_symbols.json b/packages/core/test/bundling/animations-standalone/bundle.golden_symbols.json index e620d87174b..148da69336f 100644 --- a/packages/core/test/bundling/animations-standalone/bundle.golden_symbols.json +++ b/packages/core/test/bundling/animations-standalone/bundle.golden_symbols.json @@ -179,9 +179,6 @@ { "name": "DomAdapter" }, - { - "name": "DomEventsPlugin" - }, { "name": "DomRendererFactory2" }, @@ -236,9 +233,6 @@ { "name": "ErrorHandler" }, - { - "name": "EventDelegationPlugin" - }, { "name": "EventEmitter" }, @@ -251,9 +245,6 @@ { "name": "FALSE_BOOLEAN_VALUES" }, - { - "name": "GLOBAL_EVENT_DELEGATION" - }, { "name": "GenericBrowserDomAdapter" }, diff --git a/packages/core/test/bundling/animations/bundle.golden_symbols.json b/packages/core/test/bundling/animations/bundle.golden_symbols.json index 8d7c319332c..e405a868302 100644 --- a/packages/core/test/bundling/animations/bundle.golden_symbols.json +++ b/packages/core/test/bundling/animations/bundle.golden_symbols.json @@ -257,9 +257,6 @@ { "name": "ErrorHandler" }, - { - "name": "EventDelegationPlugin" - }, { "name": "EventEmitter" }, @@ -272,9 +269,6 @@ { "name": "FALSE_BOOLEAN_VALUES" }, - { - "name": "GLOBAL_EVENT_DELEGATION" - }, { "name": "GenericBrowserDomAdapter" }, diff --git a/packages/core/test/bundling/cyclic_import/bundle.golden_symbols.json b/packages/core/test/bundling/cyclic_import/bundle.golden_symbols.json index 451205243f2..397eefc26bc 100644 --- a/packages/core/test/bundling/cyclic_import/bundle.golden_symbols.json +++ b/packages/core/test/bundling/cyclic_import/bundle.golden_symbols.json @@ -164,9 +164,6 @@ { "name": "ErrorHandler" }, - { - "name": "EventDelegationPlugin" - }, { "name": "EventEmitter" }, @@ -176,9 +173,6 @@ { "name": "EventManagerPlugin" }, - { - "name": "GLOBAL_EVENT_DELEGATION" - }, { "name": "GenericBrowserDomAdapter" }, diff --git a/packages/core/test/bundling/defer/bundle.golden_symbols.json b/packages/core/test/bundling/defer/bundle.golden_symbols.json index 24cd75b8b3d..a6ff37a3e29 100644 --- a/packages/core/test/bundling/defer/bundle.golden_symbols.json +++ b/packages/core/test/bundling/defer/bundle.golden_symbols.json @@ -197,9 +197,6 @@ { "name": "ErrorHandler" }, - { - "name": "EventDelegationPlugin" - }, { "name": "EventEmitter" }, @@ -209,9 +206,6 @@ { "name": "EventManagerPlugin" }, - { - "name": "GLOBAL_EVENT_DELEGATION" - }, { "name": "GenericBrowserDomAdapter" }, @@ -1436,9 +1430,6 @@ { "name": "init_event_contract_defines" }, - { - "name": "init_event_delegation" - }, { "name": "init_event_delegation_utils" }, diff --git a/packages/core/test/bundling/forms_reactive/bundle.golden_symbols.json b/packages/core/test/bundling/forms_reactive/bundle.golden_symbols.json index aa1136d48c4..517d00b0c3b 100644 --- a/packages/core/test/bundling/forms_reactive/bundle.golden_symbols.json +++ b/packages/core/test/bundling/forms_reactive/bundle.golden_symbols.json @@ -224,9 +224,6 @@ { "name": "ErrorHandler" }, - { - "name": "EventDelegationPlugin" - }, { "name": "EventEmitter" }, @@ -272,9 +269,6 @@ { "name": "FormsExampleModule" }, - { - "name": "GLOBAL_EVENT_DELEGATION" - }, { "name": "GenericBrowserDomAdapter" }, diff --git a/packages/core/test/bundling/forms_template_driven/bundle.golden_symbols.json b/packages/core/test/bundling/forms_template_driven/bundle.golden_symbols.json index 84696708788..c0e234eaa6a 100644 --- a/packages/core/test/bundling/forms_template_driven/bundle.golden_symbols.json +++ b/packages/core/test/bundling/forms_template_driven/bundle.golden_symbols.json @@ -227,9 +227,6 @@ { "name": "ErrorHandler" }, - { - "name": "EventDelegationPlugin" - }, { "name": "EventEmitter" }, @@ -251,9 +248,6 @@ { "name": "FormsModule" }, - { - "name": "GLOBAL_EVENT_DELEGATION" - }, { "name": "GenericBrowserDomAdapter" }, diff --git a/packages/core/test/bundling/hydration/bundle.golden_symbols.json b/packages/core/test/bundling/hydration/bundle.golden_symbols.json index cdb158ae439..02486234070 100644 --- a/packages/core/test/bundling/hydration/bundle.golden_symbols.json +++ b/packages/core/test/bundling/hydration/bundle.golden_symbols.json @@ -125,9 +125,6 @@ { "name": "DomAdapter" }, - { - "name": "DomEventsPlugin" - }, { "name": "DomRendererFactory2" }, @@ -170,9 +167,6 @@ { "name": "ErrorHandler" }, - { - "name": "EventDelegationPlugin" - }, { "name": "EventEmitter" }, @@ -182,9 +176,6 @@ { "name": "EventManagerPlugin" }, - { - "name": "GLOBAL_EVENT_DELEGATION" - }, { "name": "GenericBrowserDomAdapter" }, diff --git a/packages/core/test/bundling/router/bundle.golden_symbols.json b/packages/core/test/bundling/router/bundle.golden_symbols.json index 0fb8a78249d..991b0fc034d 100644 --- a/packages/core/test/bundling/router/bundle.golden_symbols.json +++ b/packages/core/test/bundling/router/bundle.golden_symbols.json @@ -188,9 +188,6 @@ { "name": "DomAdapter" }, - { - "name": "DomEventsPlugin" - }, { "name": "DomRendererFactory2" }, @@ -236,9 +233,6 @@ { "name": "ErrorHandler" }, - { - "name": "EventDelegationPlugin" - }, { "name": "EventEmitter" }, @@ -251,9 +245,6 @@ { "name": "EventType2" }, - { - "name": "GLOBAL_EVENT_DELEGATION" - }, { "name": "GenericBrowserDomAdapter" }, diff --git a/packages/core/test/bundling/standalone_bootstrap/bundle.golden_symbols.json b/packages/core/test/bundling/standalone_bootstrap/bundle.golden_symbols.json index 8e871b3a808..52fd33934b5 100644 --- a/packages/core/test/bundling/standalone_bootstrap/bundle.golden_symbols.json +++ b/packages/core/test/bundling/standalone_bootstrap/bundle.golden_symbols.json @@ -107,9 +107,6 @@ { "name": "DomAdapter" }, - { - "name": "DomEventsPlugin" - }, { "name": "DomRendererFactory2" }, @@ -149,9 +146,6 @@ { "name": "ErrorHandler" }, - { - "name": "EventDelegationPlugin" - }, { "name": "EventEmitter" }, @@ -161,9 +155,6 @@ { "name": "EventManagerPlugin" }, - { - "name": "GLOBAL_EVENT_DELEGATION" - }, { "name": "GenericBrowserDomAdapter" }, diff --git a/packages/core/test/bundling/todo/bundle.golden_symbols.json b/packages/core/test/bundling/todo/bundle.golden_symbols.json index f062204aafc..427583d551a 100644 --- a/packages/core/test/bundling/todo/bundle.golden_symbols.json +++ b/packages/core/test/bundling/todo/bundle.golden_symbols.json @@ -167,9 +167,6 @@ { "name": "ErrorHandler" }, - { - "name": "EventDelegationPlugin" - }, { "name": "EventEmitter" }, @@ -179,9 +176,6 @@ { "name": "EventManagerPlugin" }, - { - "name": "GLOBAL_EVENT_DELEGATION" - }, { "name": "GenericBrowserDomAdapter" }, diff --git a/packages/core/test/event_dispatch/BUILD.bazel b/packages/core/test/event_dispatch/BUILD.bazel deleted file mode 100644 index 7f3a1604a9b..00000000000 --- a/packages/core/test/event_dispatch/BUILD.bazel +++ /dev/null @@ -1,20 +0,0 @@ -load("//tools:defaults.bzl", "karma_web_test_suite", "ts_library") - -ts_library( - name = "test_lib", - testonly = True, - srcs = [ - "event_dispatch_spec.ts", - ], - deps = [ - "//packages/core", - "//packages/core/primitives/event-dispatch", - "//packages/core/testing", - "//packages/platform-browser", - ], -) - -karma_web_test_suite( - name = "test", - deps = [":test_lib"], -) diff --git a/packages/core/test/event_dispatch/event_dispatch_spec.ts b/packages/core/test/event_dispatch/event_dispatch_spec.ts deleted file mode 100644 index e375576cfcf..00000000000 --- a/packages/core/test/event_dispatch/event_dispatch_spec.ts +++ /dev/null @@ -1,303 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import { - Component, - HostListener, - Renderer2, - inject, - ɵprovideGlobalEventDelegation, -} from '@angular/core'; -import {ComponentFixture, TestBed} from '@angular/core/testing'; - -function configureTestingModule(components: unknown[]) { - TestBed.configureTestingModule({ - imports: components, - providers: [ɵprovideGlobalEventDelegation()], - }); -} - -describe('event dispatch', () => { - let fixture: ComponentFixture; - - it(`executes an onclick handler`, async () => { - const onClickSpy = jasmine.createSpy(); - @Component({ - selector: 'app', - standalone: true, - template: ` - - `, - }) - class AppComponent { - onClick = onClickSpy; - } - configureTestingModule([AppComponent]); - const addEventListenerSpy = spyOn( - HTMLButtonElement.prototype, - 'addEventListener', - ).and.callThrough(); - fixture = TestBed.createComponent(AppComponent); - const button = (fixture.debugElement.nativeElement as Element) - .firstElementChild as HTMLButtonElement; - button.click(); - expect(onClickSpy).toHaveBeenCalledTimes(1); - expect(addEventListenerSpy).not.toHaveBeenCalled(); - }); - - it('should work for elements with local refs', async () => { - const onClickSpy = jasmine.createSpy(); - - @Component({ - selector: 'app', - standalone: true, - template: ` - - `, - }) - class AppComponent { - onClick = onClickSpy; - } - configureTestingModule([AppComponent]); - fixture = TestBed.createComponent(AppComponent); - fixture.debugElement.nativeElement.querySelector('#btn').click(); - expect(onClickSpy).toHaveBeenCalled(); - }); - it('should route to the appropriate component with content projection', async () => { - const outerOnClickSpy = jasmine.createSpy(); - const innerOnClickSpy = jasmine.createSpy(); - @Component({ - selector: 'app-card', - standalone: true, - template: ` -
- - -
- `, - }) - class CardComponent { - onClick = innerOnClickSpy; - } - - @Component({ - selector: 'app', - imports: [CardComponent], - standalone: true, - template: ` - -

Card Title

-

This is some card content.

- -
- `, - }) - class AppComponent { - onClick = outerOnClickSpy; - } - configureTestingModule([AppComponent]); - fixture = TestBed.createComponent(AppComponent); - const nativeElement = fixture.debugElement.nativeElement; - const outer = nativeElement.querySelector('#outer-button')!; - const inner = nativeElement.querySelector('#inner-button')!; - outer.click(); - inner.click(); - expect(outerOnClickSpy).toHaveBeenCalledBefore(innerOnClickSpy); - }); - - describe('bubbling behavior', () => { - it('should propagate events', async () => { - const onClickSpy = jasmine.createSpy(); - @Component({ - standalone: true, - selector: 'app', - template: ` -
-
-
- `, - }) - class SimpleComponent { - onClick = onClickSpy; - } - configureTestingModule([SimpleComponent]); - fixture = TestBed.createComponent(SimpleComponent); - const nativeElement = fixture.debugElement.nativeElement; - const bottomEl = nativeElement.querySelector('#bottom')!; - bottomEl.click(); - expect(onClickSpy).toHaveBeenCalledTimes(2); - }); - - it('should not propagate events if stopPropagation is called', async () => { - @Component({ - standalone: true, - selector: 'app', - template: ` -
-
-
- `, - }) - class SimpleComponent { - onClick(e: Event) { - e.stopPropagation(); - } - } - const onClickSpy = spyOn(SimpleComponent.prototype, 'onClick').and.callThrough(); - configureTestingModule([SimpleComponent]); - fixture = TestBed.createComponent(SimpleComponent); - const nativeElement = fixture.debugElement.nativeElement; - const bottomEl = nativeElement.querySelector('#bottom')!; - bottomEl.click(); - expect(onClickSpy).toHaveBeenCalledTimes(1); - }); - it('should call the original stopPropagation method', async () => { - @Component({ - standalone: true, - selector: 'app', - template: ` -
-
-
- `, - }) - class SimpleComponent { - onClick(e: Event) { - e.stopPropagation(); - e.preventDefault(); - } - } - configureTestingModule([SimpleComponent]); - fixture = TestBed.createComponent(SimpleComponent); - const nativeElement = fixture.debugElement.nativeElement; - const bottomEl = nativeElement.querySelector('#bottom')!; - const event = new MouseEvent('click', {bubbles: true}); - spyOn(event, 'stopPropagation'); - const stopPropagation = event.stopPropagation; - spyOn(event, 'preventDefault'); - const preventDefault = event.preventDefault; - bottomEl.dispatchEvent(event); - expect(stopPropagation).toHaveBeenCalled(); - expect(preventDefault).toHaveBeenCalled(); - }); - }); - - describe('manual listening', () => { - it('should trigger events when manually registered', async () => { - const onClickSpy = jasmine.createSpy(); - @Component({ - standalone: true, - selector: 'app', - template: ` -
-
-
- `, - }) - class SimpleComponent { - renderer = inject(Renderer2); - destroy!: Function; - listen(el: Element) { - this.destroy = this.renderer.listen(el, 'click', onClickSpy); - } - } - configureTestingModule([SimpleComponent]); - fixture = TestBed.createComponent(SimpleComponent); - const nativeElement = fixture.debugElement.nativeElement; - (fixture.componentInstance as SimpleComponent).listen(nativeElement); - const bottomEl = nativeElement.querySelector('#bottom')!; - bottomEl.click(); - expect(onClickSpy).toHaveBeenCalledTimes(1); - bottomEl.dispatchEvent(new MouseEvent('click', {bubbles: true, shiftKey: true})); - expect(onClickSpy).toHaveBeenCalledTimes(2); - (fixture.componentInstance as SimpleComponent).destroy(); - bottomEl.click(); - expect(onClickSpy).toHaveBeenCalledTimes(2); - }); - it('should allow host listening on the window', async () => { - const onClickSpy = jasmine.createSpy(); - @Component({ - standalone: true, - selector: 'app', - template: ` -
-
-
- `, - }) - class SimpleComponent { - renderer = inject(Renderer2); - destroy!: Function; - @HostListener('window:click', ['$event.target']) - listen(el: Element) { - onClickSpy(); - } - } - configureTestingModule([SimpleComponent]); - fixture = TestBed.createComponent(SimpleComponent); - const nativeElement = fixture.debugElement.nativeElement; - const bottomEl = nativeElement.querySelector('#bottom')!; - bottomEl.click(); - expect(onClickSpy).toHaveBeenCalledTimes(1); - }); - it('should allow host listening on the window', async () => { - const onClickSpy = jasmine.createSpy(); - @Component({ - standalone: true, - selector: 'app', - template: ` -
-
-
- `, - }) - class SimpleComponent { - renderer = inject(Renderer2); - destroy!: Function; - @HostListener('window:click', ['$event.target']) - listen(el: Element) { - onClickSpy(); - } - } - configureTestingModule([SimpleComponent]); - fixture = TestBed.createComponent(SimpleComponent); - const nativeElement = fixture.debugElement.nativeElement; - const bottomEl = nativeElement.querySelector('#bottom')!; - bottomEl.click(); - expect(onClickSpy).toHaveBeenCalledTimes(1); - }); - }); -}); - -describe('capture behavior', () => { - let fixture: ComponentFixture; - it('should not bubble', async () => { - const onFocusSpy = jasmine.createSpy(); - @Component({ - standalone: true, - selector: 'app', - template: ` -
-
-
- `, - }) - class SimpleComponent { - onFocus = onFocusSpy; - } - configureTestingModule([SimpleComponent]); - fixture = TestBed.createComponent(SimpleComponent); - const nativeElement = fixture.debugElement.nativeElement; - const bottomEl = nativeElement.querySelector('#bottom')!; - const topEl = nativeElement.querySelector('#top')!; - bottomEl.dispatchEvent(new FocusEvent('focus')); - expect(onFocusSpy).toHaveBeenCalledTimes(0); - topEl.dispatchEvent(new FocusEvent('focus')); - expect(onFocusSpy).toHaveBeenCalledTimes(1); - }); -}); diff --git a/packages/platform-browser/src/browser.ts b/packages/platform-browser/src/browser.ts index e46640c7ef0..d0e34c6ecc2 100644 --- a/packages/platform-browser/src/browser.ts +++ b/packages/platform-browser/src/browser.ts @@ -49,7 +49,6 @@ import {BrowserGetTestability} from './browser/testability'; import {BrowserXhr} from './browser/xhr'; import {DomRendererFactory2} from './dom/dom_renderer'; import {DomEventsPlugin} from './dom/events/dom_events'; -import {EventDelegationPlugin} from './dom/events/event_delegation'; import {EVENT_MANAGER_PLUGINS, EventManager} from './dom/events/event_manager'; import {KeyEventsPlugin} from './dom/events/key_events'; import {SharedStylesHost} from './dom/shared_styles_host'; @@ -241,11 +240,6 @@ const BROWSER_MODULE_PROVIDERS: Provider[] = [ deps: [DOCUMENT, NgZone, PLATFORM_ID], }, {provide: EVENT_MANAGER_PLUGINS, useClass: KeyEventsPlugin, multi: true, deps: [DOCUMENT]}, - { - provide: EVENT_MANAGER_PLUGINS, - useClass: EventDelegationPlugin, - multi: true, - }, DomRendererFactory2, SharedStylesHost, EventManager, diff --git a/packages/platform-browser/src/dom/events/event_delegation.ts b/packages/platform-browser/src/dom/events/event_delegation.ts deleted file mode 100644 index 7cc08cec82a..00000000000 --- a/packages/platform-browser/src/dom/events/event_delegation.ts +++ /dev/null @@ -1,33 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {Inject, Injectable, inject, ɵGLOBAL_EVENT_DELEGATION} from '@angular/core'; -import {EventManagerPlugin} from './event_manager'; -import {DOCUMENT} from '@angular/common'; - -@Injectable() -export class EventDelegationPlugin extends EventManagerPlugin { - private delegate = inject(ɵGLOBAL_EVENT_DELEGATION, {optional: true}); - constructor(@Inject(DOCUMENT) doc: any) { - super(doc); - } - - override supports(eventName: string): boolean { - // If `GlobalDelegationEventPlugin` implementation is not provided, - // this plugin is kept disabled. - return this.delegate ? this.delegate.supports(eventName) : false; - } - - override addEventListener(element: HTMLElement, eventName: string, handler: Function): Function { - return this.delegate!.addEventListener(element, eventName, handler); - } - - removeEventListener(element: HTMLElement, eventName: string, callback: Function): void { - return this.delegate!.removeEventListener(element, eventName, callback); - } -}