1- import { NgModuleRef , Injector , NgModuleFactory , Type , Compiler } from '@angular/core' ;
1+ import { NgModuleRef , Injector , Type , createNgModule , InjectionToken , isStandalone } from '@angular/core' ;
22import {
33 Transition ,
44 LazyLoadResult ,
55 UIRouter ,
66 Resolvable ,
77 NATIVE_INJECTOR_TOKEN ,
8- isString ,
98 unnestR ,
109 inArray ,
1110 StateObject ,
@@ -15,6 +14,7 @@ import {
1514import { UIROUTER_MODULE_TOKEN , UIROUTER_ROOT_MODULE } from '../injectionTokens' ;
1615import { RootModule , StatesModule } from '../uiRouterNgModule' ;
1716import { applyModuleConfig } from '../uiRouterConfig' ;
17+ import { Ng2StateDeclaration } from '../interface' ;
1818
1919/**
2020 * A function that returns an NgModule, or a promise for an NgModule
@@ -26,7 +26,7 @@ import { applyModuleConfig } from '../uiRouterConfig';
2626 * }
2727 * ```
2828 */
29- export type ModuleTypeCallback = ( ) => Type < any > | Promise < Type < any > > ;
29+ export type ModuleTypeCallback < T = unknown > = ( ) => Type < T > | Promise < Type < T > > ;
3030
3131/**
3232 * Returns a function which lazy loads a nested module
@@ -36,29 +36,16 @@ export type ModuleTypeCallback = () => Type<any> | Promise<Type<any>>;
3636 * It could also be used manually as a [[StateDeclaration.lazyLoad]] property to lazy load an `NgModule` and its state(s).
3737 *
3838 * #### Example:
39- * Using `import()` and named export of `HomeModule`
40- * ```js
41- * declare var System;
39+ * ```ts
4240 * var futureState = {
4341 * name: 'home.**',
4442 * url: '/home',
4543 * lazyLoad: loadNgModule(() => import('./home/home.module').then(result => result.HomeModule))
4644 * }
4745 * ```
4846 *
49- * #### Example:
50- * Using a path (string) to the module
51- * ```js
52- * var futureState = {
53- * name: 'home.**',
54- * url: '/home',
55- * lazyLoad: loadNgModule('./home/home.module#HomeModule')
56- * }
57- * ```
58- *
5947 *
60- * @param moduleToLoad a path (string) to the NgModule to load.
61- * Or a function which loads the NgModule code which should
48+ * @param moduleToLoad function which loads the NgModule code which should
6249 * return a reference to the `NgModule` class being loaded (or a `Promise` for it).
6350 *
6451 * @returns A function which takes a transition, which:
@@ -67,17 +54,15 @@ export type ModuleTypeCallback = () => Type<any> | Promise<Type<any>>;
6754 * - Finds the "replacement state" for the target state, and adds the new NgModule Injector to it (as a resolve)
6855 * - Returns the new states array
6956 */
70- export function loadNgModule (
71- moduleToLoad : ModuleTypeCallback
57+ export function loadNgModule < T > (
58+ moduleToLoad : ModuleTypeCallback < T >
7259) : ( transition : Transition , stateObject : StateDeclaration ) => Promise < LazyLoadResult > {
7360 return ( transition : Transition , stateObject : StateDeclaration ) => {
74- const ng2Injector = transition . injector ( ) . get ( NATIVE_INJECTOR_TOKEN ) ;
75-
76- const createModule = ( factory : NgModuleFactory < any > ) => factory . create ( ng2Injector ) ;
7761
78- const applyModule = ( moduleRef : NgModuleRef < any > ) => applyNgModule ( transition , moduleRef , ng2Injector , stateObject ) ;
62+ const ng2Injector = transition . injector ( ) . get ( NATIVE_INJECTOR_TOKEN ) ;
7963
80- return loadModuleFactory ( moduleToLoad , ng2Injector ) . then ( createModule ) . then ( applyModule ) ;
64+ return loadModuleFactory ( moduleToLoad , ng2Injector )
65+ . then ( moduleRef => applyNgModule ( moduleRef , ng2Injector , stateObject ) ) ;
8166 } ;
8267}
8368
@@ -90,22 +75,18 @@ export function loadNgModule(
9075 *
9176 * @internal
9277 */
93- export function loadModuleFactory (
94- moduleToLoad : ModuleTypeCallback ,
78+ export function loadModuleFactory < T > (
79+ moduleToLoad : ModuleTypeCallback < T > ,
9580 ng2Injector : Injector
96- ) : Promise < NgModuleFactory < any > > {
97- const compiler : Compiler = ng2Injector . get ( Compiler ) ;
98-
99- const unwrapEsModuleDefault = ( x ) => ( x && x . __esModule && x [ 'default' ] ? x [ 'default' ] : x ) ;
81+ ) : Promise < NgModuleRef < T > > {
10082
10183 return Promise . resolve ( moduleToLoad ( ) )
102- . then ( unwrapEsModuleDefault )
103- . then ( ( t : NgModuleFactory < any > | Type < any > ) => {
104- if ( t instanceof NgModuleFactory ) {
105- return t ;
106- }
107- return compiler . compileModuleAsync ( t ) ;
108- } ) ;
84+ . then ( _unwrapEsModuleDefault )
85+ . then ( ( t : Type < T > ) => createNgModule ( t , ng2Injector ) ) ;
86+ }
87+
88+ function _unwrapEsModuleDefault ( x ) {
89+ return x && x . __esModule && x [ 'default' ] ? x [ 'default' ] : x ;
10990}
11091
11192/**
@@ -122,9 +103,8 @@ export function loadModuleFactory(
122103 *
123104 * @internal
124105 */
125- export function applyNgModule (
126- transition : Transition ,
127- ng2Module : NgModuleRef < any > ,
106+ export function applyNgModule < T > (
107+ ng2Module : NgModuleRef < T > ,
128108 parentInjector : Injector ,
129109 lazyLoadState : StateDeclaration
130110) : LazyLoadResult {
@@ -192,8 +172,78 @@ export function applyNgModule(
192172 *
193173 * @internal
194174 */
195- export function multiProviderParentChildDelta ( parent : Injector , child : Injector , token : any ) {
196- const childVals : RootModule [ ] = child . get ( token , [ ] ) ;
197- const parentVals : RootModule [ ] = parent . get ( token , [ ] ) ;
175+ export function multiProviderParentChildDelta < T > ( parent : Injector , child : Injector , token : InjectionToken < T > ) : RootModule [ ] {
176+ const childVals : RootModule [ ] = child . get < RootModule [ ] > ( token , [ ] ) ;
177+ const parentVals : RootModule [ ] = parent . get < RootModule [ ] > ( token , [ ] ) ;
198178 return childVals . filter ( ( val ) => parentVals . indexOf ( val ) === - 1 ) ;
199179}
180+
181+ /**
182+ * A function that returns a Component, or a promise for a Component
183+ *
184+ * #### Example:
185+ * ```ts
186+ * export function loadFooComponent() {
187+ * return import('../foo/foo.component').then(result => result.FooComponent);
188+ * }
189+ * ```
190+ */
191+ export type ComponentTypeCallback < T > = ModuleTypeCallback < T > ;
192+
193+ /**
194+ * Returns a function which lazy loads a standalone component for the target state
195+ *
196+ * #### Example:
197+ * ```ts
198+ * var futureComponentState = {
199+ * name: 'home',
200+ * url: '/home',
201+ * lazyLoad: loadComponent(() => import('./home.component').then(result => result.HomeComponent))
202+ * }
203+ * ```
204+ *
205+ * @param callback function which loads the Component code which should
206+ * return a reference to the `Component` class being loaded (or a `Promise` for it).
207+ *
208+ * @returns A function which takes a transition, stateObject, and:
209+ * - Loads a standalone component
210+ * - replaces the component configuration of the stateObject.
211+ * - Returns the new states array
212+ */
213+ export function loadComponent < T > (
214+ callback : ComponentTypeCallback < T >
215+ ) : ( transition : Transition , stateObject : Ng2StateDeclaration ) => Promise < LazyLoadResult > {
216+ return ( transition : Transition , stateObject : Ng2StateDeclaration ) => {
217+
218+ return Promise . resolve ( callback ( ) )
219+ . then ( _unwrapEsModuleDefault )
220+ . then ( ( component : Type < T > ) => applyComponent ( component , transition , stateObject ) )
221+ }
222+ }
223+
224+ /**
225+ * Apply the lazy-loaded component to the stateObject.
226+ *
227+ * @internal
228+ * @param component reference to the component class
229+ * @param transition Transition object reference
230+ * @param stateObject target state configuration object
231+ *
232+ * @returns the new states array
233+ */
234+ export function applyComponent < T > (
235+ component : Type < T > ,
236+ transition : Transition ,
237+ stateObject : Ng2StateDeclaration
238+ ) : LazyLoadResult {
239+
240+ if ( ! isStandalone ( component ) ) throw new Error ( "Is not a standalone component." ) ;
241+
242+ const registry = transition . router . stateRegistry ;
243+ const current = stateObject . component ;
244+ stateObject . component = component || current ;
245+ const removed = registry . deregister ( stateObject ) . map ( child => child . self ) ;
246+ const children = removed . filter ( i => i . name != stateObject . name ) ;
247+
248+ return { states : [ stateObject , ...children ] }
249+ }
0 commit comments