diff --git a/modules/@angular/router/src/recognize.ts b/modules/@angular/router/src/recognize.ts index 47d7081b5d..9788f8219d 100644 --- a/modules/@angular/router/src/recognize.ts +++ b/modules/@angular/router/src/recognize.ts @@ -116,7 +116,8 @@ function processPathsWithParamsAgainstRoute( const snapshot = new ActivatedRouteSnapshot( paths, Object.freeze(merge(inherited.allParams, params)), merge(inherited.allData, getData(route)), outlet, route.component, route, - getSourceSegment(rawSegment), getPathIndexShift(rawSegment) - 1, newInheritedResolve); + getSourceSegment(rawSegment), getPathIndexShift(rawSegment) + paths.length, + newInheritedResolve); return [new TreeNode(snapshot, [])]; } @@ -130,7 +131,7 @@ function processPathsWithParamsAgainstRoute( const snapshot = new ActivatedRouteSnapshot( consumedPaths, Object.freeze(merge(inherited.allParams, parameters)), merge(inherited.allData, getData(route)), outlet, route.component, route, - getSourceSegment(rawSegment), getPathIndexShift(rawSegment) + pathIndex + lastChild - 1, + getSourceSegment(rawSegment), getPathIndexShift(rawSegment) + consumedPaths.length, newInheritedResolve); const newInherited = route.component ? @@ -227,12 +228,12 @@ function getSourceSegment(segment: UrlSegment): UrlSegment { function getPathIndexShift(segment: UrlSegment): number { let s = segment; - let res = 0; + let res = (s._pathIndexShift ? s._pathIndexShift : 0); while (s._sourceSegment) { s = s._sourceSegment; - res += segment._pathIndexShift; + res += (s._pathIndexShift ? s._pathIndexShift : 0); } - return res; + return res - 1; } function split( @@ -245,7 +246,7 @@ function split( createChildrenForEmptyPaths( segment, consumedPaths, config, new UrlSegment(slicedPath, segment.children))); s._sourceSegment = segment; - s._pathIndexShift = 0; + s._pathIndexShift = consumedPaths.length; return {segment: s, slicedPath: []}; } else if (slicedPath.length === 0 && containsEmptyPathMatches(segment, slicedPath, config)) { @@ -253,11 +254,14 @@ function split( segment.pathsWithParams, addEmptyPathsToChildrenIfNeeded(segment, slicedPath, config, segment.children)); s._sourceSegment = segment; - s._pathIndexShift = 0; + s._pathIndexShift = consumedPaths.length; return {segment: s, slicedPath}; } else { - return {segment, slicedPath}; + const s = new UrlSegment(segment.pathsWithParams, segment.children); + s._sourceSegment = segment; + s._pathIndexShift = consumedPaths.length; + return {segment: s, slicedPath}; } } diff --git a/modules/@angular/router/test/create_url_tree.spec.ts b/modules/@angular/router/test/create_url_tree.spec.ts index 4f17275622..9189e72c42 100644 --- a/modules/@angular/router/test/create_url_tree.spec.ts +++ b/modules/@angular/router/test/create_url_tree.spec.ts @@ -8,7 +8,6 @@ import {DefaultUrlSerializer, UrlPathWithParams, UrlSegment, UrlTree} from '../s describe('createUrlTree', () => { const serializer = new DefaultUrlSerializer(); - it('should navigate to the root', () => { const p = serializer.parse('/'); const t = createRoot(p, ['/']); @@ -132,12 +131,6 @@ describe('createUrlTree', () => { expect(serializer.serialize(t)).toEqual('/a/c2'); }); - it('should work when given ../', () => { - const p = serializer.parse('/a/c'); - const t = create(p.root.children[PRIMARY_OUTLET], 1, p, ['../', 'c2']); - expect(serializer.serialize(t)).toEqual('/a/c2'); - }); - it('should support setting matrix params', () => { const p = serializer.parse('/a/(c//left:cp)(left:ap)'); const t = create(p.root.children[PRIMARY_OUTLET], 0, p, ['../', {x: 5}]); @@ -152,10 +145,22 @@ describe('createUrlTree', () => { expect(serializer.serialize(t)).toEqual('/q2(left:ap)'); }); - xit('should navigate to the root', () => { + it('should navigate to the root', () => { const p = serializer.parse('/a/c'); const t = create(p.root.children[PRIMARY_OUTLET], 0, p, ['../']); - expect(serializer.serialize(t)).toEqual(''); + expect(serializer.serialize(t)).toEqual('/'); + }); + + it('should work with ../ when absolute url', () => { + const p = serializer.parse('/a/c'); + const t = create(p.root.children[PRIMARY_OUTLET], 1, p, ['../', 'c2']); + expect(serializer.serialize(t)).toEqual('/a/c2'); + }); + + it('should work with position = -1', () => { + const p = serializer.parse('/'); + const t = create(p.root, -1, p, ['11']); + expect(serializer.serialize(t)).toEqual('/11'); }); it('should throw when too many ..', () => { @@ -196,6 +201,7 @@ describe('createUrlTree', () => { }); }); + function createRoot(tree: UrlTree, commands: any[], queryParams?: Params, fragment?: string) { const s = new ActivatedRouteSnapshot( [], {}, {}, PRIMARY_OUTLET, 'someComponent', null, tree.root, -1, null); diff --git a/modules/@angular/router/test/recognize.spec.ts b/modules/@angular/router/test/recognize.spec.ts index bc72517fc7..19f54a86da 100644 --- a/modules/@angular/router/test/recognize.spec.ts +++ b/modules/@angular/router/test/recognize.spec.ts @@ -81,6 +81,28 @@ describe('recognize', () => { }); }); + it('should set url segment and index properly (wildcard)', () => { + const url = tree('a/b/c'); + recognize( + RootComponent, + [ + {path: 'a', component: ComponentA, children: [{path: '**', component: ComponentB}]}, + ], + url, 'a/b/c') + .subscribe((s: RouterStateSnapshot) => { + expect(s.root._urlSegment).toBe(url.root); + expect(s.root._lastPathIndex).toBe(-1); + + const compA = s.firstChild(s.root); + expect(compA._urlSegment).toBe(url.root.children[PRIMARY_OUTLET]); + expect(compA._lastPathIndex).toBe(0); + + const compC = s.firstChild(compA); + expect(compC._urlSegment).toBe(url.root.children[PRIMARY_OUTLET]); + expect(compC._lastPathIndex).toBe(2); + }); + }); + it('should match routes in the depth first order', () => { checkRecognize( [ @@ -357,6 +379,62 @@ describe('recognize', () => { expect(c._lastPathIndex).toBe(0); }); }); + + it('should set url segment and index properly when nested empty-path segments', () => { + const url = tree('a'); + recognize( + RootComponent, [{ + path: 'a', + children: [ + {path: '', component: ComponentB, children: [{path: '', component: ComponentC}]} + ] + }], + url, 'a') + .forEach((s: RouterStateSnapshot) => { + expect(s.root._urlSegment).toBe(url.root); + expect(s.root._lastPathIndex).toBe(-1); + + const a = s.firstChild(s.root); + expect(a._urlSegment).toBe(url.root.children[PRIMARY_OUTLET]); + expect(a._lastPathIndex).toBe(0); + + const b = s.firstChild(a); + expect(b._urlSegment).toBe(url.root.children[PRIMARY_OUTLET]); + expect(b._lastPathIndex).toBe(0); + + const c = s.firstChild(b); + expect(c._urlSegment).toBe(url.root.children[PRIMARY_OUTLET]); + expect(c._lastPathIndex).toBe(0); + }); + }); + + it('should set url segment and index properly when nested empty-path segments (2)', () => { + const url = tree(''); + recognize( + RootComponent, [{ + path: '', + children: [ + {path: '', component: ComponentB, children: [{path: '', component: ComponentC}]} + ] + }], + url, '') + .forEach((s: RouterStateSnapshot) => { + expect(s.root._urlSegment).toBe(url.root); + expect(s.root._lastPathIndex).toBe(-1); + + const a = s.firstChild(s.root); + expect(a._urlSegment).toBe(url.root); + expect(a._lastPathIndex).toBe(-1); + + const b = s.firstChild(a); + expect(b._urlSegment).toBe(url.root); + expect(b._lastPathIndex).toBe(-1); + + const c = s.firstChild(b); + expect(c._urlSegment).toBe(url.root); + expect(c._lastPathIndex).toBe(-1); + }); + }); }); describe('aux split at the end (no right child)', () => {