77 */
88
99import {
10- EventDispatcher ,
11- EarlyJsactionDataContainer ,
12- EventContract ,
13- EventContractContainer ,
14- registerDispatcher ,
1510 isSupportedEvent ,
1611 isCaptureEvent ,
12+ EventContractContainer ,
13+ EventContract ,
14+ EventDispatcher ,
15+ registerDispatcher ,
16+ EarlyJsactionDataContainer ,
1717} from '@angular/core/primitives/event-dispatch' ;
1818
1919import { APP_BOOTSTRAP_LISTENER , ApplicationRef , whenStable } from '../application/application_ref' ;
20- import { APP_ID } from '../application/application_tokens' ;
2120import { ENVIRONMENT_INITIALIZER , Injector } from '../di' ;
2221import { inject } from '../di/injector_compatibility' ;
2322import { Provider } from '../di/interface/provider' ;
24- import { setDisableEventReplayImpl } from '../render3/instructions/listener' ;
25- import { TNode , TNodeType } from '../render3/interfaces/node' ;
26- import { RElement , RNode } from '../render3/interfaces/renderer_dom' ;
23+ import { setStashFn } from '../render3/instructions/listener' ;
24+ import { RElement } from '../render3/interfaces/renderer_dom' ;
2725import { CLEANUP , LView , TView } from '../render3/interfaces/view' ;
2826import { isPlatformBrowser } from '../render3/util/misc_utils' ;
2927import { unwrapRNode } from '../render3/util/view_utils' ;
3028
31- import { IS_EVENT_REPLAY_ENABLED } from './tokens' ;
32-
33- export const EVENT_REPLAY_ENABLED_DEFAULT = false ;
34- export const CONTRACT_PROPERTY = 'ngContracts' ;
29+ import { IS_EVENT_REPLAY_ENABLED , IS_GLOBAL_EVENT_DELEGATION_ENABLED } from './tokens' ;
30+ import {
31+ GlobalEventDelegation ,
32+ sharedStashFunction ,
33+ removeListeners ,
34+ invokeRegisteredListeners ,
35+ } from '../event_delegation_utils' ;
36+ import { APP_ID } from '../application/application_tokens' ;
3537
3638declare global {
3739 var ngContracts : { [ key : string ] : EarlyJsactionDataContainer } ;
38- interface Element {
39- __jsaction_fns : Map < string , Function [ ] > | undefined ;
40- }
41- }
42-
43- // TODO: Upstream this back into event-dispatch.
44- function getJsactionData ( container : EarlyJsactionDataContainer ) {
45- return container . _ejsa ;
4640}
4741
48- const JSACTION_ATTRIBUTE = 'jsaction ' ;
42+ export const CONTRACT_PROPERTY = 'ngContracts ' ;
4943
5044/**
5145 * A set of DOM elements with `jsaction` attributes.
5246 */
5347const jsactionSet = new Set < Element > ( ) ;
5448
49+ function isGlobalEventDelegationEnabled ( injector : Injector ) {
50+ return injector . get ( IS_GLOBAL_EVENT_DELEGATION_ENABLED , false ) ;
51+ }
52+
5553/**
5654 * Returns a set of providers required to setup support for event replay.
5755 * Requires hydration to be enabled separately.
@@ -65,21 +63,13 @@ export function withEventReplay(): Provider[] {
6563 {
6664 provide : ENVIRONMENT_INITIALIZER ,
6765 useValue : ( ) => {
68- setDisableEventReplayImpl ( ( rEl : RElement , eventName : string , listenerFn : VoidFunction ) => {
69- if ( rEl . hasAttribute ( JSACTION_ATTRIBUTE ) ) {
70- const el = rEl as unknown as Element ;
71- // We don't immediately remove the attribute here because
72- // we need it for replay that happens after hydration.
73- if ( ! jsactionSet . has ( el ) ) {
74- jsactionSet . add ( el ) ;
75- el . __jsaction_fns = new Map ( ) ;
76- }
77- const eventMap = el . __jsaction_fns ! ;
78- if ( ! eventMap . has ( eventName ) ) {
79- eventMap . set ( eventName , [ ] ) ;
80- }
81- eventMap . get ( eventName ) ! . push ( listenerFn ) ;
82- }
66+ const injector = inject ( Injector ) ;
67+ if ( isGlobalEventDelegationEnabled ( injector ) ) {
68+ return ;
69+ }
70+ setStashFn ( ( rEl : RElement , eventName : string , listenerFn : VoidFunction ) => {
71+ sharedStashFunction ( rEl , eventName , listenerFn ) ;
72+ jsactionSet . add ( rEl as unknown as Element ) ;
8373 } ) ;
8474 } ,
8575 multi : true ,
@@ -95,33 +85,15 @@ export function withEventReplay(): Provider[] {
9585 // of the application is completed. This timing is similar to the unclaimed
9686 // dehydrated views cleanup timing.
9787 whenStable ( appRef ) . then ( ( ) => {
98- const appId = injector . get ( APP_ID ) ;
99- // This is set in packages/platform-server/src/utils.ts
100- // Note: globalThis[CONTRACT_PROPERTY] may be undefined in case Event Replay feature
101- // is enabled, but there are no events configured in an application.
102- const container = globalThis [ CONTRACT_PROPERTY ] ?. [ appId ] ;
103- const earlyJsactionData = getJsactionData ( container ) ;
104- if ( earlyJsactionData ) {
105- const eventContract = new EventContract (
106- new EventContractContainer ( earlyJsactionData . c ) ,
107- ) ;
108- for ( const et of earlyJsactionData . et ) {
109- eventContract . addEvent ( et ) ;
110- }
111- for ( const et of earlyJsactionData . etc ) {
112- eventContract . addEvent ( et ) ;
113- }
114- eventContract . replayEarlyEvents ( container ) ;
115- const dispatcher = new EventDispatcher ( handleEvent ) ;
116- registerDispatcher ( eventContract , dispatcher ) ;
117- for ( const el of jsactionSet ) {
118- el . removeAttribute ( JSACTION_ATTRIBUTE ) ;
119- el . __jsaction_fns = undefined ;
120- }
121- // After hydration, we shouldn't need to do anymore work related to
122- // event replay anymore.
123- setDisableEventReplayImpl ( ( ) => { } ) ;
88+ if ( isGlobalEventDelegationEnabled ( injector ) ) {
89+ return ;
12490 }
91+ const globalEventDelegation = injector . get ( GlobalEventDelegation ) ;
92+ initEventReplay ( globalEventDelegation , injector ) ;
93+ jsactionSet . forEach ( removeListeners ) ;
94+ // After hydration, we shouldn't need to do anymore work related to
95+ // event replay anymore.
96+ setStashFn ( ( ) => { } ) ;
12597 } ) ;
12698 } ;
12799 }
@@ -132,6 +104,32 @@ export function withEventReplay(): Provider[] {
132104 ] ;
133105}
134106
107+ // TODO: Upstream this back into event-dispatch.
108+ function getJsactionData ( container : EarlyJsactionDataContainer ) {
109+ return container . _ejsa ;
110+ }
111+
112+ const initEventReplay = ( eventDelegation : GlobalEventDelegation , injector : Injector ) => {
113+ const appId = injector . get ( APP_ID ) ;
114+ // This is set in packages/platform-server/src/utils.ts
115+ // Note: globalThis[CONTRACT_PROPERTY] may be undefined in case Event Replay feature
116+ // is enabled, but there are no events configured in an application.
117+ const container = globalThis [ CONTRACT_PROPERTY ] ?. [ appId ] ;
118+ const earlyJsactionData = getJsactionData ( container ) ! ;
119+ const eventContract = ( eventDelegation . eventContract = new EventContract (
120+ new EventContractContainer ( earlyJsactionData . c ) ,
121+ ) ) ;
122+ for ( const et of earlyJsactionData . et ) {
123+ eventContract . addEvent ( et ) ;
124+ }
125+ for ( const et of earlyJsactionData . etc ) {
126+ eventContract . addEvent ( et ) ;
127+ }
128+ eventContract . replayEarlyEvents ( container ) ;
129+ const dispatcher = new EventDispatcher ( invokeRegisteredListeners ) ;
130+ registerDispatcher ( eventContract , dispatcher ) ;
131+ } ;
132+
135133/**
136134 * Extracts information about all DOM events (added in a template) registered on elements in a give
137135 * LView. Maps collected events to a corresponding DOM element (an element is used as a key).
@@ -180,28 +178,3 @@ export function collectDomEventsInfo(
180178 }
181179 return events ;
182180}
183-
184- export function setJSActionAttribute (
185- tNode : TNode ,
186- rNode : RNode ,
187- nativeElementToEvents : Map < Element , string [ ] > ,
188- ) {
189- if ( tNode . type & TNodeType . Element ) {
190- const nativeElement = unwrapRNode ( rNode ) as Element ;
191- const events = nativeElementToEvents . get ( nativeElement ) ?? [ ] ;
192- const parts = events . map ( ( event ) => `${ event } :` ) ;
193- if ( parts . length > 0 ) {
194- nativeElement . setAttribute ( JSACTION_ATTRIBUTE , parts . join ( ';' ) ) ;
195- }
196- }
197- }
198-
199- function handleEvent ( event : Event ) {
200- const handlerFns = ( event . currentTarget as Element ) ?. __jsaction_fns ?. get ( event . type ) ;
201- if ( ! handlerFns ) {
202- return ;
203- }
204- for ( const handler of handlerFns ) {
205- handler ( event ) ;
206- }
207- }
0 commit comments