fix(core): error when invoking callbacks registered via ViewRef.onDestroy (#37543)

Invoking a callback registered through `ViewRef.onDestroy` throws an error, because we weren't registering it correctly in the internal data structure. These changes also remove the `storeCleanupFn` function, because it was mostly identical to `storeCleanupWithContext` and was only used in one place.

Fixes #36213.

PR Close #37543
This commit is contained in:
crisbeto 2020-06-25 21:35:46 +02:00 committed by Andrew Kushnir
parent c00f4ab2ae
commit 543b679762
3 changed files with 21 additions and 19 deletions

View File

@ -795,22 +795,6 @@ export function storeCleanupWithContext(
} }
} }
/**
* Saves the cleanup function itself in LView.cleanupInstances.
*
* This is necessary for functions that are wrapped with their contexts, like in renderer2
* listeners.
*
* On the first template pass, the index of the cleanup function is saved in TView.
*/
export function storeCleanupFn(tView: TView, lView: LView, cleanupFn: Function): void {
getLCleanup(lView).push(cleanupFn);
if (tView.firstCreatePass) {
getTViewCleanup(tView).push(lView[CLEANUP]!.length - 1, null);
}
}
/** /**
* Constructs a TNode object from the arguments. * Constructs a TNode object from the arguments.
* *

View File

@ -11,7 +11,7 @@ import {ChangeDetectorRef as viewEngine_ChangeDetectorRef} from '../change_detec
import {ViewContainerRef as viewEngine_ViewContainerRef} from '../linker/view_container_ref'; import {ViewContainerRef as viewEngine_ViewContainerRef} from '../linker/view_container_ref';
import {EmbeddedViewRef as viewEngine_EmbeddedViewRef, InternalViewRef as viewEngine_InternalViewRef} from '../linker/view_ref'; import {EmbeddedViewRef as viewEngine_EmbeddedViewRef, InternalViewRef as viewEngine_InternalViewRef} from '../linker/view_ref';
import {assertDefined} from '../util/assert'; import {assertDefined} from '../util/assert';
import {checkNoChangesInRootView, checkNoChangesInternal, detectChangesInRootView, detectChangesInternal, markViewDirty, storeCleanupFn} from './instructions/shared'; import {checkNoChangesInRootView, checkNoChangesInternal, detectChangesInRootView, detectChangesInternal, markViewDirty, storeCleanupWithContext} from './instructions/shared';
import {CONTAINER_HEADER_OFFSET} from './interfaces/container'; import {CONTAINER_HEADER_OFFSET} from './interfaces/container';
import {TElementNode, TNode, TNodeType, TViewNode} from './interfaces/node'; import {TElementNode, TNode, TNodeType, TViewNode} from './interfaces/node';
import {isLContainer} from './interfaces/type_checks'; import {isLContainer} from './interfaces/type_checks';
@ -88,7 +88,7 @@ export class ViewRef<T> implements viewEngine_EmbeddedViewRef<T>, viewEngine_Int
} }
onDestroy(callback: Function) { onDestroy(callback: Function) {
storeCleanupFn(this._lView[TVIEW], this._lView, callback); storeCleanupWithContext(this._lView[TVIEW], this._lView, null, callback);
} }
/** /**

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {ApplicationRef, Component, ComponentFactoryResolver, ComponentRef, ElementRef, Injector, NgModule} from '@angular/core'; import {ApplicationRef, ChangeDetectorRef, Component, ComponentFactoryResolver, ComponentRef, ElementRef, Injector, NgModule} from '@angular/core';
import {InternalViewRef} from '@angular/core/src/linker/view_ref'; import {InternalViewRef} from '@angular/core/src/linker/view_ref';
import {TestBed} from '@angular/core/testing'; import {TestBed} from '@angular/core/testing';
@ -54,4 +54,22 @@ describe('ViewRef', () => {
fixture.detectChanges(); fixture.detectChanges();
expect(document.body.querySelector('dynamic-cpt')).toBeFalsy(); expect(document.body.querySelector('dynamic-cpt')).toBeFalsy();
}); });
it('should invoke the onDestroy callback of a view ref', () => {
let called = false;
@Component({template: ''})
class App {
constructor(changeDetectorRef: ChangeDetectorRef) {
(changeDetectorRef as InternalViewRef).onDestroy(() => called = true);
}
}
TestBed.configureTestingModule({declarations: [App]});
const fixture = TestBed.createComponent(App);
fixture.detectChanges();
fixture.destroy();
expect(called).toBe(true);
});
}); });