Created
November 19, 2019 16:04
-
-
Save nartc/e4e15ff511d68ea4cdc0255f7e369162 to your computer and use it in GitHub Desktop.
Angular - LazyLoad non-route Module Directive
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { | |
Compiler, | |
ComponentRef, | |
Directive, | |
Inject, | |
Injector, | |
Input, | |
NgModuleFactory, | |
OnInit, | |
Type, | |
ViewContainerRef, | |
} from '@angular/core'; | |
import { LoadChildrenCallback } from '../../../../utilities/types'; | |
import { LAZY_MODULES_MAP, LazyNonRoutableModules } from './load-module-map'; | |
import { LoadModuleService } from './load-module.service'; | |
type ModuleWithRoot = Type<any> & { rootComponent: Type<any> }; | |
@Directive({ | |
selector: '[loadModule]', | |
}) | |
export class LoadModuleDirective implements OnInit { | |
@Input('loadModule') moduleName: keyof LazyNonRoutableModules; | |
private componentRef: ComponentRef<any>; | |
constructor( | |
private _vcr: ViewContainerRef, | |
private _injector: Injector, | |
private _compiler: Compiler, | |
private _loadModuleService: LoadModuleService, | |
@Inject(LAZY_MODULES_MAP) private modulesMap: LazyNonRoutableModules, | |
) {} | |
async ngOnInit() { | |
let ref: any = this._loadModuleService.moduleRefs[this.moduleName]; | |
let refPromise: Promise<any>; | |
if (ref) { | |
this.componentRef && this.componentRef.destroy(); | |
refPromise = Promise.resolve(ref); | |
} else { | |
const moduleFactory = await loadModuleFactory( | |
this.modulesMap[this.moduleName] as LoadChildrenCallback, | |
this._compiler, | |
); | |
const moduleRef = moduleFactory.create(this._injector); | |
ref = { | |
moduleFactory, | |
moduleRef, | |
}; | |
this._loadModuleService.updateModuleRefs(prev => ({ | |
...prev, | |
[this.moduleName]: ref, | |
})); | |
refPromise = Promise.resolve(ref); | |
} | |
const { moduleFactory: _factory, moduleRef: _ref } = await refPromise; | |
const rootComponent = (_factory.moduleType as ModuleWithRoot).rootComponent; | |
const factory = _ref.componentFactoryResolver.resolveComponentFactory(rootComponent); | |
this.componentRef = this._vcr.createComponent(factory); | |
this.componentRef.changeDetectorRef.detectChanges(); | |
} | |
} | |
async function loadModuleFactory(loadChildren: () => Promise<any>, compiler: Compiler): Promise<NgModuleFactory<any>> { | |
const m = await loadChildren(); | |
if (m instanceof NgModuleFactory) { | |
return m; | |
} else { | |
return compiler.compileModuleAsync(m); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { InjectionToken } from '@angular/core'; | |
import { LoadChildrenCallback } from '../../../../utilities/types'; | |
export interface LazyNonRoutableModules { | |
fistModule: LoadChildrenCallback<FirstModule>; | |
secondModule: LoadChildrenCallback<SecondModule>; | |
} | |
const lazyModulesMap: LazyNonRoutableModules = { | |
firstModule: () => | |
import('app/first/first.module').then( | |
m => m.FirstModule, | |
), | |
secondModule: () => | |
import('app/second/second.module').then( | |
m => m.SecondModule, | |
) | |
}; | |
export const lazyModulesMapFactory = () => lazyModulesMap; | |
export const LAZY_MODULES_MAP = new InjectionToken('LAZY_MODULES_MAP'); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { NgModule } from '@angular/core'; | |
import { LAZY_MODULES_MAP, lazyModulesMapFactory } from './load-module-map'; | |
import { LoadModuleDirective } from './load-module.directive'; | |
@NgModule({ | |
declarations: [LoadModuleDirective], | |
exports: [LoadModuleDirective], | |
providers: [ | |
{ | |
provide: LAZY_MODULES_MAP, | |
useFactory: lazyModulesMapFactory, | |
}, | |
], | |
}) | |
export class LoadModuleModule {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Injectable, NgModuleFactory, NgModuleRef } from '@angular/core'; | |
import { SetStateArgs } from '../../../../utilities/types'; | |
import { StateSubject } from '../../extended/state-subject'; | |
import { LazyNonRoutableModules } from './load-module-map'; | |
type LazyModuleRefsMap = { | |
[key in keyof LazyNonRoutableModules]?: { | |
moduleRef: NgModuleRef<any>; | |
moduleFactory: NgModuleFactory<any>; | |
}; | |
}; | |
@Injectable({ providedIn: 'root' }) | |
export class LoadModuleService { | |
private _moduleRefsSub: StateSubject<LazyModuleRefsMap> = new StateSubject<LazyModuleRefsMap>({}); | |
get moduleRefs(): LazyModuleRefsMap { | |
return this._moduleRefsSub.value; | |
} | |
updateModuleRefs(arg: SetStateArgs<LazyModuleRefsMap>) { | |
this._moduleRefsSub.setState(arg); | |
} | |
destroy(moduleName: keyof LazyNonRoutableModules): void { | |
this.moduleRefs[moduleName] && | |
this.moduleRefs[moduleName].moduleRef && | |
this.moduleRefs[moduleName].moduleRef.destroy(); | |
this.updateModuleRefs(prev => ({ ...prev, [moduleName]: null })); | |
} | |
destroyBulk(...moduleNames: Array<keyof LazyNonRoutableModules>): void { | |
for (let i = 0; i < moduleNames.length; i++) { | |
const moduleName = moduleNames[i]; | |
this.destroy(moduleName); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<ng-container [loadModule]="'firstModule'"></ng-container> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment