feat(core): introduce `NgModuleRef.destroy` and call `ngOnDestroy` on all providers
This commit is contained in:
parent
c161ed415d
commit
ecdaded25f
|
@ -8,6 +8,8 @@
|
||||||
|
|
||||||
import {Injectable} from '@angular/core';
|
import {Injectable} from '@angular/core';
|
||||||
|
|
||||||
|
import {LifecycleHooks} from '../core_private';
|
||||||
|
|
||||||
import {CompileDiDependencyMetadata, CompileIdentifierMap, CompileIdentifierMetadata, CompileNgModuleMetadata, CompileProviderMetadata, CompileTokenMetadata} from './compile_metadata';
|
import {CompileDiDependencyMetadata, CompileIdentifierMap, CompileIdentifierMetadata, CompileNgModuleMetadata, CompileProviderMetadata, CompileTokenMetadata} from './compile_metadata';
|
||||||
import {isBlank, isPresent} from './facade/lang';
|
import {isBlank, isPresent} from './facade/lang';
|
||||||
import {Identifiers, identifierToken} from './identifiers';
|
import {Identifiers, identifierToken} from './identifiers';
|
||||||
|
@ -71,6 +73,7 @@ class _InjectorBuilder {
|
||||||
private _instances = new CompileIdentifierMap<CompileTokenMetadata, o.Expression>();
|
private _instances = new CompileIdentifierMap<CompileTokenMetadata, o.Expression>();
|
||||||
private _fields: o.ClassField[] = [];
|
private _fields: o.ClassField[] = [];
|
||||||
private _createStmts: o.Statement[] = [];
|
private _createStmts: o.Statement[] = [];
|
||||||
|
private _destroyStmts: o.Statement[] = [];
|
||||||
private _getters: o.ClassGetter[] = [];
|
private _getters: o.ClassGetter[] = [];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -85,6 +88,9 @@ class _InjectorBuilder {
|
||||||
var instance = this._createProviderProperty(
|
var instance = this._createProviderProperty(
|
||||||
propName, resolvedProvider, providerValueExpressions, resolvedProvider.multiProvider,
|
propName, resolvedProvider, providerValueExpressions, resolvedProvider.multiProvider,
|
||||||
resolvedProvider.eager);
|
resolvedProvider.eager);
|
||||||
|
if (resolvedProvider.lifecycleHooks.indexOf(LifecycleHooks.OnDestroy) !== -1) {
|
||||||
|
this._destroyStmts.push(instance.callMethod('ngOnDestroy', []).toStmt());
|
||||||
|
}
|
||||||
this._instances.add(resolvedProvider.token, instance);
|
this._instances.add(resolvedProvider.token, instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,7 +114,10 @@ class _InjectorBuilder {
|
||||||
new o.FnParam(InjectMethodVars.notFoundResult.name, o.DYNAMIC_TYPE)
|
new o.FnParam(InjectMethodVars.notFoundResult.name, o.DYNAMIC_TYPE)
|
||||||
],
|
],
|
||||||
getMethodStmts.concat([new o.ReturnStatement(InjectMethodVars.notFoundResult)]),
|
getMethodStmts.concat([new o.ReturnStatement(InjectMethodVars.notFoundResult)]),
|
||||||
o.DYNAMIC_TYPE)
|
o.DYNAMIC_TYPE),
|
||||||
|
new o.ClassMethod(
|
||||||
|
'destroyInternal', [], this._destroyStmts
|
||||||
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
var ctor = new o.ClassMethod(
|
var ctor = new o.ClassMethod(
|
||||||
|
|
|
@ -7,12 +7,14 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Injector, THROW_IF_NOT_FOUND} from '../di/injector';
|
import {Injector, THROW_IF_NOT_FOUND} from '../di/injector';
|
||||||
import {unimplemented} from '../facade/exceptions';
|
import {BaseException, unimplemented} from '../facade/exceptions';
|
||||||
import {ConcreteType} from '../facade/lang';
|
import {ConcreteType, stringify} from '../facade/lang';
|
||||||
|
|
||||||
import {ComponentFactory} from './component_factory';
|
import {ComponentFactory} from './component_factory';
|
||||||
import {CodegenComponentFactoryResolver, ComponentFactoryResolver} from './component_factory_resolver';
|
import {CodegenComponentFactoryResolver, ComponentFactoryResolver} from './component_factory_resolver';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents an instance of an NgModule created via a {@link NgModuleFactory}.
|
* Represents an instance of an NgModule created via a {@link NgModuleFactory}.
|
||||||
*
|
*
|
||||||
|
@ -37,6 +39,16 @@ export abstract class NgModuleRef<T> {
|
||||||
* The NgModule instance.
|
* The NgModule instance.
|
||||||
*/
|
*/
|
||||||
get instance(): T { return unimplemented(); }
|
get instance(): T { return unimplemented(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroys the module instance and all of the data structures associated with it.
|
||||||
|
*/
|
||||||
|
abstract destroy(): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows to register a callback that will be called when the module is destroyed.
|
||||||
|
*/
|
||||||
|
abstract onDestroy(callback: () => void): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -64,6 +76,9 @@ const _UNDEFINED = new Object();
|
||||||
export abstract class NgModuleInjector<T> extends CodegenComponentFactoryResolver implements
|
export abstract class NgModuleInjector<T> extends CodegenComponentFactoryResolver implements
|
||||||
Injector,
|
Injector,
|
||||||
NgModuleRef<T> {
|
NgModuleRef<T> {
|
||||||
|
private _destroyListeners: (() => void)[] = [];
|
||||||
|
private _destroyed: boolean = false;
|
||||||
|
|
||||||
public instance: T;
|
public instance: T;
|
||||||
|
|
||||||
constructor(public parent: Injector, factories: ComponentFactory<any>[]) {
|
constructor(public parent: Injector, factories: ComponentFactory<any>[]) {
|
||||||
|
@ -87,4 +102,18 @@ export abstract class NgModuleInjector<T> extends CodegenComponentFactoryResolve
|
||||||
get injector(): Injector { return this; }
|
get injector(): Injector { return this; }
|
||||||
|
|
||||||
get componentFactoryResolver(): ComponentFactoryResolver { return this; }
|
get componentFactoryResolver(): ComponentFactoryResolver { return this; }
|
||||||
|
|
||||||
|
destroy(): void {
|
||||||
|
if (this._destroyed) {
|
||||||
|
throw new BaseException(
|
||||||
|
`This module is already destroyed (${stringify(this.instance.constructor)})`);
|
||||||
|
}
|
||||||
|
this._destroyed = true;
|
||||||
|
this.destroyInternal();
|
||||||
|
this._destroyListeners.forEach((listener) => listener());
|
||||||
|
}
|
||||||
|
|
||||||
|
onDestroy(callback: () => void): void { this._destroyListeners.push(callback); }
|
||||||
|
|
||||||
|
abstract destroyInternal(): void;
|
||||||
}
|
}
|
||||||
|
|
|
@ -917,6 +917,69 @@ function declareTests({useJit}: {useJit: boolean}) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('lifecycle', () => {
|
||||||
|
it('should instantiate modules eagerly', () => {
|
||||||
|
let created = false;
|
||||||
|
|
||||||
|
@NgModule()
|
||||||
|
class ImportedModule {
|
||||||
|
constructor() { created = true; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@NgModule({imports: [ImportedModule]})
|
||||||
|
class SomeModule {
|
||||||
|
}
|
||||||
|
|
||||||
|
createModule(SomeModule);
|
||||||
|
|
||||||
|
expect(created).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should instantiate providers that are not used by a module lazily', () => {
|
||||||
|
let created = false;
|
||||||
|
|
||||||
|
createInjector([{
|
||||||
|
provide: 'someToken',
|
||||||
|
useFactory: () => {
|
||||||
|
created = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}]);
|
||||||
|
|
||||||
|
expect(created).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support ngOnDestroy on any provider', () => {
|
||||||
|
let destroyed = false;
|
||||||
|
|
||||||
|
class SomeInjectable {
|
||||||
|
ngOnDestroy() { destroyed = true; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@NgModule({providers: [SomeInjectable]})
|
||||||
|
class SomeModule {
|
||||||
|
}
|
||||||
|
|
||||||
|
const moduleRef = createModule(SomeModule);
|
||||||
|
expect(destroyed).toBe(false);
|
||||||
|
moduleRef.destroy();
|
||||||
|
expect(destroyed).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should instantiate providers with lifecycle eagerly', () => {
|
||||||
|
let created = false;
|
||||||
|
|
||||||
|
class SomeInjectable {
|
||||||
|
constructor() { created = true; }
|
||||||
|
ngOnDestroy() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
createInjector([SomeInjectable]);
|
||||||
|
|
||||||
|
expect(created).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('imported and exported modules', () => {
|
describe('imported and exported modules', () => {
|
||||||
it('should add the providers of imported modules', () => {
|
it('should add the providers of imported modules', () => {
|
||||||
@NgModule({providers: [{provide: 'token1', useValue: 'imported'}]})
|
@NgModule({providers: [{provide: 'token1', useValue: 'imported'}]})
|
||||||
|
|
Loading…
Reference in New Issue