@@ -10,7 +10,7 @@ import {ViewEncapsulation} from '../metadata/view';
|
10 | 10 | import {Renderer2} from '../render/api';
|
11 | 11 | import {RendererStyleFlags2} from '../render/api_flags';
|
12 | 12 | import {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'; |
14 | 14 | import {assertLContainer, assertLView, assertTNodeForLView} from './assert';
|
15 | 15 | import {attachData} from './context_discovery';
|
16 | 16 | import {icuContainerIterate} from './i18n/i18n_tree_shaking';
|
@@ -418,7 +418,7 @@ function cleanUpView(tView: TView, lView: LView): void {
|
418 | 418 | lView[FLAGS] |= LViewFlags.Destroyed;
|
419 | 419 |
|
420 | 420 | executeOnDestroys(tView, lView);
|
421 |
| -removeListeners(tView, lView); |
| 421 | +processCleanups(tView, lView); |
422 | 422 | // For component views only, the local renderer is destroyed at clean up time.
|
423 | 423 | if (lView[TVIEW].type === TViewType.Component && isProceduralRenderer(lView[RENDERER])) {
|
424 | 424 | ngDevMode && ngDevMode.rendererDestroy++;
|
@@ -443,38 +443,49 @@ function cleanUpView(tView: TView, lView: LView): void {
|
443 | 443 | }
|
444 | 444 |
|
445 | 445 | /** Removes listeners and unsubscribes from output subscriptions */
|
446 |
| -function removeListeners(tView: TView, lView: LView): void { |
| 446 | +function processCleanups(tView: TView, lView: LView): void { |
447 | 447 | 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; |
448 | 453 | if (tCleanup !== null) {
|
449 |
| -const lCleanup = lView[CLEANUP]!; |
450 | 454 | for (let i = 0; i < tCleanup.length - 1; i += 2) {
|
451 | 455 | if (typeof tCleanup[i] === 'string') {
|
452 | 456 | // This is a native DOM listener
|
453 | 457 | const idxOrTargetGetter = tCleanup[i + 1];
|
454 | 458 | const target = typeof idxOrTargetGetter === 'function' ?
|
455 | 459 | idxOrTargetGetter(lView) :
|
456 | 460 | unwrapRNode(lView[idxOrTargetGetter]);
|
457 |
| -const listener = lCleanup[tCleanup[i + 2]]; |
| 461 | +const listener = lCleanup[lastLCleanupIndex = tCleanup[i + 2]]; |
458 | 462 | const useCaptureOrSubIdx = tCleanup[i + 3];
|
459 | 463 | if (typeof useCaptureOrSubIdx === 'boolean') {
|
460 | 464 | // native DOM listener registered with Renderer3
|
461 | 465 | target.removeEventListener(tCleanup[i], listener, useCaptureOrSubIdx);
|
462 | 466 | } else {
|
463 | 467 | if (useCaptureOrSubIdx >= 0) {
|
464 | 468 | // unregister
|
465 |
| -lCleanup[useCaptureOrSubIdx](); |
| 469 | +lCleanup[lastLCleanupIndex = useCaptureOrSubIdx](); |
466 | 470 | } else {
|
467 | 471 | // Subscription
|
468 |
| -lCleanup[-useCaptureOrSubIdx].unsubscribe(); |
| 472 | +lCleanup[lastLCleanupIndex = -useCaptureOrSubIdx].unsubscribe(); |
469 | 473 | }
|
470 | 474 | }
|
471 | 475 | i += 2;
|
472 | 476 | } else {
|
473 | 477 | // 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]]; |
475 | 479 | tCleanup[i].call(context);
|
476 | 480 | }
|
477 | 481 | }
|
| 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 | +} |
478 | 489 | lView[CLEANUP] = null;
|
479 | 490 | }
|
480 | 491 | }
|
|
0 commit comments