Skip to content

Instantly share code, notes, and snippets.

@nartc
Created November 19, 2019 16:04
Show Gist options
  • Save nartc/e4e15ff511d68ea4cdc0255f7e369162 to your computer and use it in GitHub Desktop.
Save nartc/e4e15ff511d68ea4cdc0255f7e369162 to your computer and use it in GitHub Desktop.
Angular - LazyLoad non-route Module Directive
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);
}
}
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');
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 {}
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);
}
}
}
<ng-container [loadModule]="'firstModule'"></ng-container>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment