1
- import { NgModuleRef , Injector , NgModuleFactory , Type , Compiler } from '@angular/core' ;
1
+ import { NgModuleRef , Injector , Type , createNgModule , InjectionToken , isStandalone } from '@angular/core' ;
2
2
import {
3
3
Transition ,
4
4
LazyLoadResult ,
5
5
UIRouter ,
6
6
Resolvable ,
7
7
NATIVE_INJECTOR_TOKEN ,
8
- isString ,
9
8
unnestR ,
10
9
inArray ,
11
10
StateObject ,
@@ -15,6 +14,7 @@ import {
15
14
import { UIROUTER_MODULE_TOKEN , UIROUTER_ROOT_MODULE } from '../injectionTokens' ;
16
15
import { RootModule , StatesModule } from '../uiRouterNgModule' ;
17
16
import { applyModuleConfig } from '../uiRouterConfig' ;
17
+ import { Ng2StateDeclaration } from '../interface' ;
18
18
19
19
/**
20
20
* A function that returns an NgModule, or a promise for an NgModule
@@ -26,7 +26,7 @@ import { applyModuleConfig } from '../uiRouterConfig';
26
26
* }
27
27
* ```
28
28
*/
29
- export type ModuleTypeCallback = ( ) => Type < any > | Promise < Type < any > > ;
29
+ export type ModuleTypeCallback < T = unknown > = ( ) => Type < T > | Promise < Type < T > > ;
30
30
31
31
/**
32
32
* Returns a function which lazy loads a nested module
@@ -36,29 +36,16 @@ export type ModuleTypeCallback = () => Type<any> | Promise<Type<any>>;
36
36
* It could also be used manually as a [[StateDeclaration.lazyLoad]] property to lazy load an `NgModule` and its state(s).
37
37
*
38
38
* #### Example:
39
- * Using `import()` and named export of `HomeModule`
40
- * ```js
41
- * declare var System;
39
+ * ```ts
42
40
* var futureState = {
43
41
* name: 'home.**',
44
42
* url: '/home',
45
43
* lazyLoad: loadNgModule(() => import('./home/home.module').then(result => result.HomeModule))
46
44
* }
47
45
* ```
48
46
*
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
- *
59
47
*
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
62
49
* return a reference to the `NgModule` class being loaded (or a `Promise` for it).
63
50
*
64
51
* @returns A function which takes a transition, which:
@@ -67,17 +54,15 @@ export type ModuleTypeCallback = () => Type<any> | Promise<Type<any>>;
67
54
* - Finds the "replacement state" for the target state, and adds the new NgModule Injector to it (as a resolve)
68
55
* - Returns the new states array
69
56
*/
70
- export function loadNgModule (
71
- moduleToLoad : ModuleTypeCallback
57
+ export function loadNgModule < T > (
58
+ moduleToLoad : ModuleTypeCallback < T >
72
59
) : ( transition : Transition , stateObject : StateDeclaration ) => Promise < LazyLoadResult > {
73
60
return ( transition : Transition , stateObject : StateDeclaration ) => {
74
- const ng2Injector = transition . injector ( ) . get ( NATIVE_INJECTOR_TOKEN ) ;
75
-
76
- const createModule = ( factory : NgModuleFactory < any > ) => factory . create ( ng2Injector ) ;
77
61
78
- const applyModule = ( moduleRef : NgModuleRef < any > ) => applyNgModule ( transition , moduleRef , ng2Injector , stateObject ) ;
62
+ const ng2Injector = transition . injector ( ) . get ( NATIVE_INJECTOR_TOKEN ) ;
79
63
80
- return loadModuleFactory ( moduleToLoad , ng2Injector ) . then ( createModule ) . then ( applyModule ) ;
64
+ return loadModuleFactory ( moduleToLoad , ng2Injector )
65
+ . then ( moduleRef => applyNgModule ( moduleRef , ng2Injector , stateObject ) ) ;
81
66
} ;
82
67
}
83
68
@@ -90,22 +75,18 @@ export function loadNgModule(
90
75
*
91
76
* @internal
92
77
*/
93
- export function loadModuleFactory (
94
- moduleToLoad : ModuleTypeCallback ,
78
+ export function loadModuleFactory < T > (
79
+ moduleToLoad : ModuleTypeCallback < T > ,
95
80
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 > > {
100
82
101
83
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 ;
109
90
}
110
91
111
92
/**
@@ -122,9 +103,8 @@ export function loadModuleFactory(
122
103
*
123
104
* @internal
124
105
*/
125
- export function applyNgModule (
126
- transition : Transition ,
127
- ng2Module : NgModuleRef < any > ,
106
+ export function applyNgModule < T > (
107
+ ng2Module : NgModuleRef < T > ,
128
108
parentInjector : Injector ,
129
109
lazyLoadState : StateDeclaration
130
110
) : LazyLoadResult {
@@ -192,8 +172,78 @@ export function applyNgModule(
192
172
*
193
173
* @internal
194
174
*/
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 , [ ] ) ;
198
178
return childVals . filter ( ( val ) => parentVals . indexOf ( val ) === - 1 ) ;
199
179
}
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