diff --git a/modules/@angular/router/src/create_url_tree.ts b/modules/@angular/router/src/create_url_tree.ts index 092760bf80..d99dc4774b 100644 --- a/modules/@angular/router/src/create_url_tree.ts +++ b/modules/@angular/router/src/create_url_tree.ts @@ -157,15 +157,30 @@ function findStartingPosition( return new Position(urlTree.root, true, 0); } else if (route.snapshot._lastPathIndex === -1) { return new Position(route.snapshot._urlSegment, true, 0); - } else if (route.snapshot._lastPathIndex + 1 - normalizedChange.numberOfDoubleDots >= 0) { - return new Position( - route.snapshot._urlSegment, false, - route.snapshot._lastPathIndex + 1 - normalizedChange.numberOfDoubleDots); } else { - throw new Error('Invalid number of \'../\''); + const modifier = isMatrixParams(normalizedChange.commands[0]) ? 0 : 1; + const index = route.snapshot._lastPathIndex + modifier; + return createPositionApplyingDoubleDots( + route.snapshot._urlSegment, index, normalizedChange.numberOfDoubleDots); } } +function createPositionApplyingDoubleDots( + group: UrlSegmentGroup, index: number, numberOfDoubleDots: number): Position { + let g = group; + let ci = index; + let dd = numberOfDoubleDots; + while (dd > ci) { + dd -= ci; + g = g.parent; + if (!g) { + throw new Error('Invalid number of \'../\''); + } + ci = g.segments.length; + } + return new Position(g, false, ci - dd); +} + function getPath(command: any): any { return `${command}`; } @@ -184,6 +199,7 @@ function updateSegmentGroup( if (segmentGroup.segments.length === 0 && segmentGroup.hasChildren()) { return updateSegmentGroupChildren(segmentGroup, startIndex, commands); } + const m = prefixedWith(segmentGroup, startIndex, commands); const slicedCommands = commands.slice(m.lastIndex); @@ -258,7 +274,7 @@ function createNewSegmentGroup( } // if we start with an object literal, we need to reuse the path part from the segment - if (i === 0 && (typeof commands[0] === 'object')) { + if (i === 0 && isMatrixParams(commands[0])) { const p = segmentGroup.segments[startIndex]; paths.push(new UrlSegment(p.path, commands[0])); i++; @@ -267,7 +283,7 @@ function createNewSegmentGroup( const curr = getPath(commands[i]); const next = (i < commands.length - 1) ? commands[i + 1] : null; - if (curr && next && (typeof next === 'object')) { + if (curr && next && isMatrixParams(next)) { paths.push(new UrlSegment(curr, stringify(next))); i += 2; } else { diff --git a/modules/@angular/router/test/create_url_tree.spec.ts b/modules/@angular/router/test/create_url_tree.spec.ts index 7f8e20138a..dbe69ceaab 100644 --- a/modules/@angular/router/test/create_url_tree.spec.ts +++ b/modules/@angular/router/test/create_url_tree.spec.ts @@ -139,10 +139,23 @@ describe('createUrlTree', () => { expect(serializer.serialize(t)).toEqual('/a/(c2//left:cp)(left:ap)'); }); - it('should work when given params', () => { + it('should support parameters-only navigation', () => { + const p = serializer.parse('/a'); + const t = create(p.root.children[PRIMARY_OUTLET], 0, p, [{k: 99}]); + expect(serializer.serialize(t)).toEqual('/a;k=99'); + }); + + it('should support parameters-only navigation (nested case)', () => { const p = serializer.parse('/a/(c//left:cp)(left:ap)'); const t = create(p.root.children[PRIMARY_OUTLET], 0, p, [{'x': 99}]); - expect(serializer.serialize(t)).toEqual('/a/(c;x=99//left:cp)(left:ap)'); + expect(serializer.serialize(t)).toEqual('/a;x=99(left:ap)'); + }); + + it('should support parameters-only navigation (with a double dot)', () => { + const p = serializer.parse('/a/(c//left:cp)(left:ap)'); + const t = + create(p.root.children[PRIMARY_OUTLET].children[PRIMARY_OUTLET], 0, p, ['../', {x: 5}]); + expect(serializer.serialize(t)).toEqual('/a;x=5(left:ap)'); }); it('should work when index > 0', () => { @@ -157,13 +170,7 @@ describe('createUrlTree', () => { 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}]); - expect(serializer.serialize(t)).toEqual('/a;x=5(left:ap)'); - }); - - xit('should support going to a parent (across segments)', () => { + it('should support going to a parent (across segments)', () => { const p = serializer.parse('/q/(a/(c//left:cp)//left:qp)(left:ap)'); const t =