66 * found in the LICENSE file at https://angular.io/license
77 */
88
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' ;
1113
1214import { Route , Routes } from './config' ;
1315import { RouterLink , RouterLinkWithHref } from './directives/router_link' ;
@@ -19,7 +21,7 @@ import {ErrorHandler, Router} from './router';
1921import { ROUTES } from './router_config_loader' ;
2022import { RouterOutletMap } from './router_outlet_map' ;
2123import { NoPreloading , PreloadAllModules , PreloadingStrategy , RouterPreloader } from './router_preloader' ;
22- import { ActivatedRoute } from './router_state' ;
24+ import { ActivatedRoute , RouterStateSnapshot } from './router_state' ;
2325import { UrlHandlingStrategy } from './url_handling_strategy' ;
2426import { DefaultUrlSerializer , UrlSerializer } from './url_tree' ;
2527import { flatten } from './utils/collection' ;
@@ -208,6 +210,32 @@ export function provideRoutes(routes: Routes): any {
208210 ] ;
209211}
210212
213+ /**
214+ * @whatItDoes Represents an option to configure when the initial navigation is performed.
215+ *
216+ * @description
217+ * * 'enabled' - the initial navigation starts before the root component is created.
218+ * The bootstrap is blocked until the initial navigation is complete.
219+ * * 'disabled' - the initial navigation is not performed. The location listener is set up before
220+ * the root component gets created.
221+ * * 'legacy_enabled'- the initial navigation starts after the root component has been created.
222+ * The bootstrap is not blocked until the initial navigation is complete. @deprecated
223+ * * 'legacy_disabled'- the initial navigation is not performed. The location listener is set up
224+ * after @deprecated
225+ * the root component gets created.
226+ * * `true` - same as 'legacy_enabled'. @deprecated
227+ * * `false` - same as 'legacy_disabled'. @deprecated
228+ *
229+ * The 'enabled' option should be used for applications unless there is a reason to have
230+ * more control over when the router starts its initial navigation due to some complex
231+ * initialization logic. In this case, 'disabled' should be used.
232+ *
233+ * The 'legacy_enabled' and 'legacy_disabled' should not be used for new applications.
234+ *
235+ * @experimental
236+ */
237+ export type InitialNavigation =
238+ boolean | 'enabled' | 'disabled' | 'legacy_enabled' | 'legacy_disabled' ;
211239
212240/**
213241 * @whatItDoes Represents options to configure the router.
@@ -228,7 +256,7 @@ export interface ExtraOptions {
228256 /**
229257 * Disables the initial navigation.
230258 */
231- initialNavigation ?: boolean ;
259+ initialNavigation ?: InitialNavigation ;
232260
233261 /**
234262 * A custom error handler.
@@ -278,22 +306,100 @@ export function rootRoute(router: Router): ActivatedRoute {
278306 return router . routerState . root ;
279307}
280308
281- export function initialRouterNavigation (
282- router : Router , ref : ApplicationRef , preloader : RouterPreloader , opts : ExtraOptions ) {
283- return ( bootstrappedComponentRef : ComponentRef < any > ) => {
309+ /**
310+ * To initialize the router properly we need to do in two steps:
311+ *
312+ * We need to start the navigation in a APP_INITIALIZER to block the bootstrap if
313+ * a resolver or a guards executes asynchronously. Second, we need to actually run
314+ * activation in a BOOTSTRAP_LISTENER. We utilize the afterPreactivation
315+ * hook provided by the router to do that.
316+ *
317+ * The router navigation starts, reaches the point when preactivation is done, and then
318+ * pauses. It waits for the hook to be resolved. We then resolve it only in a bootstrap listener.
319+ */
320+ @Injectable ( )
321+ export class RouterInitializer {
322+ private initNavigation : boolean = false ;
323+ private resultOfPreactivationDone = new Subject < void > ( ) ;
324+
325+ constructor ( private injector : Injector ) { }
326+
327+ appInitializer ( ) : Promise < any > {
328+ const p : Promise < any > = this . injector . get ( LOCATION_INITIALIZED , Promise . resolve ( null ) ) ;
329+ return p . then ( ( ) => {
330+ let resolve : Function = null ;
331+ const res = new Promise ( r => resolve = r ) ;
332+ const router = this . injector . get ( Router ) ;
333+ const opts = this . injector . get ( ROUTER_CONFIGURATION ) ;
334+
335+ if ( this . isLegacyDisabled ( opts ) || this . isLegacyEnabled ( opts ) ) {
336+ resolve ( true ) ;
337+
338+ } else if ( opts . initialNavigation === 'disabled' ) {
339+ router . setUpLocationChangeListener ( ) ;
340+ resolve ( true ) ;
341+
342+ } else if ( opts . initialNavigation === 'enabled' ) {
343+ router . hooks . afterPreactivation = ( ) => {
344+ // only the initial navigation should be delayed
345+ if ( ! this . initNavigation ) {
346+ this . initNavigation = true ;
347+ resolve ( true ) ;
348+ return this . resultOfPreactivationDone ;
349+
350+ // subsequent navigations should not be delayed
351+ } else {
352+ return of ( null ) ;
353+ }
354+ } ;
355+ router . initialNavigation ( ) ;
356+
357+ } else {
358+ throw new Error ( `Invalid initialNavigation options: '${ opts . initialNavigation } '` ) ;
359+ }
360+
361+ return res ;
362+ } ) ;
363+ }
364+
365+ bootstrapListener ( bootstrappedComponentRef : ComponentRef < any > ) : void {
366+ const opts = this . injector . get ( ROUTER_CONFIGURATION ) ;
367+ const preloader = this . injector . get ( RouterPreloader ) ;
368+ const router = this . injector . get ( Router ) ;
369+ const ref = this . injector . get ( ApplicationRef ) ;
284370
285371 if ( bootstrappedComponentRef !== ref . components [ 0 ] ) {
286372 return ;
287373 }
288374
289- router . resetRootComponentType ( ref . componentTypes [ 0 ] ) ;
290- preloader . setUpPreloading ( ) ;
291- if ( opts . initialNavigation === false ) {
292- router . setUpLocationChangeListener ( ) ;
293- } else {
375+ if ( this . isLegacyEnabled ( opts ) ) {
294376 router . initialNavigation ( ) ;
377+ } else if ( this . isLegacyDisabled ( opts ) ) {
378+ router . setUpLocationChangeListener ( ) ;
295379 }
296- } ;
380+
381+ preloader . setUpPreloading ( ) ;
382+ router . resetRootComponentType ( ref . componentTypes [ 0 ] ) ;
383+ this . resultOfPreactivationDone . next ( null ) ;
384+ this . resultOfPreactivationDone . complete ( ) ;
385+ }
386+
387+ private isLegacyEnabled ( opts : ExtraOptions ) : boolean {
388+ return opts . initialNavigation === 'legacy_enabled' || opts . initialNavigation === true ||
389+ opts . initialNavigation === undefined ;
390+ }
391+
392+ private isLegacyDisabled ( opts : ExtraOptions ) : boolean {
393+ return opts . initialNavigation === 'legacy_disabled' || opts . initialNavigation === false ;
394+ }
395+ }
396+
397+ export function getAppInitializer ( r : RouterInitializer ) {
398+ return r . appInitializer . bind ( r ) ;
399+ }
400+
401+ export function getBootstrapListener ( r : RouterInitializer ) {
402+ return r . bootstrapListener . bind ( r ) ;
297403}
298404
299405/**
@@ -305,11 +411,14 @@ export const ROUTER_INITIALIZER = new OpaqueToken('Router Initializer');
305411
306412export function provideRouterInitializer ( ) {
307413 return [
414+ RouterInitializer ,
308415 {
309- provide : ROUTER_INITIALIZER ,
310- useFactory : initialRouterNavigation ,
311- deps : [ Router , ApplicationRef , RouterPreloader , ROUTER_CONFIGURATION ]
416+ provide : APP_INITIALIZER ,
417+ multi : true ,
418+ useFactory : getAppInitializer ,
419+ deps : [ RouterInitializer ]
312420 } ,
421+ { provide : ROUTER_INITIALIZER , useFactory : getBootstrapListener , deps : [ RouterInitializer ] } ,
313422 { provide : APP_BOOTSTRAP_LISTENER , multi : true , useExisting : ROUTER_INITIALIZER } ,
314423 ] ;
315424}
0 commit comments