diff --git a/modules/@angular/router/src/router_module.ts b/modules/@angular/router/src/router_module.ts index 120ded2235..19d182c31d 100644 --- a/modules/@angular/router/src/router_module.ts +++ b/modules/@angular/router/src/router_module.ts @@ -7,7 +7,7 @@ */ import {APP_BASE_HREF, HashLocationStrategy, Location, LocationStrategy, PathLocationStrategy, PlatformLocation} from '@angular/common'; -import {ANALYZE_FOR_ENTRY_COMPONENTS, APP_BOOTSTRAP_LISTENER, ApplicationRef, Compiler, Inject, Injector, ModuleWithProviders, NgModule, NgModuleFactoryLoader, OpaqueToken, Optional, SystemJsNgModuleLoader} from '@angular/core'; +import {ANALYZE_FOR_ENTRY_COMPONENTS, APP_BOOTSTRAP_LISTENER, ApplicationRef, BaseException, Compiler, Inject, Injector, ModuleWithProviders, NgModule, NgModuleFactoryLoader, OpaqueToken, Optional, SkipSelf, SystemJsNgModuleLoader} from '@angular/core'; import {Route, Routes} from './config'; import {RouterLink, RouterLinkWithHref} from './directives/router_link'; @@ -32,6 +32,8 @@ export const ROUTER_DIRECTIVES = [RouterOutlet, RouterLink, RouterLinkWithHref, */ export const ROUTER_CONFIGURATION = new OpaqueToken('ROUTER_CONFIGURATION'); +export const ROUTER_FORROOT_GUARD = new OpaqueToken('ROUTER_FORROOT_GUARD'); + const pathLocationStrategy = { provide: LocationStrategy, useClass: PathLocationStrategy @@ -81,11 +83,17 @@ export const ROUTER_PROVIDERS: any[] = [ */ @NgModule({declarations: ROUTER_DIRECTIVES, exports: ROUTER_DIRECTIVES}) export class RouterModule { + constructor(@Optional() @Inject(ROUTER_FORROOT_GUARD) guard: any) {} + static forRoot(routes: Routes, config?: ExtraOptions): ModuleWithProviders { return { ngModule: RouterModule, providers: [ - ROUTER_PROVIDERS, provideRoutes(routes), + ROUTER_PROVIDERS, provideRoutes(routes), { + provide: ROUTER_FORROOT_GUARD, + useFactory: provideForRootGuard, + deps: [[Router, new Optional(), new SkipSelf()]] + }, {provide: ROUTER_CONFIGURATION, useValue: config ? config : {}}, { provide: LocationStrategy, useFactory: provideLocationStrategy, @@ -109,6 +117,14 @@ export function provideLocationStrategy( new PathLocationStrategy(platformLocationStrategy, baseHref); } +export function provideForRootGuard(router: Router): any { + if (router) { + throw new BaseException( + `RouterModule.forRoot() called twice. Lazy loaded modules should use RouterModule.forChild() instead.`); + } + return 'guarded'; +} + /** * @stable */ diff --git a/modules/@angular/router/test/integration.spec.ts b/modules/@angular/router/test/integration.spec.ts index 489bcfe358..707cab20cc 100644 --- a/modules/@angular/router/test/integration.spec.ts +++ b/modules/@angular/router/test/integration.spec.ts @@ -1411,7 +1411,34 @@ describe('Integration', () => { expect(fixture.debugElement.nativeElement) .toHaveText('lazy-loaded-parent [lazy-loaded-child]'); }))); + it('throws an error when forRoot() is used in a lazy context', + fakeAsync(inject( + [Router, Location, NgModuleFactoryLoader], + (router: Router, location: Location, loader: SpyNgModuleFactoryLoader) => { + @Component({selector: 'lazy', template: 'should not show'}) + class LazyLoadedComponent { + } + @NgModule({ + declarations: [LazyLoadedComponent], + imports: [RouterModule.forRoot([{path: 'loaded', component: LazyLoadedComponent}])] + }) + class LoadedModule { + } + + loader.stubbedModules = {expected: LoadedModule}; + + const fixture = createRoot(router, RootCmp); + + router.resetConfig([{path: 'lazy', loadChildren: 'expected'}]); + + let recordedError: any = null; + router.navigateByUrl('/lazy/loaded').catch(err => recordedError = err); + advance(fixture); + expect(recordedError.message) + .toEqual( + `RouterModule.forRoot() called twice. Lazy loaded modules should use RouterModule.forChild() instead.`); + }))); it('should combine routes from multiple modules into a single configuration', fakeAsync(inject( [Router, Location, NgModuleFactoryLoader], diff --git a/tools/public_api_guard/router/index.d.ts b/tools/public_api_guard/router/index.d.ts index d86136619e..daf6c4ab15 100644 --- a/tools/public_api_guard/router/index.d.ts +++ b/tools/public_api_guard/router/index.d.ts @@ -239,6 +239,7 @@ export declare class RouterLinkWithHref implements OnChanges, OnDestroy { /** @stable */ export declare class RouterModule { + constructor(guard: any); static forChild(routes: Routes): ModuleWithProviders; static forRoot(routes: Routes, config?: ExtraOptions): ModuleWithProviders; }