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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user