diff --git a/packages/core/src/render3/instructions/listener.ts b/packages/core/src/render3/instructions/listener.ts index 6e3903a24a7..383cdf63bb3 100644 --- a/packages/core/src/render3/instructions/listener.ts +++ b/packages/core/src/render3/instructions/listener.ts @@ -207,7 +207,7 @@ export function listenerInternal( (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); } } } diff --git a/packages/core/src/render3/view/directive_outputs.ts b/packages/core/src/render3/view/directive_outputs.ts index 60d6d37ebc3..33b4924041d 100644 --- a/packages/core/src/render3/view/directive_outputs.ts +++ b/packages/core/src/render3/view/directive_outputs.ts @@ -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( 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( /** Listens to an output on a specific directive. */ function listenToDirectiveOutput( tNode: TNode, - tView: TView, lView: LView, target: DirectiveDef, 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; + ngDevMode && assertIndexInRange(lView, directiveIndex); + const instance = lView[directiveIndex]; + const tView = lView[TVIEW]; + const def = tView.data[directiveIndex] as DirectiveDef; 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).subscribe(listenerFn); const idx = lCleanup.length; lCleanup.push(listenerFn, subscription); diff --git a/packages/core/src/render3/view/listeners.ts b/packages/core/src/render3/view/listeners.ts index 2d714e67b11..dbb4ae21a05 100644 --- a/packages/core/src/render3/view/listeners.ts +++ b/packages/core/src/render3/view/listeners.ts @@ -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. 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 df61ba2e912..955ae05958d 100644 --- a/packages/core/test/bundling/forms_reactive/bundle.golden_symbols.json +++ b/packages/core/test/bundling/forms_reactive/bundle.golden_symbols.json @@ -387,8 +387,10 @@ "getNodeInjectable", "getNullInjector", "getOrCreateInjectable", + "getOrCreateLViewCleanup", "getOrCreateNodeInjectorForNode", "getOrCreateTNode", + "getOrCreateTViewCleanup", "getOrCreateViewRefs", "getOwnDefinition", "getParentInjectorIndex", 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 dd564b728c8..bb018a0ffe3 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 @@ -374,8 +374,10 @@ "getNodeInjectable", "getNullInjector", "getOrCreateInjectable", + "getOrCreateLViewCleanup", "getOrCreateNodeInjectorForNode", "getOrCreateTNode", + "getOrCreateTViewCleanup", "getOrCreateViewRefs", "getOwnDefinition", "getParentInjectorIndex", diff --git a/packages/core/test/bundling/todo/bundle.golden_symbols.json b/packages/core/test/bundling/todo/bundle.golden_symbols.json index c9a0d6ce79f..8887ccc61c8 100644 --- a/packages/core/test/bundling/todo/bundle.golden_symbols.json +++ b/packages/core/test/bundling/todo/bundle.golden_symbols.json @@ -318,8 +318,10 @@ "getNodeInjectable", "getNullInjector", "getOrCreateInjectable", + "getOrCreateLViewCleanup", "getOrCreateNodeInjectorForNode", "getOrCreateTNode", + "getOrCreateTViewCleanup", "getOrCreateViewRefs", "getOwnDefinition", "getParentInjectorIndex",