fix(ivy): don't throw when attempting to destroy a destroyed ComponentRef (#31022)

Currently in Ivy we throw when attempting to destroy a `ComponentRef` that has been destroyed, however in ViewEngine we didn't which can cause some tests to break. These changes remove the error to match ViewEngine.

These changes resolve FW-1379.

PR Close #31022
This commit is contained in:
crisbeto 2019-06-13 17:41:45 +02:00 committed by Andrew Kushnir
parent 0c3bb6a731
commit 8f5c396a7c
2 changed files with 28 additions and 12 deletions

View File

@ -18,7 +18,6 @@ import {ElementRef as viewEngine_ElementRef} from '../linker/element_ref';
import {NgModuleRef as viewEngine_NgModuleRef} from '../linker/ng_module_factory'; import {NgModuleRef as viewEngine_NgModuleRef} from '../linker/ng_module_factory';
import {RendererFactory2} from '../render/api'; import {RendererFactory2} from '../render/api';
import {Sanitizer} from '../sanitization/security'; import {Sanitizer} from '../sanitization/security';
import {assertDefined} from '../util/assert';
import {VERSION} from '../version'; import {VERSION} from '../version';
import {NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR} from '../view/provider'; import {NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR} from '../view/provider';
@ -258,13 +257,16 @@ export class ComponentRef<T> extends viewEngine_ComponentRef<T> {
get injector(): Injector { return new NodeInjector(this._tNode, this._rootLView); } get injector(): Injector { return new NodeInjector(this._tNode, this._rootLView); }
destroy(): void { destroy(): void {
ngDevMode && assertDefined(this.destroyCbs, 'NgModule already destroyed'); if (this.destroyCbs) {
this.destroyCbs !.forEach(fn => fn()); this.destroyCbs.forEach(fn => fn());
this.destroyCbs = null; this.destroyCbs = null;
!this.hostView.destroyed && this.hostView.destroy(); !this.hostView.destroyed && this.hostView.destroy();
}
} }
onDestroy(callback: () => void): void { onDestroy(callback: () => void): void {
ngDevMode && assertDefined(this.destroyCbs, 'NgModule already destroyed'); if (this.destroyCbs) {
this.destroyCbs !.push(callback); this.destroyCbs.push(callback);
}
} }
} }

View File

@ -1035,6 +1035,20 @@ describe('ViewContainerRef', () => {
.toEqual( .toEqual(
'<p vcref=""></p><embedded-cmp-with-ngcontent>12<hr>34</embedded-cmp-with-ngcontent>'); '<p vcref=""></p><embedded-cmp-with-ngcontent>12<hr>34</embedded-cmp-with-ngcontent>');
}); });
it('should not throw when calling destroy() multiple times for a ComponentRef', () => {
@Component({template: ''})
class App {
}
TestBed.configureTestingModule({declarations: [App]});
const fixture = TestBed.createComponent(App);
fixture.detectChanges();
fixture.componentRef.destroy();
expect(() => fixture.componentRef.destroy()).not.toThrow();
});
}); });
describe('insertion points and declaration points', () => { describe('insertion points and declaration points', () => {
@ -1061,7 +1075,7 @@ describe('ViewContainerRef', () => {
<ng-template #foo> <ng-template #foo>
<div>{{name}}</div> <div>{{name}}</div>
</ng-template> </ng-template>
<child [tpl]="foo"></child> <child [tpl]="foo"></child>
` `
}) })
@ -1105,10 +1119,10 @@ describe('ViewContainerRef', () => {
<ng-template #cellTemplate let-cell> <ng-template #cellTemplate let-cell>
<div>{{cell}} - {{row.value}} - {{name}}</div> <div>{{cell}} - {{row.value}} - {{name}}</div>
</ng-template> </ng-template>
<loop-comp [tpl]="cellTemplate" [rows]="row.data"></loop-comp> <loop-comp [tpl]="cellTemplate" [rows]="row.data"></loop-comp>
</ng-template> </ng-template>
<loop-comp [tpl]="rowTemplate" [rows]="rows"></loop-comp> <loop-comp [tpl]="rowTemplate" [rows]="rows"></loop-comp>
`, `,
}) })
@ -1401,7 +1415,7 @@ describe('ViewContainerRef', () => {
<ng-template #foo> <ng-template #foo>
<span>{{name}}</span> <span>{{name}}</span>
</ng-template> </ng-template>
<child> <child>
<header vcref [tplRef]="foo" [name]="name">blah</header> <header vcref [tplRef]="foo" [name]="name">blah</header>
</child>` </child>`
@ -1780,7 +1794,7 @@ class ConstructorDir {
@Component({ @Component({
selector: 'constructor-app', selector: 'constructor-app',
template: ` template: `
<div *constructorDir> <div *constructorDir>
<span *constructorDir #foo></span> <span *constructorDir #foo></span>
</div> </div>