Revert "fix(router): do not finish bootstrap until all the routes are resolved (#14608)"
This reverts commit 2a191cae2dff9b2616d4e7a4d47ae2ffb2cdf15d.
This commit is contained in:
		
							parent
							
								
									2a191cae2d
								
							
						
					
					
						commit
						670f2eca00
					
				| @ -6,7 +6,6 @@ | |||||||
|  * found in the LICENSE file at https://angular.io/license
 |  * found in the LICENSE file at https://angular.io/license
 | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| import {InjectionToken} from '@angular/core'; |  | ||||||
| /** | /** | ||||||
|  * This class should not be used directly by an application developer. Instead, use |  * This class should not be used directly by an application developer. Instead, use | ||||||
|  * {@link Location}. |  * {@link Location}. | ||||||
| @ -51,12 +50,6 @@ export abstract class PlatformLocation { | |||||||
|   abstract back(): void; |   abstract back(): void; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** |  | ||||||
|  * @whatItDoes indicates when a location is initialized |  | ||||||
|  * @experimental |  | ||||||
|  */ |  | ||||||
| export const LOCATION_INITIALIZED = new InjectionToken<Promise<any>>('Location Initialized'); |  | ||||||
| 
 |  | ||||||
| /** | /** | ||||||
|  * A serializable version of the event from onPopState or onHashChange |  * A serializable version of the event from onPopState or onHashChange | ||||||
|  * |  * | ||||||
|  | |||||||
| @ -6,7 +6,7 @@ | |||||||
|  * found in the LICENSE file at https://angular.io/license
 |  * found in the LICENSE file at https://angular.io/license
 | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| import {LOCATION_INITIALIZED, PlatformLocation} from '@angular/common'; | import {PlatformLocation} from '@angular/common'; | ||||||
| import {APP_INITIALIZER, InjectionToken, NgZone} from '@angular/core'; | import {APP_INITIALIZER, InjectionToken, NgZone} from '@angular/core'; | ||||||
| 
 | 
 | ||||||
| import {WebWorkerPlatformLocation} from './platform_location'; | import {WebWorkerPlatformLocation} from './platform_location'; | ||||||
| @ -25,18 +25,9 @@ export const WORKER_APP_LOCATION_PROVIDERS = [ | |||||||
|     multi: true, |     multi: true, | ||||||
|     deps: [PlatformLocation, NgZone], |     deps: [PlatformLocation, NgZone], | ||||||
|   }, |   }, | ||||||
|   { |  | ||||||
|     provide: LOCATION_INITIALIZED, |  | ||||||
|     useFactory: locationInitialized, |  | ||||||
|     deps: [PlatformLocation], |  | ||||||
|   }, |  | ||||||
| ]; | ]; | ||||||
| 
 | 
 | ||||||
| export function locationInitialized(platformLocation: WebWorkerPlatformLocation) { | function appInitFnFactory(platformLocation: WebWorkerPlatformLocation, zone: NgZone): () => | ||||||
|   return platformLocation.initialized; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| export function appInitFnFactory(platformLocation: WebWorkerPlatformLocation, zone: NgZone): () => |  | ||||||
|     Promise<boolean> { |     Promise<boolean> { | ||||||
|   return () => zone.runGuarded(() => platformLocation.init()); |   return () => zone.runGuarded(() => platformLocation.init()); | ||||||
| } | } | ||||||
|  | |||||||
| @ -22,8 +22,6 @@ export class WebWorkerPlatformLocation extends PlatformLocation { | |||||||
|   private _hashChangeListeners: Array<Function> = []; |   private _hashChangeListeners: Array<Function> = []; | ||||||
|   private _location: LocationType = null; |   private _location: LocationType = null; | ||||||
|   private _channelSource: EventEmitter<Object>; |   private _channelSource: EventEmitter<Object>; | ||||||
|   public initialized: Promise<any>; |  | ||||||
|   private initializedResolve: () => void; |  | ||||||
| 
 | 
 | ||||||
|   constructor( |   constructor( | ||||||
|       brokerFactory: ClientMessageBrokerFactory, bus: MessageBus, private _serializer: Serializer) { |       brokerFactory: ClientMessageBrokerFactory, bus: MessageBus, private _serializer: Serializer) { | ||||||
| @ -50,7 +48,6 @@ export class WebWorkerPlatformLocation extends PlatformLocation { | |||||||
|         } |         } | ||||||
|       } |       } | ||||||
|     }); |     }); | ||||||
|     this.initialized = new Promise(res => this.initializedResolve = res); |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   /** @internal **/ |   /** @internal **/ | ||||||
| @ -61,7 +58,6 @@ export class WebWorkerPlatformLocation extends PlatformLocation { | |||||||
|         .then( |         .then( | ||||||
|             (val: LocationType) => { |             (val: LocationType) => { | ||||||
|               this._location = val; |               this._location = val; | ||||||
|               this.initializedResolve(); |  | ||||||
|               return true; |               return true; | ||||||
|             }, |             }, | ||||||
|             err => { throw new Error(err); }); |             err => { throw new Error(err); }); | ||||||
|  | |||||||
| @ -180,18 +180,6 @@ type NavigationParams = { | |||||||
|   source: NavigationSource, |   source: NavigationSource, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /** |  | ||||||
|  * @internal |  | ||||||
|  */ |  | ||||||
| export type RouterHook = (snapshot: RouterStateSnapshot) => Observable<void>; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * @internal |  | ||||||
|  */ |  | ||||||
| function defaultRouterHook(snapshot: RouterStateSnapshot): Observable<void> { |  | ||||||
|   return of (null); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /** | /** | ||||||
|  * Does not detach any subtrees. Reuses routes as long as their route config is the same. |  * Does not detach any subtrees. Reuses routes as long as their route config is the same. | ||||||
|  */ |  */ | ||||||
| @ -233,23 +221,11 @@ export class Router { | |||||||
|    */ |    */ | ||||||
|   errorHandler: ErrorHandler = defaultErrorHandler; |   errorHandler: ErrorHandler = defaultErrorHandler; | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|   /** |   /** | ||||||
|    * Indicates if at least one navigation happened. |    * Indicates if at least one navigation happened. | ||||||
|    */ |    */ | ||||||
|   navigated: boolean = false; |   navigated: boolean = false; | ||||||
| 
 | 
 | ||||||
|   /** |  | ||||||
|    * Used by RouterModule. This allows us to |  | ||||||
|    * pause the navigation either before preactivation or after it. |  | ||||||
|    * @internal |  | ||||||
|    */ |  | ||||||
|   hooks: {beforePreactivation: RouterHook, afterPreactivation: RouterHook} = { |  | ||||||
|     beforePreactivation: defaultRouterHook, |  | ||||||
|     afterPreactivation: defaultRouterHook |  | ||||||
|   }; |  | ||||||
| 
 |  | ||||||
|   /** |   /** | ||||||
|    * Extracts and merges URLs. Used for AngularJS to Angular migrations. |    * Extracts and merges URLs. Used for AngularJS to Angular migrations. | ||||||
|    */ |    */ | ||||||
| @ -626,25 +602,18 @@ export class Router { | |||||||
|         urlAndSnapshot$ = of ({appliedUrl: url, snapshot: precreatedState}); |         urlAndSnapshot$ = of ({appliedUrl: url, snapshot: precreatedState}); | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       const beforePreactivationDone$ = mergeMap.call( |  | ||||||
|           urlAndSnapshot$, (p: {appliedUrl: string, snapshot: RouterStateSnapshot}) => { |  | ||||||
|             return map.call(this.hooks.beforePreactivation(p.snapshot), () => p); |  | ||||||
|           }); |  | ||||||
| 
 | 
 | ||||||
|       // run preactivation: guards and data resolvers
 |       // run preactivation: guards and data resolvers
 | ||||||
|       let preActivation: PreActivation; |       let preActivation: PreActivation; | ||||||
|       const preactivationTraverse$ = map.call( |       const preactivationTraverse$ = map.call(urlAndSnapshot$, ({appliedUrl, snapshot}: any) => { | ||||||
|           beforePreactivationDone$, |  | ||||||
|           ({appliedUrl, snapshot}: {appliedUrl: string, snapshot: RouterStateSnapshot}) => { |  | ||||||
|         preActivation = |         preActivation = | ||||||
|             new PreActivation(snapshot, this.currentRouterState.snapshot, this.injector); |             new PreActivation(snapshot, this.currentRouterState.snapshot, this.injector); | ||||||
|         preActivation.traverse(this.outletMap); |         preActivation.traverse(this.outletMap); | ||||||
|         return {appliedUrl, snapshot}; |         return {appliedUrl, snapshot}; | ||||||
|       }); |       }); | ||||||
| 
 | 
 | ||||||
|       const preactivationCheckGuards$ = mergeMap.call( |       const preactivationCheckGuards = | ||||||
|           preactivationTraverse$, |           mergeMap.call(preactivationTraverse$, ({appliedUrl, snapshot}: any) => { | ||||||
|           ({appliedUrl, snapshot}: {appliedUrl: string, snapshot: RouterStateSnapshot}) => { |  | ||||||
|             if (this.navigationId !== id) return of (false); |             if (this.navigationId !== id) return of (false); | ||||||
| 
 | 
 | ||||||
|             return map.call(preActivation.checkGuards(), (shouldActivate: boolean) => { |             return map.call(preActivation.checkGuards(), (shouldActivate: boolean) => { | ||||||
| @ -652,7 +621,7 @@ export class Router { | |||||||
|             }); |             }); | ||||||
|           }); |           }); | ||||||
| 
 | 
 | ||||||
|       const preactivationResolveData$ = mergeMap.call(preactivationCheckGuards$, (p: any) => { |       const preactivationResolveData$ = mergeMap.call(preactivationCheckGuards, (p: any) => { | ||||||
|         if (this.navigationId !== id) return of (false); |         if (this.navigationId !== id) return of (false); | ||||||
| 
 | 
 | ||||||
|         if (p.shouldActivate) { |         if (p.shouldActivate) { | ||||||
| @ -662,15 +631,11 @@ export class Router { | |||||||
|         } |         } | ||||||
|       }); |       }); | ||||||
| 
 | 
 | ||||||
|       const preactivationDone$ = mergeMap.call(preactivationResolveData$, (p: any) => { |  | ||||||
|         return map.call(this.hooks.afterPreactivation(p.snapshot), () => p); |  | ||||||
|       }); |  | ||||||
| 
 |  | ||||||
| 
 | 
 | ||||||
|       // create router state
 |       // create router state
 | ||||||
|       // this operation has side effects => route state is being affected
 |       // this operation has side effects => route state is being affected
 | ||||||
|       const routerState$ = |       const routerState$ = | ||||||
|           map.call(preactivationDone$, ({appliedUrl, snapshot, shouldActivate}: any) => { |           map.call(preactivationResolveData$, ({appliedUrl, snapshot, shouldActivate}: any) => { | ||||||
|             if (shouldActivate) { |             if (shouldActivate) { | ||||||
|               const state = |               const state = | ||||||
|                   createRouterState(this.routeReuseStrategy, snapshot, this.currentRouterState); |                   createRouterState(this.routeReuseStrategy, snapshot, this.currentRouterState); | ||||||
|  | |||||||
| @ -58,13 +58,10 @@ export class RouterConfigLoader { | |||||||
|     if (typeof loadChildren === 'string') { |     if (typeof loadChildren === 'string') { | ||||||
|       return fromPromise(this.loader.load(loadChildren)); |       return fromPromise(this.loader.load(loadChildren)); | ||||||
|     } else { |     } else { | ||||||
|       return mergeMap.call(wrapIntoObservable(loadChildren()), (t: any) => { |       const offlineMode = this.compiler instanceof Compiler; | ||||||
|         if (t instanceof NgModuleFactory) { |       return mergeMap.call( | ||||||
|           return of (t); |           wrapIntoObservable(loadChildren()), | ||||||
|         } else { |           (t: any) => offlineMode ? of (<any>t) : fromPromise(this.compiler.compileModuleAsync(t))); | ||||||
|           return fromPromise(this.compiler.compileModuleAsync(t)); |  | ||||||
|         } |  | ||||||
|       }); |  | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -6,10 +6,8 @@ | |||||||
|  * found in the LICENSE file at https://angular.io/license
 |  * found in the LICENSE file at https://angular.io/license
 | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| import {APP_BASE_HREF, HashLocationStrategy, LOCATION_INITIALIZED, Location, LocationStrategy, PathLocationStrategy, PlatformLocation} from '@angular/common'; | import {APP_BASE_HREF, HashLocationStrategy, Location, LocationStrategy, PathLocationStrategy, PlatformLocation} from '@angular/common'; | ||||||
| import {ANALYZE_FOR_ENTRY_COMPONENTS, APP_BOOTSTRAP_LISTENER, APP_INITIALIZER, ApplicationRef, Compiler, ComponentRef, Inject, Injectable, InjectionToken, Injector, ModuleWithProviders, NgModule, NgModuleFactoryLoader, NgProbeToken, Optional, Provider, SkipSelf, SystemJsNgModuleLoader} from '@angular/core'; | import {ANALYZE_FOR_ENTRY_COMPONENTS, APP_BOOTSTRAP_LISTENER, ApplicationRef, Compiler, ComponentRef, Inject, InjectionToken, Injector, ModuleWithProviders, NgModule, NgModuleFactoryLoader, NgProbeToken, Optional, Provider, SkipSelf, SystemJsNgModuleLoader} from '@angular/core'; | ||||||
| import {Subject} from 'rxjs/Subject'; |  | ||||||
| import {of } from 'rxjs/observable/of'; |  | ||||||
| 
 | 
 | ||||||
| import {Route, Routes} from './config'; | import {Route, Routes} from './config'; | ||||||
| import {RouterLink, RouterLinkWithHref} from './directives/router_link'; | import {RouterLink, RouterLinkWithHref} from './directives/router_link'; | ||||||
| @ -21,7 +19,7 @@ import {ErrorHandler, Router} from './router'; | |||||||
| import {ROUTES} from './router_config_loader'; | import {ROUTES} from './router_config_loader'; | ||||||
| import {RouterOutletMap} from './router_outlet_map'; | import {RouterOutletMap} from './router_outlet_map'; | ||||||
| import {NoPreloading, PreloadAllModules, PreloadingStrategy, RouterPreloader} from './router_preloader'; | import {NoPreloading, PreloadAllModules, PreloadingStrategy, RouterPreloader} from './router_preloader'; | ||||||
| import {ActivatedRoute, RouterStateSnapshot} from './router_state'; | import {ActivatedRoute} from './router_state'; | ||||||
| import {UrlHandlingStrategy} from './url_handling_strategy'; | import {UrlHandlingStrategy} from './url_handling_strategy'; | ||||||
| import {DefaultUrlSerializer, UrlSerializer} from './url_tree'; | import {DefaultUrlSerializer, UrlSerializer} from './url_tree'; | ||||||
| import {flatten} from './utils/collection'; | import {flatten} from './utils/collection'; | ||||||
| @ -280,77 +278,22 @@ export function rootRoute(router: Router): ActivatedRoute { | |||||||
|   return router.routerState.root; |   return router.routerState.root; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** | export function initialRouterNavigation( | ||||||
|  * To initialize the router properly we need to do in two steps: |     router: Router, ref: ApplicationRef, preloader: RouterPreloader, opts: ExtraOptions) { | ||||||
|  * |   return (bootstrappedComponentRef: ComponentRef<any>) => { | ||||||
|  * We need to start the navigation in a APP_INITIALIZER to block the bootstrap if |  | ||||||
|  * a resolver or a guards executes asynchronously. Second, we need to actually run |  | ||||||
|  * activation in a BOOTSTRAP_LISTENER. We utilize the afterPreactivation |  | ||||||
|  * hook provided by the router to do that. |  | ||||||
|  * |  | ||||||
|  * The router navigation starts, reaches the point when preactivation is done, and then |  | ||||||
|  * pauses. It waits for the hook to be resolved. We then resolve it only in a bootstrap listener. |  | ||||||
|  */ |  | ||||||
| @Injectable() |  | ||||||
| export class RouterInitializer { |  | ||||||
|   private initNavigation: boolean; |  | ||||||
|   private resultOfPreactivationDone = new Subject<void>(); |  | ||||||
| 
 | 
 | ||||||
|   constructor(private injector: Injector) {} |  | ||||||
| 
 |  | ||||||
|   appInitializer(): Promise<any> { |  | ||||||
|     const p: Promise<any> = this.injector.get(LOCATION_INITIALIZED, Promise.resolve(null)); |  | ||||||
|     return p.then(() => { |  | ||||||
|       let resolve: Function = null; |  | ||||||
|       const res = new Promise(r => resolve = r); |  | ||||||
|       const router = this.injector.get(Router); |  | ||||||
|       const opts = this.injector.get(ROUTER_CONFIGURATION); |  | ||||||
| 
 |  | ||||||
|       if (opts.initialNavigation === false) { |  | ||||||
|         router.setUpLocationChangeListener(); |  | ||||||
|       } else { |  | ||||||
|         router.hooks.afterPreactivation = () => { |  | ||||||
|           // only the initial navigation should be delayed
 |  | ||||||
|           if (!this.initNavigation) { |  | ||||||
|             this.initNavigation = true; |  | ||||||
|             resolve(true); |  | ||||||
|             return this.resultOfPreactivationDone; |  | ||||||
| 
 |  | ||||||
|             // subsequent navigations should not be delayed
 |  | ||||||
|           } else { |  | ||||||
|             return of (null); |  | ||||||
|           } |  | ||||||
|         }; |  | ||||||
|         router.initialNavigation(); |  | ||||||
|       } |  | ||||||
| 
 |  | ||||||
|       return res; |  | ||||||
|     }); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   bootstrapListener(bootstrappedComponentRef: ComponentRef<any>): void { |  | ||||||
|     const ref = this.injector.get(ApplicationRef); |  | ||||||
|     if (bootstrappedComponentRef !== ref.components[0]) { |     if (bootstrappedComponentRef !== ref.components[0]) { | ||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     const preloader = this.injector.get(RouterPreloader); |  | ||||||
|     preloader.setUpPreloading(); |  | ||||||
| 
 |  | ||||||
|     const router = this.injector.get(Router); |  | ||||||
|     router.resetRootComponentType(ref.componentTypes[0]); |     router.resetRootComponentType(ref.componentTypes[0]); | ||||||
| 
 |     preloader.setUpPreloading(); | ||||||
|     this.resultOfPreactivationDone.next(null); |     if (opts.initialNavigation === false) { | ||||||
|     this.resultOfPreactivationDone.complete(); |       router.setUpLocationChangeListener(); | ||||||
|  |     } else { | ||||||
|  |       router.initialNavigation(); | ||||||
|     } |     } | ||||||
| } |   }; | ||||||
| 
 |  | ||||||
| export function getAppInitializer(r: RouterInitializer) { |  | ||||||
|   return r.appInitializer.bind(r); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| export function getBootstrapListener(r: RouterInitializer) { |  | ||||||
|   return r.bootstrapListener.bind(r); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
| @ -363,14 +306,11 @@ export const ROUTER_INITIALIZER = | |||||||
| 
 | 
 | ||||||
| export function provideRouterInitializer() { | export function provideRouterInitializer() { | ||||||
|   return [ |   return [ | ||||||
|     RouterInitializer, |  | ||||||
|     { |     { | ||||||
|       provide: APP_INITIALIZER, |       provide: ROUTER_INITIALIZER, | ||||||
|       multi: true, |       useFactory: initialRouterNavigation, | ||||||
|       useFactory: getAppInitializer, |       deps: [Router, ApplicationRef, RouterPreloader, ROUTER_CONFIGURATION] | ||||||
|       deps: [RouterInitializer] |  | ||||||
|     }, |     }, | ||||||
|     {provide: ROUTER_INITIALIZER, useFactory: getBootstrapListener, deps: [RouterInitializer]}, |  | ||||||
|     {provide: APP_BOOTSTRAP_LISTENER, multi: true, useExisting: ROUTER_INITIALIZER}, |     {provide: APP_BOOTSTRAP_LISTENER, multi: true, useExisting: ROUTER_INITIALIZER}, | ||||||
|   ]; |   ]; | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,128 +0,0 @@ | |||||||
| /** |  | ||||||
|  * @license |  | ||||||
|  * Copyright Google Inc. All Rights Reserved. |  | ||||||
|  * |  | ||||||
|  * Use of this source code is governed by an MIT-style license that can be |  | ||||||
|  * found in the LICENSE file at https://angular.io/license
 |  | ||||||
|  */ |  | ||||||
| 
 |  | ||||||
| import {APP_BASE_HREF} from '@angular/common'; |  | ||||||
| import {ApplicationRef, CUSTOM_ELEMENTS_SCHEMA, Component, NgModule, destroyPlatform} from '@angular/core'; |  | ||||||
| import {BrowserModule, DOCUMENT} from '@angular/platform-browser'; |  | ||||||
| import {platformBrowserDynamic} from '@angular/platform-browser-dynamic'; |  | ||||||
| import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter'; |  | ||||||
| import {Resolve, Router, RouterModule} from '@angular/router'; |  | ||||||
| 
 |  | ||||||
| describe('bootstrap', () => { |  | ||||||
| 
 |  | ||||||
|   @Component({selector: 'test-app', template: 'root <router-outlet></router-outlet>'}) |  | ||||||
|   class RootCmp { |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   @Component({selector: 'test-app2', template: 'root <router-outlet></router-outlet>'}) |  | ||||||
|   class SecondRootCmp { |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   @Component({selector: 'test', template: 'test'}) |  | ||||||
|   class TestCmp { |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   class TestResolver implements Resolve<any> { |  | ||||||
|     resolve() { |  | ||||||
|       let resolve: any = null; |  | ||||||
|       const res = new Promise(r => resolve = r); |  | ||||||
|       setTimeout(() => resolve('test-data'), 0); |  | ||||||
|       return res; |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   let testProviders: any[] = null; |  | ||||||
| 
 |  | ||||||
|   beforeEach(() => { |  | ||||||
|     destroyPlatform(); |  | ||||||
|     const fakeDoc = getDOM().createHtmlDocument(); |  | ||||||
|     const el1 = getDOM().createElement('test-app', fakeDoc); |  | ||||||
|     const el2 = getDOM().createElement('test-app2', fakeDoc); |  | ||||||
|     getDOM().appendChild(fakeDoc.body, el1); |  | ||||||
|     getDOM().appendChild(fakeDoc.body, el2); |  | ||||||
|     testProviders = |  | ||||||
|         [{provide: DOCUMENT, useValue: fakeDoc}, {provide: APP_BASE_HREF, useValue: ''}]; |  | ||||||
|   }); |  | ||||||
| 
 |  | ||||||
|   it('should wait for resolvers to complete', (done) => { |  | ||||||
|     @NgModule({ |  | ||||||
|       imports: [ |  | ||||||
|         BrowserModule, |  | ||||||
|         RouterModule.forRoot( |  | ||||||
|             [{path: '**', component: TestCmp, resolve: {test: TestResolver}}], {useHash: true}) |  | ||||||
|       ], |  | ||||||
|       declarations: [SecondRootCmp, RootCmp, TestCmp], |  | ||||||
|       bootstrap: [RootCmp], |  | ||||||
|       providers: [...testProviders, TestResolver], |  | ||||||
|       schemas: [CUSTOM_ELEMENTS_SCHEMA] |  | ||||||
|     }) |  | ||||||
|     class TestModule { |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     platformBrowserDynamic([]).bootstrapModule(TestModule).then(res => { |  | ||||||
|       const router = res.injector.get(Router); |  | ||||||
|       const data = router.routerState.snapshot.root.firstChild.data; |  | ||||||
|       expect(data['test']).toEqual('test-data'); |  | ||||||
|       done(); |  | ||||||
|     }); |  | ||||||
|   }); |  | ||||||
| 
 |  | ||||||
|   it('should not init router navigation listeners if a non root component is bootstrapped', |  | ||||||
|      (done) => { |  | ||||||
|        @NgModule({ |  | ||||||
|          imports: [BrowserModule, RouterModule.forRoot([], {useHash: true})], |  | ||||||
|          declarations: [SecondRootCmp, RootCmp], |  | ||||||
|          entryComponents: [SecondRootCmp], |  | ||||||
|          bootstrap: [RootCmp], |  | ||||||
|          providers: testProviders, |  | ||||||
|          schemas: [CUSTOM_ELEMENTS_SCHEMA] |  | ||||||
|        }) |  | ||||||
|        class TestModule { |  | ||||||
|        } |  | ||||||
| 
 |  | ||||||
|        platformBrowserDynamic([]).bootstrapModule(TestModule).then(res => { |  | ||||||
|          const router = res.injector.get(Router); |  | ||||||
|          spyOn(router, 'resetRootComponentType').and.callThrough(); |  | ||||||
| 
 |  | ||||||
|          const appRef: ApplicationRef = res.injector.get(ApplicationRef); |  | ||||||
|          appRef.bootstrap(SecondRootCmp); |  | ||||||
| 
 |  | ||||||
|          expect(router.resetRootComponentType).not.toHaveBeenCalled(); |  | ||||||
| 
 |  | ||||||
|          done(); |  | ||||||
|        }); |  | ||||||
|      }); |  | ||||||
| 
 |  | ||||||
|   it('should reinit router navigation listeners if a previously bootstrapped root component is destroyed', |  | ||||||
|      (done) => { |  | ||||||
|        @NgModule({ |  | ||||||
|          imports: [BrowserModule, RouterModule.forRoot([], {useHash: true})], |  | ||||||
|          declarations: [SecondRootCmp, RootCmp], |  | ||||||
|          entryComponents: [SecondRootCmp], |  | ||||||
|          bootstrap: [RootCmp], |  | ||||||
|          providers: testProviders, |  | ||||||
|          schemas: [CUSTOM_ELEMENTS_SCHEMA] |  | ||||||
|        }) |  | ||||||
|        class TestModule { |  | ||||||
|        } |  | ||||||
| 
 |  | ||||||
|        platformBrowserDynamic([]).bootstrapModule(TestModule).then(res => { |  | ||||||
|          const router = res.injector.get(Router); |  | ||||||
|          spyOn(router, 'resetRootComponentType').and.callThrough(); |  | ||||||
| 
 |  | ||||||
|          const appRef: ApplicationRef = res.injector.get(ApplicationRef); |  | ||||||
|          appRef.components[0].onDestroy(() => { |  | ||||||
|            appRef.bootstrap(SecondRootCmp); |  | ||||||
|            expect(router.resetRootComponentType).toHaveBeenCalled(); |  | ||||||
|            done(); |  | ||||||
|          }); |  | ||||||
| 
 |  | ||||||
|          appRef.components[0].destroy(); |  | ||||||
|        }); |  | ||||||
|      }); |  | ||||||
| }); |  | ||||||
							
								
								
									
										91
									
								
								modules/@angular/router/test/router_module.spec.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								modules/@angular/router/test/router_module.spec.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,91 @@ | |||||||
|  | /** | ||||||
|  |  * @license | ||||||
|  |  * Copyright Google Inc. All Rights Reserved. | ||||||
|  |  * | ||||||
|  |  * Use of this source code is governed by an MIT-style license that can be | ||||||
|  |  * found in the LICENSE file at https://angular.io/license
 | ||||||
|  |  */ | ||||||
|  | import {APP_BASE_HREF} from '@angular/common'; | ||||||
|  | import {ApplicationRef, Component, NgModule} from '@angular/core'; | ||||||
|  | import {TestBed, inject} from '@angular/core/testing'; | ||||||
|  | import {DOCUMENT} from '@angular/platform-browser'; | ||||||
|  | import {Router, RouterModule, Routes} from '@angular/router'; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @Component({selector: 'app-root', template: ''}) | ||||||
|  | export class AppRootComponent { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | @Component({selector: 'bootstrappable-component', template: ''}) | ||||||
|  | export class BootstrappableComponent { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export const appRoutes: Routes = [{path: '**', redirectTo: ''}]; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @NgModule({ | ||||||
|  |   imports: [RouterModule.forRoot(appRoutes)], | ||||||
|  |   declarations: [AppRootComponent, BootstrappableComponent], | ||||||
|  |   entryComponents: [AppRootComponent, BootstrappableComponent], | ||||||
|  |   providers: [{provide: APP_BASE_HREF, useValue: '/'}] | ||||||
|  | }) | ||||||
|  | export class RouterInitTestModule { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | describe('RouterModule', () => { | ||||||
|  |   describe('RouterInitializer', () => { | ||||||
|  | 
 | ||||||
|  |     beforeEach(() => { TestBed.configureTestingModule({imports: [RouterInitTestModule]}); }); | ||||||
|  | 
 | ||||||
|  |     beforeEach(inject([DOCUMENT], function(doc: HTMLDocument) { | ||||||
|  | 
 | ||||||
|  |       const elRootApp = doc.createElement('app-root'); | ||||||
|  |       doc.body.appendChild(elRootApp); | ||||||
|  | 
 | ||||||
|  |       const elBootComp = doc.createElement('bootstrappable-component'); | ||||||
|  |       doc.body.appendChild(elBootComp); | ||||||
|  | 
 | ||||||
|  |     })); | ||||||
|  |     it('should not init router navigation listeners if a non root component is bootstrapped', | ||||||
|  |        () => { | ||||||
|  | 
 | ||||||
|  |          const appRef: ApplicationRef = TestBed.get(ApplicationRef); | ||||||
|  |          const r: Router = TestBed.get(Router); | ||||||
|  | 
 | ||||||
|  |          const spy = spyOn(r, 'resetRootComponentType').and.callThrough(); | ||||||
|  | 
 | ||||||
|  |          appRef.bootstrap(AppRootComponent); | ||||||
|  |          expect(r.resetRootComponentType).toHaveBeenCalled(); | ||||||
|  | 
 | ||||||
|  |          spy.calls.reset(); | ||||||
|  | 
 | ||||||
|  |          appRef.bootstrap(BootstrappableComponent); | ||||||
|  |          expect(r.resetRootComponentType).not.toHaveBeenCalled(); | ||||||
|  |        }); | ||||||
|  |     it('should reinit router navigation listeners if a previously bootstrapped root component is destroyed', | ||||||
|  |        (done) => { | ||||||
|  | 
 | ||||||
|  |          const appRef: ApplicationRef = TestBed.get(ApplicationRef); | ||||||
|  |          const r: Router = TestBed.get(Router); | ||||||
|  | 
 | ||||||
|  |          const spy = spyOn(r, 'resetRootComponentType').and.callThrough(); | ||||||
|  | 
 | ||||||
|  |          const compRef = appRef.bootstrap(AppRootComponent); | ||||||
|  |          expect(r.resetRootComponentType).toHaveBeenCalled(); | ||||||
|  | 
 | ||||||
|  |          spy.calls.reset(); | ||||||
|  | 
 | ||||||
|  |          compRef.onDestroy(() => { | ||||||
|  | 
 | ||||||
|  |            appRef.bootstrap(BootstrappableComponent); | ||||||
|  |            expect(r.resetRootComponentType).toHaveBeenCalled(); | ||||||
|  | 
 | ||||||
|  |            done(); | ||||||
|  |          }); | ||||||
|  | 
 | ||||||
|  |          compRef.destroy(); | ||||||
|  |        }); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  | }); | ||||||
| @ -6,8 +6,8 @@ | |||||||
|  * found in the LICENSE file at https://angular.io/license
 |  * found in the LICENSE file at https://angular.io/license
 | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| import {APP_BOOTSTRAP_LISTENER, ComponentRef, InjectionToken} from '@angular/core'; | import {APP_BOOTSTRAP_LISTENER, ApplicationRef, ComponentRef, InjectionToken} from '@angular/core'; | ||||||
| import {Router} from '@angular/router'; | import {ExtraOptions, ROUTER_CONFIGURATION, ROUTER_INITIALIZER, Router, RouterPreloader} from '@angular/router'; | ||||||
| import {UpgradeModule} from '@angular/upgrade/static'; | import {UpgradeModule} from '@angular/upgrade/static'; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @ -36,17 +36,38 @@ import {UpgradeModule} from '@angular/upgrade/static'; | |||||||
|  * @experimental |  * @experimental | ||||||
|  */ |  */ | ||||||
| export const RouterUpgradeInitializer = { | export const RouterUpgradeInitializer = { | ||||||
|   provide: APP_BOOTSTRAP_LISTENER, |   provide: ROUTER_INITIALIZER, | ||||||
|   multi: true, |   useFactory: initialRouterNavigation, | ||||||
|   useFactory: locationSyncBootstrapListener, |   deps: [UpgradeModule, ApplicationRef, RouterPreloader, ROUTER_CONFIGURATION] | ||||||
|   deps: [UpgradeModule] |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * @internal |  * @internal | ||||||
|  */ |  */ | ||||||
| export function locationSyncBootstrapListener(ngUpgrade: UpgradeModule) { | export function initialRouterNavigation( | ||||||
|   return () => { setUpLocationSync(ngUpgrade); }; |     ngUpgrade: UpgradeModule, ref: ApplicationRef, preloader: RouterPreloader, | ||||||
|  |     opts: ExtraOptions): Function { | ||||||
|  |   return () => { | ||||||
|  |     if (!ngUpgrade.$injector) { | ||||||
|  |       throw new Error(` | ||||||
|  |         RouterUpgradeInitializer can be used only after UpgradeModule.bootstrap has been called. | ||||||
|  |         Remove RouterUpgradeInitializer and call setUpLocationSync after UpgradeModule.bootstrap. | ||||||
|  |       `);
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     const router = ngUpgrade.injector.get(Router); | ||||||
|  |     const ref = ngUpgrade.injector.get(ApplicationRef); | ||||||
|  | 
 | ||||||
|  |     (router as any).resetRootComponentType(ref.componentTypes[0]); | ||||||
|  |     preloader.setUpPreloading(); | ||||||
|  |     if (opts.initialNavigation === false) { | ||||||
|  |       router.setUpLocationChangeListener(); | ||||||
|  |     } else { | ||||||
|  |       router.initialNavigation(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     setUpLocationSync(ngUpgrade); | ||||||
|  |   }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
| @ -57,14 +78,7 @@ export function locationSyncBootstrapListener(ngUpgrade: UpgradeModule) { | |||||||
|  * |  * | ||||||
|  * @experimental |  * @experimental | ||||||
|  */ |  */ | ||||||
| export function setUpLocationSync(ngUpgrade: UpgradeModule) { | export function setUpLocationSync(ngUpgrade: UpgradeModule): void { | ||||||
|   if (!ngUpgrade.$injector) { |  | ||||||
|     throw new Error(` |  | ||||||
|         RouterUpgradeInitializer can be used only after UpgradeModule.bootstrap has been called. |  | ||||||
|         Remove RouterUpgradeInitializer and call setUpLocationSync after UpgradeModule.bootstrap. |  | ||||||
|       `);
 |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   const router: Router = ngUpgrade.injector.get(Router); |   const router: Router = ngUpgrade.injector.get(Router); | ||||||
|   const url = document.createElement('a'); |   const url = document.createElement('a'); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -25,9 +25,7 @@ export const ROUTES = [ | |||||||
| 
 | 
 | ||||||
| @NgModule({ | @NgModule({ | ||||||
|   imports: [WorkerAppModule, RouterModule.forRoot(ROUTES, {useHash: true})], |   imports: [WorkerAppModule, RouterModule.forRoot(ROUTES, {useHash: true})], | ||||||
|   providers: [ |   providers: [WORKER_APP_LOCATION_PROVIDERS], | ||||||
|     WORKER_APP_LOCATION_PROVIDERS, |  | ||||||
|   ], |  | ||||||
|   bootstrap: [App], |   bootstrap: [App], | ||||||
|   declarations: [App, Start, Contact, About] |   declarations: [App, Start, Contact, About] | ||||||
| }) | }) | ||||||
|  | |||||||
							
								
								
									
										3
									
								
								tools/public_api_guard/common/index.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								tools/public_api_guard/common/index.d.ts
									
									
									
									
										vendored
									
									
								
							| @ -86,9 +86,6 @@ export declare class Location { | |||||||
|     static stripTrailingSlash(url: string): string; |     static stripTrailingSlash(url: string): string; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** @experimental */ |  | ||||||
| export declare const LOCATION_INITIALIZED: InjectionToken<Promise<any>>; |  | ||||||
| 
 |  | ||||||
| /** @experimental */ | /** @experimental */ | ||||||
| export interface LocationChangeEvent { | export interface LocationChangeEvent { | ||||||
|     type: string; |     type: string; | ||||||
|  | |||||||
| @ -93,10 +93,6 @@ export declare const WORKER_APP_LOCATION_PROVIDERS: ({ | |||||||
|     useFactory: (platformLocation: WebWorkerPlatformLocation, zone: NgZone) => () => Promise<boolean>; |     useFactory: (platformLocation: WebWorkerPlatformLocation, zone: NgZone) => () => Promise<boolean>; | ||||||
|     multi: boolean; |     multi: boolean; | ||||||
|     deps: (typeof NgZone | typeof PlatformLocation)[]; |     deps: (typeof NgZone | typeof PlatformLocation)[]; | ||||||
| } | { |  | ||||||
|     provide: InjectionToken<Promise<any>>; |  | ||||||
|     useFactory: (platformLocation: WebWorkerPlatformLocation) => Promise<any>; |  | ||||||
|     deps: typeof PlatformLocation[]; |  | ||||||
| })[]; | })[]; | ||||||
| 
 | 
 | ||||||
| /** @experimental */ | /** @experimental */ | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user