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 , ɵRuntimeError as RuntimeError } 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' ;
1718import { RuntimeErrorCode } from './errors' ;
18- import { Event , stringifyEvent } from './events' ;
19+ import { Event , NavigationCancel , NavigationEnd , NavigationError , stringifyEvent } from './events' ;
1920import { Route , Routes } from './models' ;
2021import { DefaultTitleStrategy , TitleStrategy } from './page_title_strategy' ;
2122import { RouteReuseStrategy } from './route_reuse_strategy' ;
@@ -25,6 +26,7 @@ import {ChildrenOutletContexts} from './router_outlet_context';
2526import { PreloadingStrategy , RouterPreloader } from './router_preloader' ;
2627import { ROUTER_SCROLLER , RouterScroller } from './router_scroller' ;
2728import { ActivatedRoute } from './router_state' ;
29+ import { REDIRECTING_CANCELLATION_REASON } from './shared' ;
2830import { UrlHandlingStrategy } from './url_handling_strategy' ;
2931import { DefaultUrlSerializer , UrlSerializer , UrlTree } from './url_tree' ;
3032import { flatten } from './utils/collection' ;
@@ -590,18 +592,63 @@ function provideEnabledBlockingInitialNavigation(): Provider {
590592 injector . get ( LOCATION_INITIALIZED , Promise . resolve ( null ) ) ;
591593 let initNavigation = false ;
592594
595+ /**
596+ * Performs the given action once the router finishes its next/current navigation.
597+ *
598+ * If the navigation is canceled or errors without a redirect, the navigation is considered
599+ * complete. If the `NavigationEnd` event emits, the navigation is also considered complete.
600+ */
601+ function afterNextNavigation ( action : ( ) => void ) {
602+ const router = injector . get ( Router ) ;
603+ router . events
604+ . pipe (
605+ filter (
606+ ( e ) : e is NavigationEnd | NavigationCancel | NavigationError =>
607+ e instanceof NavigationEnd || e instanceof NavigationCancel ||
608+ e instanceof NavigationError ) ,
609+ map ( e => {
610+ if ( e instanceof NavigationEnd ) {
611+ // Navigation assumed to succeed if we get `ActivationStart`
612+ return true ;
613+ }
614+ const newNavigationStarted = router . navigationId !== e . id ;
615+ // TODO(atscott): Do not rely on the string reason to determine if cancelation
616+ // is redirecting
617+ const redirectingWithUrlTree = e instanceof NavigationCancel ?
618+ e . reason . indexOf ( REDIRECTING_CANCELLATION_REASON ) !== - 1 :
619+ false ;
620+ // Navigation failed, but if we already have a new navigation, wait for the
621+ // result of that one instead.
622+ return newNavigationStarted || redirectingWithUrlTree ? null : false ;
623+ } ) ,
624+ filter ( ( result ) : result is boolean => result !== null ) ,
625+ take ( 1 ) ,
626+ )
627+ . subscribe ( ( ) => {
628+ action ( ) ;
629+ } ) ;
630+ }
631+
593632 return ( ) => {
594633 return locationInitialized . then ( ( ) => {
595634 return new Promise ( resolve => {
596635 const router = injector . get ( Router ) ;
597636 const bootstrapDone = injector . get ( BOOTSTRAP_DONE ) ;
637+ afterNextNavigation ( ( ) => {
638+ // Unblock APP_INITIALIZER in case the initial navigation was canceled or errored
639+ // without a redirect.
640+ resolve ( true ) ;
641+ initNavigation = true ;
642+ } ) ;
598643
599644 router . afterPreactivation = ( ) => {
600- // only the initial navigation should be delayed
645+ // Unblock APP_INITIALIZER once we get to `afterPreactivation`. At this point, we
646+ // assume activation will complete successfully (even though this is not
647+ // guaranteed).
648+ resolve ( true ) ;
649+ // only the initial navigation should be delayed until bootstrapping is done.
601650 if ( ! initNavigation ) {
602- initNavigation = true ;
603- resolve ( true ) ;
604- return bootstrapDone ;
651+ return bootstrapDone . closed ? of ( void 0 ) : bootstrapDone ;
605652 // subsequent navigations should not be delayed
606653 } else {
607654 return of ( void 0 ) ;
0 commit comments