diff --git a/modules/@angular/router/src/create_url_tree.ts b/modules/@angular/router/src/create_url_tree.ts index cd05a1b0ea..092760bf80 100644 --- a/modules/@angular/router/src/create_url_tree.ts +++ b/modules/@angular/router/src/create_url_tree.ts @@ -38,6 +38,11 @@ function validateCommands(n: NormalizedNavigationCommands): void { if (n.isAbsolute && n.commands.length > 0 && isMatrixParams(n.commands[0])) { throw new Error('Root segment cannot have matrix parameters'); } + + const c = n.commands.filter(c => typeof c === 'object' && c.outlets !== undefined); + if (c.length > 0 && c[0] !== n.commands[n.commands.length - 1]) { + throw new Error('{outlets:{}} has to be the last command'); + } } function isMatrixParams(command: any): boolean { @@ -244,8 +249,14 @@ function prefixedWith(segmentGroup: UrlSegmentGroup, startIndex: number, command function createNewSegmentGroup( segmentGroup: UrlSegmentGroup, startIndex: number, commands: any[]): UrlSegmentGroup { const paths = segmentGroup.segments.slice(0, startIndex); + let i = 0; while (i < commands.length) { + if (typeof commands[i] === 'object' && commands[i].outlets !== undefined) { + const children = createNewSegmentChldren(commands[i].outlets); + return new UrlSegmentGroup(paths, children); + } + // if we start with an object literal, we need to reuse the path part from the segment if (i === 0 && (typeof commands[0] === 'object')) { const p = segmentGroup.segments[startIndex]; @@ -267,6 +278,16 @@ function createNewSegmentGroup( return new UrlSegmentGroup(paths, {}); } +function createNewSegmentChldren(outlets: {[name: string]: any}): any { + const children: {[key: string]: UrlSegmentGroup} = {}; + forEach(outlets, (commands: any, outlet: string) => { + if (commands !== null) { + children[outlet] = createNewSegmentGroup(new UrlSegmentGroup([], {}), 0, commands); + } + }); + return children; +} + function stringify(params: {[key: string]: any}): {[key: string]: string} { const res: {[key: string]: string} = {}; forEach(params, (v: any, k: string) => res[k] = `${v}`); diff --git a/modules/@angular/router/test/create_url_tree.spec.ts b/modules/@angular/router/test/create_url_tree.spec.ts index 82a016083f..7f8e20138a 100644 --- a/modules/@angular/router/test/create_url_tree.spec.ts +++ b/modules/@angular/router/test/create_url_tree.spec.ts @@ -72,6 +72,12 @@ describe('createUrlTree', () => { expect(serializer.serialize(t)).toEqual('/a/(b//right:d/11/e)'); }); + it('should throw when outlets is not the last command', () => { + const p = serializer.parse('/a'); + expect(() => createRoot(p, ['a', {outlets: {right: ['c']}}, 'c'])) + .toThrowError('{outlets:{}} has to be the last command'); + }); + it('should support updating using a string', () => { const p = serializer.parse('/a(right:b)'); const t = createRoot(p, [{outlets: {right: 'c/11/d'}}]); @@ -188,6 +194,12 @@ describe('createUrlTree', () => { expect(() => create(p.root.children[PRIMARY_OUTLET], 0, p, ['../../'])) .toThrowError('Invalid number of \'../\''); }); + + it('should support updating secondary segments', () => { + const p = serializer.parse('/a/b'); + const t = create(p.root.children[PRIMARY_OUTLET], 1, p, [{outlets: {right: ['c']}}]); + expect(serializer.serialize(t)).toEqual('/a/b/(right:c)'); + }); }); it('should set query params', () => { @@ -209,7 +221,6 @@ describe('createUrlTree', () => { }); }); - function createRoot(tree: UrlTree, commands: any[], queryParams?: Params, fragment?: string) { const s = new ActivatedRouteSnapshot( [], {}, {}, '', {}, PRIMARY_OUTLET, 'someComponent', null, tree.root, -1,