Skip to content

Commit a4ab940

Browse files
refactor(loadNgModule): Export ngModule functions
1 parent 9e10ce7 commit a4ab940

File tree

1 file changed

+86
-94
lines changed

1 file changed

+86
-94
lines changed

src/ng2/lazyLoadNgModule.ts

Lines changed: 86 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -1,116 +1,108 @@
11
/** @module ng2 */ /** */
22
import {NgModuleFactoryLoader, NgModuleRef, Injector, NgModuleFactory, Type, Compiler} from "@angular/core";
3-
4-
import {LazyLoadResult} from "ui-router-core";
5-
6-
import {Transition} from "ui-router-core";
3+
import {Transition, LazyLoadResult, UIRouter, Resolvable, NATIVE_INJECTOR_TOKEN, isString} from "ui-router-core";
74
import {RootModule, StatesModule, UIROUTER_ROOT_MODULE, UIROUTER_MODULE_TOKEN} from "./uiRouterNgModule";
85
import {applyModuleConfig} from "./uiRouterConfig";
9-
import {UIRouter} from "ui-router-core";
10-
import {Resolvable} from "ui-router-core";
11-
import {NATIVE_INJECTOR_TOKEN} from "ui-router-core";
126

137
export type ModuleTypeCallback = () => Type<any> | Promise<Type<any>>;
8+
export type NgModuleToLoad = string | ModuleTypeCallback;
149

