Skip to content

Commit ab2bf83

Browse files
crisbetoalxhub
authored andcommitted
fix(ivy): destroy injector when module is destroyed (angular#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 angular#27793
1 parent 2b9cc85 commit ab2bf83

File tree

5 files changed

+50
-49
lines changed

5 files changed

+50
-49
lines changed

packages/core/src/di/r3_injector.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,8 @@ export class R3Injector {
102102
/**
103103
* Flag indicating that this injector was previously destroyed.
104104
*/
105-
private destroyed = false;
105+
get destroyed(): boolean { return this._destroyed; }
106+
private _destroyed = false;
106107

107108
constructor(
108109
def: InjectorType<any>, additionalProviders: StaticProvider[]|null,
@@ -138,7 +139,7 @@ export class R3Injector {
138139
this.assertNotDestroyed();
139140

140141
// Set destroyed = true first, in case lifecycle hooks re-enter destroy().
141-
this.destroyed = true;
142+
this._destroyed = true;
142143
try {
143144
// Call all the lifecycle hooks.
144145
this.onDestroy.forEach(service => service.ngOnDestroy());
@@ -189,7 +190,7 @@ export class R3Injector {
189190
}
190191

191192
private assertNotDestroyed(): void {
192-
if (this.destroyed) {
193+
if (this._destroyed) {
193194
throw new Error('Injector has already been destroyed.');
194195
}
195196
}

packages/core/src/render3/component_ref.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,7 @@ export class ComponentRef<T> extends viewEngine_ComponentRef<T> {
273273
ngDevMode && assertDefined(this.destroyCbs, 'NgModule already destroyed');
274274
this.destroyCbs !.forEach(fn => fn());
275275
this.destroyCbs = null;
276-
this.hostView.destroy();
276+
!this.hostView.destroyed && this.hostView.destroy();
277277
}
278278
onDestroy(callback: () => void): void {
279279
ngDevMode && assertDefined(this.destroyCbs, 'NgModule already destroyed');

packages/core/src/render3/ng_module_ref.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import {INJECTOR, Injector} from '../di/injector';
1010
import {InjectFlags} from '../di/interface/injector';
1111
import {StaticProvider} from '../di/interface/provider';
12-
import {createInjector} from '../di/r3_injector';
12+
import {R3Injector, createInjector} from '../di/r3_injector';
1313
import {Type} from '../interface/type';
1414
import {ComponentFactoryResolver as viewEngine_ComponentFactoryResolver} from '../linker/component_factory_resolver';
1515
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
3232
// tslint:disable-next-line:require-internal-with-underscore
3333
_bootstrapComponents: Type<any>[] = [];
3434
// tslint:disable-next-line:require-internal-with-underscore
35-
_r3Injector: Injector;
35+
_r3Injector: R3Injector;
3636
injector: Injector = this;
3737
instance: T;
3838
destroyCbs: (() => void)[]|null = [];
@@ -52,7 +52,7 @@ export class NgModuleRef<T> extends viewEngine_NgModuleRef<T> implements Interna
5252
},
5353
COMPONENT_FACTORY_RESOLVER
5454
];
55-
this._r3Injector = createInjector(ngModuleType, _parent, additionalProviders);
55+
this._r3Injector = createInjector(ngModuleType, _parent, additionalProviders) as R3Injector;
5656
this.instance = this.get(ngModuleType);
5757
}
5858

@@ -70,6 +70,8 @@ export class NgModuleRef<T> extends viewEngine_NgModuleRef<T> implements Interna
7070

7171
destroy(): void {
7272
ngDevMode && assertDefined(this.destroyCbs, 'NgModule already destroyed');
73+
const injector = this._r3Injector;
74+
!injector.destroyed && injector.destroy();
7375
this.destroyCbs !.forEach(fn => fn());
7476
this.destroyCbs = null;
7577
}

packages/core/test/linker/ng_module_integration_spec.ts

Lines changed: 38 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1045,55 +1045,53 @@ function declareTests(config?: {useJit: boolean}) {
10451045
expect(created).toBe(false);
10461046
});
10471047

1048-
fixmeIvy('FW-739: TestBed: destroy on NgModuleRef is not being called')
1049-
.it('should support ngOnDestroy on any provider', () => {
1050-
let destroyed = false;
1048+
it('should support ngOnDestroy on any provider', () => {
1049+
let destroyed = false;
10511050

1052-
class SomeInjectable {
1053-
ngOnDestroy() { destroyed = true; }
1054-
}
1051+
class SomeInjectable {
1052+
ngOnDestroy() { destroyed = true; }
1053+
}
10551054

1056-
@NgModule({providers: [SomeInjectable]})
1057-
class SomeModule {
1058-
// Inject SomeInjectable to make it eager...
1059-
constructor(i: SomeInjectable) {}
1060-
}
1055+
@NgModule({providers: [SomeInjectable]})
1056+
class SomeModule {
1057+
// Inject SomeInjectable to make it eager...
1058+
constructor(i: SomeInjectable) {}
1059+
}
10611060

1062-
const moduleRef = createModule(SomeModule);
1063-
expect(destroyed).toBe(false);
1064-
moduleRef.destroy();
1065-
expect(destroyed).toBe(true);
1066-
});
1061+
const moduleRef = createModule(SomeModule);
1062+
expect(destroyed).toBe(false);
1063+
moduleRef.destroy();
1064+
expect(destroyed).toBe(true);
1065+
});
10671066

1068-
fixmeIvy('FW-739: TestBed: destroy on NgModuleRef is not being called')
1069-
.it('should support ngOnDestroy for lazy providers', () => {
1070-
let created = false;
1071-
let destroyed = false;
1067+
it('should support ngOnDestroy for lazy providers', () => {
1068+
let created = false;
1069+
let destroyed = false;
10721070

1073-
class SomeInjectable {
1074-
constructor() { created = true; }
1075-
ngOnDestroy() { destroyed = true; }
1076-
}
1071+
class SomeInjectable {
1072+
constructor() { created = true; }
1073+
ngOnDestroy() { destroyed = true; }
1074+
}
10771075

1078-
@NgModule({providers: [SomeInjectable]})
1079-
class SomeModule {
1080-
}
1076+
@NgModule({providers: [SomeInjectable]})
1077+
class SomeModule {
1078+
}
10811079

1082-
let moduleRef = createModule(SomeModule);
1083-
expect(created).toBe(false);
1084-
expect(destroyed).toBe(false);
1080+
let moduleRef = createModule(SomeModule);
1081+
expect(created).toBe(false);
1082+
expect(destroyed).toBe(false);
10851083

1086-
// no error if the provider was not yet created
1087-
moduleRef.destroy();
1088-
expect(created).toBe(false);
1089-
expect(destroyed).toBe(false);
1084+
// no error if the provider was not yet created
1085+
moduleRef.destroy();
1086+
expect(created).toBe(false);
1087+
expect(destroyed).toBe(false);
10901088

1091-
moduleRef = createModule(SomeModule);
1092-
moduleRef.injector.get(SomeInjectable);
1093-
expect(created).toBe(true);
1094-
moduleRef.destroy();
1095-
expect(destroyed).toBe(true);
1096-
});
1089+
moduleRef = createModule(SomeModule);
1090+
moduleRef.injector.get(SomeInjectable);
1091+
expect(created).toBe(true);
1092+
moduleRef.destroy();
1093+
expect(destroyed).toBe(true);
1094+
});
10971095
});
10981096

10991097
describe('imported and exported modules', () => {

packages/upgrade/src/common/downgrade_component_adapter.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -213,15 +213,15 @@ export class DowngradeComponentAdapter {
213213
}
214214

215215
registerCleanup() {
216+
const testabilityRegistry = this.componentRef.injector.get(TestabilityRegistry);
216217
const destroyComponentRef = this.wrapCallback(() => this.componentRef.destroy());
217218
let destroyed = false;
218219

219220
this.element.on !('$destroy', () => this.componentScope.$destroy());
220221
this.componentScope.$on('$destroy', () => {
221222
if (!destroyed) {
222223
destroyed = true;
223-
this.componentRef.injector.get(TestabilityRegistry)
224-
.unregisterApplication(this.componentRef.location.nativeElement);
224+
testabilityRegistry.unregisterApplication(this.componentRef.location.nativeElement);
225225
destroyComponentRef();
226226
}
227227
});

0 commit comments

Comments
 (0)