fix(ivy): destroy injector when module is destroyed (#27793)
Destroys the module's injector when an `NgModule` is destroyed which in turn calls the `ngOnDestroy` methods on the instantiated providers. This PR resolves FW-739. PR Close #27793
This commit is contained in:
parent
2b9cc8503d
commit
ab2bf83398
|
@ -102,7 +102,8 @@ export class R3Injector {
|
||||||
/**
|
/**
|
||||||
* Flag indicating that this injector was previously destroyed.
|
* Flag indicating that this injector was previously destroyed.
|
||||||
*/
|
*/
|
||||||
private destroyed = false;
|
get destroyed(): boolean { return this._destroyed; }
|
||||||
|
private _destroyed = false;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
def: InjectorType<any>, additionalProviders: StaticProvider[]|null,
|
def: InjectorType<any>, additionalProviders: StaticProvider[]|null,
|
||||||
|
@ -138,7 +139,7 @@ export class R3Injector {
|
||||||
this.assertNotDestroyed();
|
this.assertNotDestroyed();
|
||||||
|
|
||||||
// Set destroyed = true first, in case lifecycle hooks re-enter destroy().
|
// Set destroyed = true first, in case lifecycle hooks re-enter destroy().
|
||||||
this.destroyed = true;
|
this._destroyed = true;
|
||||||
try {
|
try {
|
||||||
// Call all the lifecycle hooks.
|
// Call all the lifecycle hooks.
|
||||||
this.onDestroy.forEach(service => service.ngOnDestroy());
|
this.onDestroy.forEach(service => service.ngOnDestroy());
|
||||||
|
@ -189,7 +190,7 @@ export class R3Injector {
|
||||||
}
|
}
|
||||||
|
|
||||||
private assertNotDestroyed(): void {
|
private assertNotDestroyed(): void {
|
||||||
if (this.destroyed) {
|
if (this._destroyed) {
|
||||||
throw new Error('Injector has already been destroyed.');
|
throw new Error('Injector has already been destroyed.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -273,7 +273,7 @@ export class ComponentRef<T> extends viewEngine_ComponentRef<T> {
|
||||||
ngDevMode && assertDefined(this.destroyCbs, 'NgModule already destroyed');
|
ngDevMode && assertDefined(this.destroyCbs, 'NgModule already destroyed');
|
||||||
this.destroyCbs !.forEach(fn => fn());
|
this.destroyCbs !.forEach(fn => fn());
|
||||||
this.destroyCbs = null;
|
this.destroyCbs = null;
|
||||||
this.hostView.destroy();
|
!this.hostView.destroyed && this.hostView.destroy();
|
||||||
}
|
}
|
||||||
onDestroy(callback: () => void): void {
|
onDestroy(callback: () => void): void {
|
||||||
ngDevMode && assertDefined(this.destroyCbs, 'NgModule already destroyed');
|
ngDevMode && assertDefined(this.destroyCbs, 'NgModule already destroyed');
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
import {INJECTOR, Injector} from '../di/injector';
|
import {INJECTOR, Injector} from '../di/injector';
|
||||||
import {InjectFlags} from '../di/interface/injector';
|
import {InjectFlags} from '../di/interface/injector';
|
||||||
import {StaticProvider} from '../di/interface/provider';
|
import {StaticProvider} from '../di/interface/provider';
|
||||||
import {createInjector} from '../di/r3_injector';
|
import {R3Injector, createInjector} from '../di/r3_injector';
|
||||||
import {Type} from '../interface/type';
|
import {Type} from '../interface/type';
|
||||||
import {ComponentFactoryResolver as viewEngine_ComponentFactoryResolver} from '../linker/component_factory_resolver';
|
import {ComponentFactoryResolver as viewEngine_ComponentFactoryResolver} from '../linker/component_factory_resolver';
|
||||||
import {InternalNgModuleRef, NgModuleFactory as viewEngine_NgModuleFactory, NgModuleRef as viewEngine_NgModuleRef} from '../linker/ng_module_factory';
|
import {InternalNgModuleRef, NgModuleFactory as viewEngine_NgModuleFactory, NgModuleRef as viewEngine_NgModuleRef} from '../linker/ng_module_factory';
|
||||||
|
@ -32,7 +32,7 @@ export class NgModuleRef<T> extends viewEngine_NgModuleRef<T> implements Interna
|
||||||
// tslint:disable-next-line:require-internal-with-underscore
|
// tslint:disable-next-line:require-internal-with-underscore
|
||||||
_bootstrapComponents: Type<any>[] = [];
|
_bootstrapComponents: Type<any>[] = [];
|
||||||
// tslint:disable-next-line:require-internal-with-underscore
|
// tslint:disable-next-line:require-internal-with-underscore
|
||||||
_r3Injector: Injector;
|
_r3Injector: R3Injector;
|
||||||
injector: Injector = this;
|
injector: Injector = this;
|
||||||
instance: T;
|
instance: T;
|
||||||
destroyCbs: (() => void)[]|null = [];
|
destroyCbs: (() => void)[]|null = [];
|
||||||
|
@ -52,7 +52,7 @@ export class NgModuleRef<T> extends viewEngine_NgModuleRef<T> implements Interna
|
||||||
},
|
},
|
||||||
COMPONENT_FACTORY_RESOLVER
|
COMPONENT_FACTORY_RESOLVER
|
||||||
];
|
];
|
||||||
this._r3Injector = createInjector(ngModuleType, _parent, additionalProviders);
|
this._r3Injector = createInjector(ngModuleType, _parent, additionalProviders) as R3Injector;
|
||||||
this.instance = this.get(ngModuleType);
|
this.instance = this.get(ngModuleType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,6 +70,8 @@ export class NgModuleRef<T> extends viewEngine_NgModuleRef<T> implements Interna
|
||||||
|
|
||||||
destroy(): void {
|
destroy(): void {
|
||||||
ngDevMode && assertDefined(this.destroyCbs, 'NgModule already destroyed');
|
ngDevMode && assertDefined(this.destroyCbs, 'NgModule already destroyed');
|
||||||
|
const injector = this._r3Injector;
|
||||||
|
!injector.destroyed && injector.destroy();
|
||||||
this.destroyCbs !.forEach(fn => fn());
|
this.destroyCbs !.forEach(fn => fn());
|
||||||
this.destroyCbs = null;
|
this.destroyCbs = null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1045,8 +1045,7 @@ function declareTests(config?: {useJit: boolean}) {
|
||||||
expect(created).toBe(false);
|
expect(created).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
fixmeIvy('FW-739: TestBed: destroy on NgModuleRef is not being called')
|
it('should support ngOnDestroy on any provider', () => {
|
||||||
.it('should support ngOnDestroy on any provider', () => {
|
|
||||||
let destroyed = false;
|
let destroyed = false;
|
||||||
|
|
||||||
class SomeInjectable {
|
class SomeInjectable {
|
||||||
|
@ -1065,8 +1064,7 @@ function declareTests(config?: {useJit: boolean}) {
|
||||||
expect(destroyed).toBe(true);
|
expect(destroyed).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
fixmeIvy('FW-739: TestBed: destroy on NgModuleRef is not being called')
|
it('should support ngOnDestroy for lazy providers', () => {
|
||||||
.it('should support ngOnDestroy for lazy providers', () => {
|
|
||||||
let created = false;
|
let created = false;
|
||||||
let destroyed = false;
|
let destroyed = false;
|
||||||
|
|
||||||
|
|
|
@ -213,6 +213,7 @@ export class DowngradeComponentAdapter {
|
||||||
}
|
}
|
||||||
|
|
||||||
registerCleanup() {
|
registerCleanup() {
|
||||||
|
const testabilityRegistry = this.componentRef.injector.get(TestabilityRegistry);
|
||||||
const destroyComponentRef = this.wrapCallback(() => this.componentRef.destroy());
|
const destroyComponentRef = this.wrapCallback(() => this.componentRef.destroy());
|
||||||
let destroyed = false;
|
let destroyed = false;
|
||||||
|
|
||||||
|
@ -220,8 +221,7 @@ export class DowngradeComponentAdapter {
|
||||||
this.componentScope.$on('$destroy', () => {
|
this.componentScope.$on('$destroy', () => {
|
||||||
if (!destroyed) {
|
if (!destroyed) {
|
||||||
destroyed = true;
|
destroyed = true;
|
||||||
this.componentRef.injector.get(TestabilityRegistry)
|
testabilityRegistry.unregisterApplication(this.componentRef.location.nativeElement);
|
||||||
.unregisterApplication(this.componentRef.location.nativeElement);
|
|
||||||
destroyComponentRef();
|
destroyComponentRef();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue