refactor(core): simplify listener signature (#60549)

Reduces the number of parameters that need to be passed into listeners.

PR Close #60549
This commit is contained in:
Kristiyan Kostadinov
2025-03-25 12:26:44 +01:00
committed by Alex Rickabaugh
parent acf84db778
commit 277aa76876
2 changed files with 43 additions and 24 deletions
@@ -11,7 +11,6 @@ import {GlobalTargetResolver, Renderer} from '../interfaces/renderer';
import {LView, RENDERER, TView} from '../interfaces/view';
import {assertTNodeType} from '../node_assert';
import {getCurrentDirectiveDef, getCurrentTNode, getLView, getTView} from '../state';
import {getOrCreateLViewCleanup, getOrCreateTViewCleanup} from '../util/view_utils';
import {listenToOutput} from '../view/directive_outputs';
import {listenToDomEvent, wrapListener} from '../view/listeners';
@@ -93,13 +92,6 @@ export function listenerInternal(
listenerFn: (e?: any) => any,
eventTargetResolver?: GlobalTargetResolver,
): void {
const tCleanup = tView.firstCreatePass ? getOrCreateTViewCleanup(tView) : null;
// When the ɵɵlistener instruction was generated and is executed we know that there is either a
// native listener or a directive output on this element. As such we we know that we will have to
// register a listener and store its cleanup function on LView.
const lCleanup = getOrCreateLViewCleanup(lView);
ngDevMode && assertTNodeType(tNode, TNodeType.AnyRNode | TNodeType.AnyContainer);
let processOutputs = true;
@@ -113,17 +105,16 @@ export function listenerInternal(
wrappedListener ??= wrapListener(tNode, lView, listenerFn);
const hasCoalescedDomEvent = listenToDomEvent(
tNode,
tView,
lView,
eventTargetResolver,
lCleanup,
tView,
eventName,
wrappedListener,
listenerFn,
renderer,
tCleanup,
eventName,
listenerFn,
wrappedListener,
);
// Context: https://github.com/angular/angular/pull/30144
if (hasCoalescedDomEvent) {
processOutputs = false;
}
+38 -10
View File
@@ -12,13 +12,20 @@ import {NotificationSource} from '../../change_detection/scheduling/zoneless_sch
import {TNode} from '../interfaces/node';
import {isComponentHost, isDirectiveHost} from '../interfaces/type_checks';
import {CLEANUP, CONTEXT, INJECTOR, LView, TView} from '../interfaces/view';
import {getComponentLViewByIndex, getNativeByTNode, unwrapRNode} from '../util/view_utils';
import {
getComponentLViewByIndex,
getNativeByTNode,
getOrCreateLViewCleanup,
getOrCreateTViewCleanup,
unwrapRNode,
} from '../util/view_utils';
import {profiler} from '../profiler';
import {ProfilerEvent} from '../profiler_types';
import {ErrorHandler} from '../../error_handler';
import {markViewDirty} from '../instructions/mark_view_dirty';
import {RElement, RNode} from '../interfaces/renderer_dom';
import {GlobalTargetResolver, Renderer} from '../interfaces/renderer';
import {assertNotSame} from '../../util/assert';
/**
* Contains a reference to a function that disables event replay feature
@@ -103,22 +110,41 @@ function handleError(lView: LView, error: any): void {
injector.get(ErrorHandler, null)?.handleError(error);
}
/**
* Listen to a DOM event on a specific node.
* @param tNode TNode on which to listen.
* @param tView TView in which the node is placed.
* @param lView LView in which the node instance is placed.
* @param eventTargetResolver Resolver for global event targets.
* @param renderer Renderer to use for listening to the event.
* @param eventName Name of the event.
* @param originalListener Original listener as it was created by the compiler. Necessary for event
* coalescing.
* @param wrappedListener Listener wrapped with additional logic like marking for check and error
* handling.
* @returns Boolean indicating whether the event was bound or was coalesced into an existing
* listener.
*/
export function listenToDomEvent(
tNode: TNode,
tView: TView,
lView: LView<{} | null>,
eventTargetResolver: GlobalTargetResolver | undefined,
lCleanup: any[],
tView: TView,
eventName: string,
wrappedListener: (e?: any) => any,
originalListener: (e?: any) => any,
renderer: Renderer,
tCleanup: any[] | null,
) {
eventName: string,
originalListener: (e?: any) => any,
wrappedListener: (e?: any) => any,
): boolean {
ngDevMode &&
assertNotSame(
wrappedListener,
originalListener,
'Expected wrapped and original listeners to be different.',
);
const isTNodeDirectiveHost = isDirectiveHost(tNode);
const native = getNativeByTNode(tNode, lView) as RElement;
const target = eventTargetResolver ? eventTargetResolver(native) : native;
const lCleanupIndex = lCleanup.length;
const idxOrTargetGetter = eventTargetResolver
? (_lView: LView) => eventTargetResolver(unwrapRNode(_lView[tNode.index]))
: tNode.index;
@@ -158,7 +184,9 @@ export function listenToDomEvent(
} else {
stashEventListener(target as RElement, eventName, wrappedListener);
const cleanupFn = renderer.listen(target as RElement, eventName, wrappedListener);
const tCleanup = tView.firstCreatePass ? getOrCreateTViewCleanup(tView) : null;
const lCleanup = getOrCreateLViewCleanup(lView);
const lCleanupIndex = lCleanup.length;
lCleanup.push(wrappedListener, cleanupFn);
tCleanup && tCleanup.push(eventName, idxOrTargetGetter, lCleanupIndex, lCleanupIndex + 1);
}