diff --git a/packages/router/src/operators/recognize.ts b/packages/router/src/operators/recognize.ts index 4d30ac2810..67e7b6a7df 100644 --- a/packages/router/src/operators/recognize.ts +++ b/packages/router/src/operators/recognize.ts @@ -17,13 +17,13 @@ import {UrlTree} from '../url_tree'; export function recognize( rootComponentType: Type| null, config: Route[], serializer: (url: UrlTree) => string, - paramsInheritanceStrategy: 'emptyOnly' | - 'always'): MonoTypeOperatorFunction { + paramsInheritanceStrategy: 'emptyOnly' | 'always', relativeLinkResolution: 'legacy' | + 'corrected'): MonoTypeOperatorFunction { return function(source: Observable) { return source.pipe(mergeMap( t => recognizeFn( rootComponentType, config, t.urlAfterRedirects, serializer(t.urlAfterRedirects), - paramsInheritanceStrategy) + paramsInheritanceStrategy, relativeLinkResolution) .pipe(map(targetSnapshot => ({...t, targetSnapshot}))))); }; -} \ No newline at end of file +} diff --git a/packages/router/src/router.ts b/packages/router/src/router.ts index ef6528f7a5..41b5cf7fa2 100644 --- a/packages/router/src/router.ts +++ b/packages/router/src/router.ts @@ -414,7 +414,7 @@ export class Router { // Recognize recognize( this.rootComponentType, this.config, (url) => this.serializeUrl(url), - this.paramsInheritanceStrategy), + this.paramsInheritanceStrategy, this.relativeLinkResolution), // Fire RoutesRecognized tap(t => { diff --git a/packages/router/test/integration.spec.ts b/packages/router/test/integration.spec.ts index 3619c5f10f..32559d1fbf 100644 --- a/packages/router/test/integration.spec.ts +++ b/packages/router/test/integration.spec.ts @@ -76,6 +76,41 @@ describe('Integration', () => { ]); }))); + describe('relativeLinkResolution', () => { + beforeEach(inject([Router], (router: Router) => { + router.resetConfig([{ + path: 'foo', + children: [{path: 'bar', children: [{path: '', component: RelativeLinkCmp}]}] + }]); + })); + + it('should not ignore empty paths in legacy mode', + fakeAsync(inject([Router], (router: Router) => { + router.relativeLinkResolution = 'legacy'; + + const fixture = createRoot(router, RootCmp); + + router.navigateByUrl('/foo/bar'); + advance(fixture); + + const link = fixture.nativeElement.querySelector('a'); + expect(link.getAttribute('href')).toEqual('/foo/bar/simple'); + }))); + + it('should ignore empty paths in corrected mode', + fakeAsync(inject([Router], (router: Router) => { + router.relativeLinkResolution = 'corrected'; + + const fixture = createRoot(router, RootCmp); + + router.navigateByUrl('/foo/bar'); + advance(fixture); + + const link = fixture.nativeElement.querySelector('a'); + expect(link.getAttribute('href')).toEqual('/foo/simple'); + }))); + }); + it('should set the restoredState to null when executing imperative navigations', fakeAsync(inject([Router], (router: Router) => { router.resetConfig([ @@ -4030,6 +4065,57 @@ describe('Integration', () => { ]); }))); }); + + describe('relativeLinkResolution', () => { + @Component({selector: 'link-cmp', template: `link`}) + class RelativeLinkCmp { + } + + @NgModule({ + declarations: [RelativeLinkCmp], + imports: [RouterModule.forChild([ + {path: 'foo/bar', children: [{path: '', component: RelativeLinkCmp}]}, + ])] + }) + class LazyLoadedModule { + } + + it('should not ignore empty path when in legacy mode', + fakeAsync(inject( + [Router, NgModuleFactoryLoader], + (router: Router, loader: SpyNgModuleFactoryLoader) => { + router.relativeLinkResolution = 'legacy'; + loader.stubbedModules = {expected: LazyLoadedModule}; + + const fixture = createRoot(router, RootCmp); + + router.resetConfig([{path: 'lazy', loadChildren: 'expected'}]); + + router.navigateByUrl('/lazy/foo/bar'); + advance(fixture); + + const link = fixture.nativeElement.querySelector('a'); + expect(link.getAttribute('href')).toEqual('/lazy/foo/bar/simple'); + }))); + + it('should ignore empty path when in corrected mode', + fakeAsync(inject( + [Router, NgModuleFactoryLoader], + (router: Router, loader: SpyNgModuleFactoryLoader) => { + router.relativeLinkResolution = 'corrected'; + loader.stubbedModules = {expected: LazyLoadedModule}; + + const fixture = createRoot(router, RootCmp); + + router.resetConfig([{path: 'lazy', loadChildren: 'expected'}]); + + router.navigateByUrl('/lazy/foo/bar'); + advance(fixture); + + const link = fixture.nativeElement.querySelector('a'); + expect(link.getAttribute('href')).toEqual('/lazy/foo/simple'); + }))); + }); }); describe('Custom Route Reuse Strategy', () => {