import { ComponentFactory , Inject , Injectable , Injector , NgModuleFactory , NgModuleFactoryLoader , Compiler } from '@angular/core' ; import { from , Observable , throwError , of , } from 'rxjs' ; import { mergeMap } from 'rxjs/operators' ; import { DynamicComponentManifest , DYNAMIC_COMPONENT , DYNAMIC_COMPONENT_MANIFESTS , DYNAMIC_MODULE } from './dynamic-component-manifest' ; @ Injectable ( ) export class DynamicComponentLoader { constructor ( @ Inject ( DYNAMIC_COMPONENT_MANIFESTS ) private manifests , private loader , private injector , private compiler ) { } template T param { ( T | NgModuleFactory < T > | Promise < T > | Observable < T > ) } value returns memberof private _wrapIntoObservable < T > ( value ) { if ( value instanceof Observable ) { return value ; } else if ( value instanceof Promise ) { return from ( value ) ; } else { return of ( value ) ; } } template T param { string } componentId param { Injector } injector returns { Observable < ComponentFactory < T > > } memberof getComponentFactory < T > ( componentId , injector ? ) { const manifest = this . manifests . find ( m => m . componentId === componentId ) ; if ( ! manifest ) { return throwError ( `DynamicComponentLoader: Unknown componentId "${ componentId }"` ) ; } const path = manifest . loadChildren ; if ( ! path ) { throw new Error ( `${ componentId } unknown!` ) ; } if ( path instanceof Function ) { return this . _wrapIntoObservable ( path ( ) ) . pipe ( mergeMap ( ( t ) => { let moduleFactory = null ; const offlineMode = this . compiler instanceof Compiler ; moduleFactory = offlineMode ? t : this . compiler . compileModuleSync ( t ) ; return this . loadFactory < T > ( moduleFactory , componentId , injector ) ; } ) ) ; } else { return from ( this . load < T > ( path , componentId , injector ) ) ; } } template T param { string } path param { string } componentId param { Injector } injector returns { Promise < ComponentFactory < T > > } memberof async load < T > ( path , componentId , injector ? ) { const ngModuleFactory = await this . loader . load ( path ) ; return await this . loadFactory < T > ( ngModuleFactory , componentId , injector ) ; } template T param { NgModuleFactory < any > } ngModuleFactory param { string } componentId param { Injector } injector returns { Promise < ComponentFactory < T > > } memberof loadFactory < T > ( ngModuleFactory , componentId , injector ? ) { const moduleRef = ngModuleFactory . create ( injector || this . injector ) ; const dynamicComponentType = moduleRef . injector . get ( DYNAMIC_COMPONENT , null ) ; if ( ! dynamicComponentType ) { const dynamicModule = moduleRef . injector . get ( DYNAMIC_MODULE , null ) ; if ( ! dynamicModule ) { throw new Error ( 'DynamicComponentLoader: Dynamic module for' + ` componentId "${ componentId }" does not contain` + ' DYNAMIC_COMPONENT or DYNAMIC_MODULE as a provider.' ) ; } if ( dynamicModule . componentId !== componentId ) { throw new Error ( 'DynamicComponentLoader: Dynamic module for' + `${ componentId } does not match manifest.` ) ; } const path = dynamicModule . loadChildren ; if ( ! path ) { throw new Error ( `${ componentId } unknown!` ) ; } if ( path instanceof Function ) { return this . _wrapIntoObservable ( path ( ) ) . pipe ( mergeMap ( ( t ) => { let moduleFactory = null ; const offlineMode = this . compiler instanceof Compiler ; moduleFactory = offlineMode ? t : this . compiler . compileModuleSync ( t ) ; return this . loadFactory < T > ( moduleFactory , componentId , injector ) ; } ) ) . toPromise ( ) ; } else { return this . load < T > ( path , componentId , injector ) ; } } return Promise . resolve ( moduleRef . componentFactoryResolver . resolveComponentFactory < T > ( dynamicComponentType ) ) ; } }