1510
/**
1611
* Returns a function which lazy loads a nested module
1712
*
18-
* Use this function as a [[StateDeclaration.lazyLoad]] property to lazy load a state tree (an NgModule).
13+
* Use this function as a [[StateDeclaration.lazyLoad]] property to lazy load an NgModule and its state.
1914
*
20-
* @param path the path to the module source code.
21-
* @returns A function which takes a transition, then:
15+
* @param moduleToLoad
16+
* If a string, it should be the path to the NgModule code, which will then be loaded by the `NgModuleFactoryLoader`.
17+
* If a function, the function should load the NgModule code and return a reference to the `NgModule` class being loaded.
2218
*
19+
* @returns A function which takes a transition, which:
2320
* - Gets the Injector (scoped properly for the destination state)
2421
* - Loads and creates the NgModule
2522
* - Finds the "replacement state" for the target state, and adds the new NgModule Injector to it (as a resolve)
23+
* - Returns the new states array
24+
*/
25+
export function loadNgModule(moduleToLoad: NgModuleToLoad): (transition: Transition) => Promise<LazyLoadResult> {
26+
return function(transition: Transition) {
27+
const ng2Injector = transition.injector().get(NATIVE_INJECTOR_TOKEN);
28+
29+
const createModule = (factory: NgModuleFactory<any>) =>
30+
factory.create(ng2Injector);
31+
32+
const applyModule = (moduleRef: NgModuleRef<any>) =>
33+
applyNgModule(transition, moduleRef);
34+
35+
return loadModuleFactory(moduleToLoad, ng2Injector)
36+
.then(createModule)
37+
.then(applyModule);
38+
}
39+
}
40+
41+
/**
42+
* Returns the module factory that can be used to instantiate a module
43+
*
44+
* For strings this:
45+
* - Finds the correct NgModuleFactoryLoader
46+
* - Loads the new NgModuleFactory from the path string (async)
47+
*
48+
* For a Type<any> or Promise<Type<any>> this:
49+
* - Compiles the component type (if not running with AOT)
50+
* - Returns the NgModuleFactory resulting from compilation (or direct loading if using AOT) as a Promise
2651
*
27-
* returns the new states array
2852
*/
29-
export function loadNgModule( moduleToLoad: string | ModuleTypeCallback): (transition: Transition) => Promise<LazyLoadResult> {
30-
/** Get the parent NgModule Injector (from resolves) */
31-
const getNg2Injector = (transition: Transition) =>
32-
transition.injector().getAsync(NATIVE_INJECTOR_TOKEN);
33-
34-
/**
35-
* Returns the module factory that can be used to instantiate a module
36-
*
37-
* For strings this:
38-
* - Finds the correct NgModuleFactoryLoader
39-
* - Loads the new NgModuleFactory from the path string (async)
40-
*
41-
* For a Type<any> or Promise<Type<any>> this:
42-
* - Compiles the component type (if not running with AOT)
43-
* - Returns the NgModuleFactory resulting from compilation (or direct loading if using AOT) as a Promise
44-
*
45-
*/
46-
const loadModuleFactory = (loadChildren: string | ModuleTypeCallback, ng2Injector: Injector): Promise<NgModuleFactory<any>>=>{
47-
if(typeof(loadChildren) === 'string'){
48-
return ng2Injector.get(NgModuleFactoryLoader).load(loadChildren);
49-
}
50-
else{
51-
const compiler: Compiler = ng2Injector.get(Compiler);
52-
const offlineMode = compiler instanceof Compiler;
53-
const loadChildrenPromise = Promise.resolve(loadChildren());
54-
return offlineMode ? loadChildrenPromise : loadChildrenPromise.then( moduleType => compiler.compileModuleAsync(moduleType))
55-
}
53+
export function loadModuleFactory(moduleToLoad: NgModuleToLoad, ng2Injector: Injector): Promise<NgModuleFactory<any>> {
54+
if (isString(moduleToLoad)) {
55+
return ng2Injector.get(NgModuleFactoryLoader).load(moduleToLoad);
5656
}
5757

58-
/**
59-
* Lazy loads the NgModule using the NgModuleFactoryLoader
60-
*
61-
* Use the parent NgModule's Injector to:
62-
* - Find the correct NgModuleFactory
63-
* - Create the new NgModule
64-
*/
65-
const createNg2Module = ( moduleToLoad: string | ModuleTypeCallback , ng2Injector: Injector) =>
66-
loadModuleFactory(moduleToLoad, ng2Injector).then((factory: NgModuleFactory<any>) =>
67-
factory.create(ng2Injector));
68-
69-
70-
/**
71-
* Apply the UI-Router Modules found in the lazy loaded module.
72-
*
73-
* Apply the Lazy Loaded NgModule's newly created Injector to the right state in the state tree.
74-
*
75-
* Lazy loading uses a placeholder state which is removed (and replaced) after the module is loaded.
76-
* The NgModule should include a state with the same name as the placeholder.
77-
*
78-
* Find the *newly loaded state* with the same name as the *placeholder state*.
79-
* The NgModule's Injector (and ComponentFactoryResolver) will be added to that state.
80-
* The Injector/Factory are used when creating Components for the `replacement` state and all its children.
81-
*/
82-
function loadUIRouterModules(transition: Transition, ng2Module: NgModuleRef<any>): LazyLoadResult {
83-
let injector = ng2Module.injector;
84-
let parentInjector = <Injector> ng2Module.injector['parent'];
85-
let uiRouter: UIRouter = injector.get(UIRouter);
86-
87-
let originalName = transition.to().name;
88-
let originalState = uiRouter.stateRegistry.get(originalName);
89-
90-
let rootModules: RootModule[] = injector.get(UIROUTER_ROOT_MODULE);
91-
let parentRootModules: RootModule[] = parentInjector.get(UIROUTER_ROOT_MODULE);
92-
let newRootModules = rootModules.filter(module => parentRootModules.indexOf(module) === -1);
93-
94-
if (newRootModules.length) {
95-
console.log(rootModules);
96-
throw new Error('Lazy loaded modules should not contain a UIRouterModule.forRoot() module');
97-
}
98-
99-
let modules: StatesModule[] = injector.get(UIROUTER_MODULE_TOKEN);
100-
modules.forEach(module => applyModuleConfig(uiRouter, injector, module));
101-
102-
let replacementState = uiRouter.stateRegistry.get(originalName);
103-
if (replacementState === originalState) {
104-
throw new Error(`The module that was loaded from ${moduleToLoad} should have a ui-router state named '${originalName}'`);
105-
}
106-
107-
// Supply the newly loaded states with the Injector from the lazy loaded NgModule
108-
replacementState.$$state().resolvables.push(Resolvable.fromData(NATIVE_INJECTOR_TOKEN, injector));
109-
110-
return {};
58+
const compiler: Compiler = ng2Injector.get(Compiler);
59+
const offlineMode = compiler instanceof Compiler;
60+
const loadChildrenPromise = Promise.resolve(moduleToLoad());
61+
const compileAsync = (moduleType: Type<any>) =>
62+
compiler.compileModuleAsync(moduleType);
63+
64+
return offlineMode ? loadChildrenPromise : loadChildrenPromise.then(compileAsync)
65+
}
66+
67+
/**
68+
* Apply the UI-Router Modules found in the lazy loaded module.
69+
*
70+
* Apply the Lazy Loaded NgModule's newly created Injector to the right state in the state tree.
71+
*
72+
* Lazy loading uses a placeholder state which is removed (and replaced) after the module is loaded.
73+
* The NgModule should include a state with the same name as the placeholder.
74+
*
75+
* Find the *newly loaded state* with the same name as the *placeholder state*.
76+
* The NgModule's Injector (and ComponentFactoryResolver) will be added to that state.
77+
* The Injector/Factory are used when creating Components for the `replacement` state and all its children.
78+
*/
79+
export function applyNgModule(transition: Transition, ng2Module: NgModuleRef<any>): LazyLoadResult {
80+
let injector = ng2Module.injector;
81+
let parentInjector = <Injector> ng2Module.injector['parent'];
82+
let uiRouter: UIRouter = injector.get(UIRouter);
83+
84+
let originalName = transition.to().name;
85+
let originalState = uiRouter.stateRegistry.get(originalName);
86+
87+
let rootModules: RootModule[] = injector.get(UIROUTER_ROOT_MODULE);
88+
let parentRootModules: RootModule[] = parentInjector.get(UIROUTER_ROOT_MODULE);
89+
let newRootModules = rootModules.filter(module => parentRootModules.indexOf(module) === -1);
90+
91+
if (newRootModules.length) {
92+
console.log(rootModules);
93+
throw new Error('Lazy loaded modules should not contain a UIRouterModule.forRoot() module');
94+
}
95+
96+
let modules: StatesModule[] = injector.get(UIROUTER_MODULE_TOKEN);
97+
modules.forEach(module => applyModuleConfig(uiRouter, injector, module));
98+
99+
let replacementState = uiRouter.stateRegistry.get(originalName);
100+
if (replacementState === originalState) {
101+
throw new Error(`The module that was lazy loaded by activating ${originalName} should also have a ui-router state named '${originalName}'`);
111102
}
112103

113-
return (transition: Transition) => getNg2Injector(transition)
114-
.then((injector: Injector) => createNg2Module(moduleToLoad, injector))
115-
.then((moduleRef: NgModuleRef<any>) => loadUIRouterModules(transition, moduleRef))
104+
// Supply the newly loaded states with the Injector from the lazy loaded NgModule
105+
replacementState.$$state().resolvables.push(Resolvable.fromData(NATIVE_INJECTOR_TOKEN, injector));
106+
107+
return {};
116108
}

0 commit comments

Comments
 (0)