6
6
* found in the LICENSE file at https://angular.io/license
7
7
*/
8
8
9
- import { APP_BASE_HREF , HashLocationStrategy , Location , LocationStrategy , PathLocationStrategy , PlatformLocation } from '@angular/common' ;
10
- import { ANALYZE_FOR_ENTRY_COMPONENTS , APP_BOOTSTRAP_LISTENER , ApplicationRef , Compiler , ComponentRef , Inject , Injector , ModuleWithProviders , NgModule , NgModuleFactoryLoader , NgProbeToken , OpaqueToken , Optional , Provider , SkipSelf , SystemJsNgModuleLoader } from '@angular/core' ;
9
+ import { APP_BASE_HREF , HashLocationStrategy , LOCATION_INITIALIZED , Location , LocationStrategy , PathLocationStrategy , PlatformLocation } from '@angular/common' ;
10
+ import { ANALYZE_FOR_ENTRY_COMPONENTS , APP_BOOTSTRAP_LISTENER , APP_INITIALIZER , ApplicationRef , Compiler , ComponentRef , Inject , Injectable , Injector , ModuleWithProviders , NgModule , NgModuleFactoryLoader , NgProbeToken , OpaqueToken , Optional , Provider , SkipSelf , SystemJsNgModuleLoader } from '@angular/core' ;
11
+ import { Subject } from 'rxjs/Subject' ;
12
+ import { of } from 'rxjs/observable/of' ;
11
13
12
14
import { Route , Routes } from './config' ;
13
15
import { RouterLink , RouterLinkWithHref } from './directives/router_link' ;
@@ -19,7 +21,7 @@ import {ErrorHandler, Router} from './router';
19
21
import { ROUTES } from './router_config_loader' ;
20
22
import { RouterOutletMap } from './router_outlet_map' ;
21
23
import { NoPreloading , PreloadAllModules , PreloadingStrategy , RouterPreloader } from './router_preloader' ;
22
- import { ActivatedRoute } from './router_state' ;
24
+ import { ActivatedRoute , RouterStateSnapshot } from './router_state' ;
23
25
import { UrlHandlingStrategy } from './url_handling_strategy' ;
24
26
import { DefaultUrlSerializer , UrlSerializer } from './url_tree' ;
25
27
import { flatten } from './utils/collection' ;
@@ -110,7 +112,7 @@ export function routerNgProbeToken() {
110
112
* In addition, we often want to split applications into multiple bundles and load them on demand.
111
113
* Doing this transparently is not trivial.
112
114
*
113
- * The Angular 2 router solves these problems. Using the router, you can declaratively specify
115
+ * The Angular router solves these problems. Using the router, you can declaratively specify
114
116
* application states, manage state transitions while taking care of the URL, and load bundles on
115
117
* demand.
116
118
*
@@ -278,22 +280,77 @@ export function rootRoute(router: Router): ActivatedRoute {
278
280
return router . routerState . root ;
279
281
}
280
282
281
- export function initialRouterNavigation (
282
- router : Router , ref : ApplicationRef , preloader : RouterPreloader , opts : ExtraOptions ) {
283
- return ( bootstrappedComponentRef : ComponentRef < any > ) => {
283
+ /**
284
+ * To initialize the router properly we need to do in two steps:
285
+ *
286
+ * We need to start the navigation in a APP_INITIALIZER to block the bootstrap if
287
+ * a resolver or a guards executes asynchronously. Second, we need to actually run
288
+ * activation in a BOOTSTRAP_LISTENER. We utilize the afterPreactivation
289
+ * hook provided by the router to do that.
290
+ *
291
+ * The router navigation starts, reaches the point when preactivation is done, and then
292
+ * pauses. It waits for the hook to be resolved. We then resolve it only in a bootstrap listener.
293
+ */
294
+ @Injectable ( )
295
+ export class RouterInitializer {
296
+ private initNavigation : boolean ;
297
+ private resultOfPreactivationDone = new Subject < void > ( ) ;
298
+
299
+ constructor ( private injector : Injector ) { }
300
+
301
+ appInitializer ( ) : Promise < any > {
302
+ const p : Promise < any > = this . injector . get ( LOCATION_INITIALIZED , Promise . resolve ( null ) ) ;
303
+ return p . then ( ( ) => {
304
+ let resolve : Function = null ;
305
+ const res = new Promise ( r => resolve = r ) ;
306
+ const router = this . injector . get ( Router ) ;
307
+ const opts = this . injector . get ( ROUTER_CONFIGURATION ) ;
308
+
309
+ if ( opts . initialNavigation === false ) {
310
+ router . setUpLocationChangeListener ( ) ;
311
+ } else {
312
+ router . hooks . afterPreactivation = ( ) => {
313
+ // only the initial navigation should be delayed
314
+ if ( ! this . initNavigation ) {
315
+ this . initNavigation = true ;
316
+ resolve ( true ) ;
317
+ return this . resultOfPreactivationDone ;
318
+
319
+ // subsequent navigations should not be delayed
320
+ } else {
321
+ return of ( null ) ;
322
+ }
323
+ } ;
324
+ router . initialNavigation ( ) ;
325
+ }
326
+
327
+ return res ;
328
+ } ) ;
329
+ }
284
330
331
+ bootstrapListener ( bootstrappedComponentRef : ComponentRef < any > ) : void {
332
+ const ref = this . injector . get ( ApplicationRef ) ;
285
333
if ( bootstrappedComponentRef !== ref . components [ 0 ] ) {
286
334
return ;
287
335
}
288
336
289
- router . resetRootComponentType ( ref . componentTypes [ 0 ] ) ;
337
+ const preloader = this . injector . get ( RouterPreloader ) ;
290
338
preloader . setUpPreloading ( ) ;
291
- if ( opts . initialNavigation === false ) {
292
- router . setUpLocationChangeListener ( ) ;
293
- } else {
294
- router . initialNavigation ( ) ;
295
- }
296
- } ;
339
+
340
+ const router = this . injector . get ( Router ) ;
341
+ router . resetRootComponentType ( ref . componentTypes [ 0 ] ) ;
342
+
343
+ this . resultOfPreactivationDone . next ( null ) ;
344
+ this . resultOfPreactivationDone . complete ( ) ;
345
+ }
346
+ }
347
+
348
+ export function getAppInitializer ( r : RouterInitializer ) {
349
+ return r . appInitializer . bind ( r ) ;
350
+ }
351
+
352
+ export function getBootstrapListener ( r : RouterInitializer ) {
353
+ return r . bootstrapListener . bind ( r ) ;
297
354
}
298
355
299
356
/**
@@ -305,11 +362,14 @@ export const ROUTER_INITIALIZER = new OpaqueToken('Router Initializer');
305
362
306
363
export function provideRouterInitializer ( ) {
307
364
return [
365
+ RouterInitializer ,
308
366
{
309
- provide : ROUTER_INITIALIZER ,
310
- useFactory : initialRouterNavigation ,
311
- deps : [ Router , ApplicationRef , RouterPreloader , ROUTER_CONFIGURATION ]
367
+ provide : APP_INITIALIZER ,
368
+ multi : true ,
369
+ useFactory : getAppInitializer ,
370
+ deps : [ RouterInitializer ]
312
371
} ,
372
+ { provide : ROUTER_INITIALIZER , useFactory : getBootstrapListener , deps : [ RouterInitializer ] } ,
313
373
{ provide : APP_BOOTSTRAP_LISTENER , multi : true , useExisting : ROUTER_INITIALIZER } ,
314
374
] ;
315
375
}
0 commit comments