@@ -10,7 +10,7 @@ import {ViewEncapsulation} from '../metadata/view';
1010import { Renderer2 } from '../render/api' ;
1111import { RendererStyleFlags2 } from '../render/api_flags' ;
1212import { addToArray , removeFromArray } from '../util/array_utils' ;
13- import { assertDefined , assertDomNode , assertEqual , assertString } from '../util/assert' ;
13+ import { assertDefined , assertDomNode , assertEqual , assertFunction , assertString } from '../util/assert' ;
1414import { assertLContainer , assertLView , assertTNodeForLView } from './assert' ;
1515import { attachPatchData } from './context_discovery' ;
1616import { icuContainerIterate } from './i18n/i18n_tree_shaking' ;
@@ -418,7 +418,7 @@ function cleanUpView(tView: TView, lView: LView): void {
418418 lView [ FLAGS ] |= LViewFlags . Destroyed ;
419419
420420 executeOnDestroys ( tView , lView ) ;
421- removeListeners ( tView , lView ) ;
421+ processCleanups ( tView , lView ) ;
422422 // For component views only, the local renderer is destroyed at clean up time.
423423 if ( lView [ TVIEW ] . type === TViewType . Component && isProceduralRenderer ( lView [ RENDERER ] ) ) {
424424 ngDevMode && ngDevMode . rendererDestroy ++ ;
@@ -443,38 +443,49 @@ function cleanUpView(tView: TView, lView: LView): void {
443443}
444444
445445/** Removes listeners and unsubscribes from output subscriptions */
446- function removeListeners ( tView : TView , lView : LView ) : void {
446+ function processCleanups ( tView : TView , lView : LView ) : void {
447447 const tCleanup = tView . cleanup ;
448+ const lCleanup = lView [ CLEANUP ] ! ;
449+ // `LCleanup` contains both share information with `TCleanup` as well as instance specific
450+ // information appended at the end. We need to know where the end of the `TCleanup` information
451+ // is, and we track this with `lastLCleanupIndex`.
452+ let lastLCleanupIndex = - 1 ;
448453 if ( tCleanup !== null ) {
449- const lCleanup = lView [ CLEANUP ] ! ;
450454 for ( let i = 0 ; i < tCleanup . length - 1 ; i += 2 ) {
451455 if ( typeof tCleanup [ i ] === 'string' ) {
452456 // This is a native DOM listener
453457 const idxOrTargetGetter = tCleanup [ i + 1 ] ;
454458 const target = typeof idxOrTargetGetter === 'function' ?
455459 idxOrTargetGetter ( lView ) :
456460 unwrapRNode ( lView [ idxOrTargetGetter ] ) ;
457- const listener = lCleanup [ tCleanup [ i + 2 ] ] ;
461+ const listener = lCleanup [ lastLCleanupIndex = tCleanup [ i + 2 ] ] ;
458462 const useCaptureOrSubIdx = tCleanup [ i + 3 ] ;
459463 if ( typeof useCaptureOrSubIdx === 'boolean' ) {
460464 // native DOM listener registered with Renderer3
461465 target . removeEventListener ( tCleanup [ i ] , listener , useCaptureOrSubIdx ) ;
462466 } else {
463467 if ( useCaptureOrSubIdx >= 0 ) {
464468 // unregister
465- lCleanup [ useCaptureOrSubIdx ] ( ) ;
469+ lCleanup [ lastLCleanupIndex = useCaptureOrSubIdx ] ( ) ;
466470 } else {
467471 // Subscription
468- lCleanup [ - useCaptureOrSubIdx ] . unsubscribe ( ) ;
472+ lCleanup [ lastLCleanupIndex = - useCaptureOrSubIdx ] . unsubscribe ( ) ;
469473 }
470474 }
471475 i += 2 ;
472476 } else {
473477 // This is a cleanup function that is grouped with the index of its context
474- const context = lCleanup [ tCleanup [ i + 1 ] ] ;
478+ const context = lCleanup [ lastLCleanupIndex = tCleanup [ i + 1 ] ] ;
475479 tCleanup [ i ] . call ( context ) ;
476480 }
477481 }
482+ if ( lCleanup !== null ) {
483+ for ( let i = lastLCleanupIndex + 1 ; i < lCleanup . length ; i ++ ) {
484+ const instanceCleanupFn = lCleanup [ i ] ;
485+ ngDevMode && assertFunction ( instanceCleanupFn , 'Expecting instance cleanup function.' ) ;
486+ instanceCleanupFn ( ) ;
487+ }
488+ }
478489 lView [ CLEANUP ] = null ;
479490 }
480491}
0 commit comments