@@ -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 {
418418lView [ FLAGS ] |= LViewFlags . Destroyed ;
419419
420420executeOnDestroys ( tView , lView ) ;
421- removeListeners ( tView , lView ) ;
421+ processCleanups ( tView , lView ) ;
422422// For component views only, the local renderer is destroyed at clean up time.
423423if ( lView [ TVIEW ] . type === TViewType . Component && isProceduralRenderer ( lView [ RENDERER ] ) ) {
424424ngDevMode && 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 {
447447const 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 ;
448453if ( tCleanup !== null ) {
449- const lCleanup = lView [ CLEANUP ] ! ;
450454for ( let i = 0 ; i < tCleanup . length - 1 ; i += 2 ) {
451455if ( typeof tCleanup [ i ] === 'string' ) {
452456// This is a native DOM listener
453457const idxOrTargetGetter = tCleanup [ i + 1 ] ;
454458const target = typeof idxOrTargetGetter === 'function' ?
455459idxOrTargetGetter ( lView ) :
456460unwrapRNode ( lView [ idxOrTargetGetter ] ) ;
457- const listener = lCleanup [ tCleanup [ i + 2 ] ] ;
461+ const listener = lCleanup [ lastLCleanupIndex = tCleanup [ i + 2 ] ] ;
458462const useCaptureOrSubIdx = tCleanup [ i + 3 ] ;
459463if ( typeof useCaptureOrSubIdx === 'boolean' ) {
460464// native DOM listener registered with Renderer3
461465target . removeEventListener ( tCleanup [ i ] , listener , useCaptureOrSubIdx ) ;
462466} else {
463467if ( 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}
471475i += 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 ] ] ;
475479tCleanup [ 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+ }
478489lView [ CLEANUP ] = null ;
479490}
480491}