refactor(core): simplify signature of listenToDirectiveOutput (#60514)

We can simplify signature of listenToDirectiveOutput by passing less
arguments (some of them can be derived from already passed arguments).

PR Close #60514
This commit is contained in:
Pawel Kozlowski
2025-03-21 14:18:09 +01:00
committed by Alex Rickabaugh
parent 11994a715b
commit bf029e4f23
6 changed files with 24 additions and 61 deletions
@@ -207,7 +207,7 @@ export function listenerInternal(
(<any>existingListener).__ngLastListenerFn__ = listenerFn;
processOutputs = false;
} else {
listenerFn = wrapListener(tNode, lView, context, listenerFn);
listenerFn = wrapListener(tNode, lView, listenerFn);
stashEventListener(target as RElement, eventName, listenerFn);
const cleanupFn = renderer.listen(target as RElement, eventName, listenerFn);
@@ -217,7 +217,7 @@ export function listenerInternal(
} else {
// Even if there is no native listener to add, we still need to wrap the listener so that OnPush
// ancestors are marked dirty when an event occurs.
listenerFn = wrapListener(tNode, lView, context, listenerFn);
listenerFn = wrapListener(tNode, lView, listenerFn);
}
if (processOutputs) {
@@ -228,33 +228,13 @@ export function listenerInternal(
for (let i = 0; i < hostDirectiveOutputConfig.length; i += 2) {
const index = hostDirectiveOutputConfig[i] as number;
const lookupName = hostDirectiveOutputConfig[i + 1] as string;
listenToOutput(
tNode,
tView,
lView,
index,
lookupName,
eventName,
listenerFn,
lCleanup,
tCleanup,
);
listenToOutput(tNode, lView, index, lookupName, eventName, listenerFn);
}
}
if (outputConfig && outputConfig.length) {
for (const index of outputConfig) {
listenToOutput(
tNode,
tView,
lView,
index,
eventName,
eventName,
listenerFn,
lCleanup,
tCleanup,
);
listenToOutput(tNode, lView, index, eventName, eventName, listenerFn);
}
}
}
@@ -10,7 +10,7 @@ import {RuntimeError, RuntimeErrorCode} from '../../errors';
import {assertIndexInRange} from '../../util/assert';
import {DirectiveDef} from '../interfaces/definition';
import {TNode} from '../interfaces/node';
import {CONTEXT, LView, TVIEW, TView} from '../interfaces/view';
import {CONTEXT, LView, TVIEW} from '../interfaces/view';
import {stringifyForError} from '../util/stringify_utils';
import {getOrCreateLViewCleanup, getOrCreateTViewCleanup} from '../util/view_utils';
import {wrapListener} from './listeners';
@@ -28,17 +28,9 @@ export function createOutputListener<T = unknown>(
eventName: string,
) {
// TODO(pk): decouple checks from the actual binding
const wrappedListener = wrapListener(tNode, lView, lView[CONTEXT], listenerFn);
const wrappedListener = wrapListener(tNode, lView, listenerFn);
// TODO(pk): simplify signature of listenToDirectiveOutput
const hasBound = listenToDirectiveOutput(
tNode,
lView[TVIEW],
lView,
targetDef,
eventName,
wrappedListener,
);
const hasBound = listenToDirectiveOutput(tNode, lView, targetDef, eventName, wrappedListener);
if (!hasBound && ngDevMode) {
throw new RuntimeError(
@@ -51,14 +43,11 @@ export function createOutputListener<T = unknown>(
/** Listens to an output on a specific directive. */
function listenToDirectiveOutput(
tNode: TNode,
tView: TView,
lView: LView,
target: DirectiveDef<unknown>,
eventName: string,
listenerFn: (e?: any) => any,
): boolean {
const tCleanup = tView.firstCreatePass ? getOrCreateTViewCleanup(tView) : null;
const lCleanup = getOrCreateLViewCleanup(lView);
let hostIndex: number | null = null;
let hostDirectivesStart: number | null = null;
let hostDirectivesEnd: number | null = null;
@@ -91,14 +80,11 @@ function listenToDirectiveOutput(
hasOutput = true;
listenToOutput(
tNode,
tView,
lView,
index,
hostDirectiveOutputs[i + 1] as string,
eventName,
listenerFn,
lCleanup,
tCleanup,
);
} else if (index > hostDirectivesEnd) {
break;
@@ -109,17 +95,7 @@ function listenToDirectiveOutput(
if (target.outputs.hasOwnProperty(eventName)) {
ngDevMode && assertIndexInRange(lView, hostIndex);
hasOutput = true;
listenToOutput(
tNode,
tView,
lView,
hostIndex,
eventName,
eventName,
listenerFn,
lCleanup,
tCleanup,
);
listenToOutput(tNode, lView, hostIndex, eventName, eventName, listenerFn);
}
return hasOutput;
@@ -127,18 +103,16 @@ function listenToDirectiveOutput(
export function listenToOutput(
tNode: TNode,
tView: TView,
lView: LView,
index: number,
directiveIndex: number,
lookupName: string,
eventName: string,
listenerFn: (e?: any) => any,
lCleanup: any[],
tCleanup: any[] | null,
) {
ngDevMode && assertIndexInRange(lView, index);
const instance = lView[index];
const def = tView.data[index] as DirectiveDef<unknown>;
ngDevMode && assertIndexInRange(lView, directiveIndex);
const instance = lView[directiveIndex];
const tView = lView[TVIEW];
const def = tView.data[directiveIndex] as DirectiveDef<unknown>;
const propertyName = def.outputs[lookupName];
const output = instance[propertyName];
@@ -146,6 +120,9 @@ export function listenToOutput(
throw new Error(`@Output ${propertyName} not initialized in '${instance.constructor.name}'.`);
}
// TODO(pk): introduce utility to store cleanup or find a different way of sharing code with listener
const tCleanup = tView.firstCreatePass ? getOrCreateTViewCleanup(tView) : null;
const lCleanup = getOrCreateLViewCleanup(lView);
const subscription = (output as SubscribableOutput<unknown>).subscribe(listenerFn);
const idx = lCleanup.length;
lCleanup.push(listenerFn, subscription);
+2 -2
View File
@@ -11,7 +11,7 @@ import {setActiveConsumer} from '@angular/core/primitives/signals';
import {NotificationSource} from '../../change_detection/scheduling/zoneless_scheduling';
import {TNode} from '../interfaces/node';
import {isComponentHost} from '../interfaces/type_checks';
import {INJECTOR, LView} from '../interfaces/view';
import {CONTEXT, INJECTOR, LView} from '../interfaces/view';
import {getComponentLViewByIndex} from '../util/view_utils';
import {profiler} from '../profiler';
import {ProfilerEvent} from '../profiler_types';
@@ -31,7 +31,6 @@ import {markViewDirty} from '../instructions/mark_view_dirty';
export function wrapListener(
tNode: TNode,
lView: LView<{} | null>,
context: {} | null,
listenerFn: (e?: any) => any,
): EventListener {
// Note: we are performing most of the work in the listener function itself
@@ -48,6 +47,7 @@ export function wrapListener(
const startView = isComponentHost(tNode) ? getComponentLViewByIndex(tNode.index, lView) : lView;
markViewDirty(startView, NotificationSource.Listener);
const context = lView[CONTEXT];
let result = executeListenerWithErrorHandling(lView, context, listenerFn, e);
// A just-invoked listener function might have coalesced listeners so we need to check for
// their presence and invoke as needed.
@@ -387,8 +387,10 @@
"getNodeInjectable",
"getNullInjector",
"getOrCreateInjectable",
"getOrCreateLViewCleanup",
"getOrCreateNodeInjectorForNode",
"getOrCreateTNode",
"getOrCreateTViewCleanup",
"getOrCreateViewRefs",
"getOwnDefinition",
"getParentInjectorIndex",
@@ -374,8 +374,10 @@
"getNodeInjectable",
"getNullInjector",
"getOrCreateInjectable",
"getOrCreateLViewCleanup",
"getOrCreateNodeInjectorForNode",
"getOrCreateTNode",
"getOrCreateTViewCleanup",
"getOrCreateViewRefs",
"getOwnDefinition",
"getParentInjectorIndex",
@@ -318,8 +318,10 @@
"getNodeInjectable",
"getNullInjector",
"getOrCreateInjectable",
"getOrCreateLViewCleanup",
"getOrCreateNodeInjectorForNode",
"getOrCreateTNode",
"getOrCreateTViewCleanup",
"getOrCreateViewRefs",
"getOwnDefinition",
"getParentInjectorIndex",