99import { HashLocationStrategy , Location , LOCATION_INITIALIZED , LocationStrategy , PathLocationStrategy , ViewportScroller } from '@angular/common' ;
1010import { ANALYZE_FOR_ENTRY_COMPONENTS , APP_BOOTSTRAP_LISTENER , APP_INITIALIZER , ApplicationRef , Compiler , ComponentRef , ENVIRONMENT_INITIALIZER , Inject , inject , InjectFlags , InjectionToken , Injector , ModuleWithProviders , NgModule , NgProbeToken , Optional , Provider , SkipSelf , Type } from '@angular/core' ;
1111import { of , Subject } from 'rxjs' ;
12+ import { filter , map , take } from 'rxjs/operators' ;
1213
1314import { EmptyOutletComponent } from './components/empty_outlet' ;
1415import { RouterLink , RouterLinkWithHref } from './directives/router_link' ;
1516import { RouterLinkActive } from './directives/router_link_active' ;
1617import { RouterOutlet } from './directives/router_outlet' ;
17- import { Event , stringifyEvent } from './events' ;
18+ import { Event , NavigationCancel , NavigationEnd , NavigationError , stringifyEvent } from './events' ;
1819import { Route , Routes } from './models' ;
1920import { DefaultTitleStrategy , TitleStrategy } from './page_title_strategy' ;
2021import { RouteReuseStrategy } from './route_reuse_strategy' ;
@@ -24,6 +25,7 @@ import {ChildrenOutletContexts} from './router_outlet_context';
2425import { PreloadingStrategy , RouterPreloader } from './router_preloader' ;
2526import { ROUTER_SCROLLER , RouterScroller } from './router_scroller' ;
2627import { ActivatedRoute } from './router_state' ;
28+ import { REDIRECTING_CANCELLATION_REASON } from './shared' ;
2729import { UrlHandlingStrategy } from './url_handling_strategy' ;
2830import { DefaultUrlSerializer , UrlSerializer , UrlTree } from './url_tree' ;
2931import { flatten } from './utils/collection' ;
@@ -588,18 +590,63 @@ function provideEnabledBlockingInitialNavigation(): Provider {
588590 injector . get ( LOCATION_INITIALIZED , Promise . resolve ( null ) ) ;
589591 let initNavigation = false ;
590592
593+ /**
594+ * Performs the given action once the router finishes its next/current navigation.
595+ *
596+ * If the navigation is canceled or errors without a redirect, the navigation is considered
597+ * complete. If the `NavigationEnd` event emits, the navigation is also considered complete.
598+ */
599+ function afterNextNavigation ( action : ( ) => void ) {
600+ const router = injector . get ( Router ) ;
601+ router . events
602+ . pipe (
603+ filter (
604+ ( e ) : e is NavigationEnd | NavigationCancel | NavigationError =>
605+ e instanceof NavigationEnd || e instanceof NavigationCancel ||
606+ e instanceof NavigationError ) ,
607+ map ( e => {
608+ if ( e instanceof NavigationEnd ) {
609+ // Navigation assumed to succeed if we get `ActivationStart`
610+ return true ;
611+ }
612+ const newNavigationStarted = router . navigationId !== e . id ;
613+ // TODO(atscott): Do not rely on the string reason to determine if cancelation
614+ // is redirecting
615+ const redirectingWithUrlTree = e instanceof NavigationCancel ?
616+ e . reason . indexOf ( REDIRECTING_CANCELLATION_REASON ) !== - 1 :
617+ false ;
618+ // Navigation failed, but if we already have a new navigation, wait for the
619+ // result of that one instead.
620+ return newNavigationStarted || redirectingWithUrlTree ? null : false ;
621+ } ) ,
622+ filter ( ( result ) : result is boolean => result !== null ) ,
623+ take ( 1 ) ,
624+ )
625+ . subscribe ( ( ) => {
626+ action ( ) ;
627+ } ) ;
628+ }
629+
591630 return ( ) => {
592631 return locationInitialized . then ( ( ) => {
593632 return new Promise ( resolve => {
594633 const router = injector . get ( Router ) ;
595634 const bootstrapDone = injector . get ( BOOTSTRAP_DONE ) ;
635+ afterNextNavigation ( ( ) => {
636+ // Unblock APP_INITIALIZER in case the initial navigation was canceled or errored
637+ // without a redirect.
638+ resolve ( true ) ;
639+ initNavigation = true ;
640+ } ) ;
596641
597642 router . afterPreactivation = ( ) => {
598- // only the initial navigation should be delayed
643+ // Unblock APP_INITIALIZER once we get to `afterPreactivation`. At this point, we
644+ // assume activation will complete successfully (even though this is not
645+ // guaranteed).
646+ resolve ( true ) ;
647+ // only the initial navigation should be delayed until bootstrapping is done.
599648 if ( ! initNavigation ) {
600- initNavigation = true ;
601- resolve ( true ) ;
602- return bootstrapDone ;
649+ return bootstrapDone . closed ? of ( void 0 ) : bootstrapDone ;
603650 // subsequent navigations should not be delayed
604651 } else {
605652 return of ( void 0 ) ;
0 commit comments