diff --git a/modules/@angular/router/src/url_serializer.ts b/modules/@angular/router/src/url_serializer.ts index 1dbd74d17c..bdc6755f29 100644 --- a/modules/@angular/router/src/url_serializer.ts +++ b/modules/@angular/router/src/url_serializer.ts @@ -1,4 +1,5 @@ import { UrlTree, UrlSegment } from './url_tree'; +import { PRIMARY_OUTLET } from './shared'; import { rootNode, TreeNode } from './utils/tree'; /** @@ -54,7 +55,8 @@ function serializeChildren(node: TreeNode): string { } export function serializeSegment(segment: UrlSegment): string { - return `${segment.path}${serializeParams(segment.parameters)}`; + const outlet = segment.outlet === PRIMARY_OUTLET ? '' : `${segment.outlet}:`; + return `${outlet}${segment.path}${serializeParams(segment.parameters)}`; } function serializeParams(params: {[key: string]: string}): string { @@ -105,41 +107,58 @@ class UrlParser { parseRootSegment(): TreeNode { if (this.remaining == '' || this.remaining == '/') { - return new TreeNode(new UrlSegment('', {}), []); + return new TreeNode(new UrlSegment('', {}, PRIMARY_OUTLET), []); } else { - let segments = this.parseSegments(); - return new TreeNode(new UrlSegment('', {}), segments); + const segments = this.parseSegments(false); + return new TreeNode(new UrlSegment('', {}, PRIMARY_OUTLET), segments); } } - parseSegments(): TreeNode[] { + parseSegments(hasOutletName: boolean): TreeNode[] { if (this.remaining.length == 0) { return []; } if (this.peekStartsWith('/')) { this.capture('/'); } - var path = matchUrlSegment(this.remaining); + let path = matchUrlSegment(this.remaining); this.capture(path); - var matrixParams: {[key: string]: any} = {}; + let outletName; + if (hasOutletName) { + if (path.indexOf(":") === -1) { + throw new Error("Not outlet name is provided"); + } + if (path.indexOf(":") > -1 && hasOutletName) { + let parts = path.split(":"); + outletName = parts[0]; + path = parts[1]; + } + } else { + if (path.indexOf(":") > -1) { + throw new Error("Not outlet name is allowed"); + } + outletName = PRIMARY_OUTLET; + } + + let matrixParams: {[key: string]: any} = {}; if (this.peekStartsWith(';')) { matrixParams = this.parseMatrixParams(); } - var secondary = []; + let secondary = []; if (this.peekStartsWith('(')) { secondary = this.parseSecondarySegments(); } - var children: TreeNode[] = []; + let children: TreeNode[] = []; if (this.peekStartsWith('/') && !this.peekStartsWith('//')) { this.capture('/'); - children = this.parseSegments(); + children = this.parseSegments(false); } - let segment = new UrlSegment(path, matrixParams); - let node = new TreeNode(segment, children); + const segment = new UrlSegment(path, matrixParams, outletName); + const node = new TreeNode(segment, children); return [node].concat(secondary); } @@ -215,7 +234,7 @@ class UrlParser { this.capture('('); while (!this.peekStartsWith(')') && this.remaining.length > 0) { - segments = segments.concat(this.parseSegments()); + segments = segments.concat(this.parseSegments(true)); if (this.peekStartsWith('//')) { this.capture('//'); } diff --git a/modules/@angular/router/src/url_tree.ts b/modules/@angular/router/src/url_tree.ts index 915c4c6b98..84608c0542 100644 --- a/modules/@angular/router/src/url_tree.ts +++ b/modules/@angular/router/src/url_tree.ts @@ -1,5 +1,10 @@ import { Tree, TreeNode } from './utils/tree'; import { shallowEqual } from './utils/collection'; +import { PRIMARY_OUTLET } from './shared'; + +export function createEmptyUrlTree() { + return new UrlTree(new TreeNode(new UrlSegment("", {}, PRIMARY_OUTLET), []), {}, null); +} /** * A URL in the tree form. @@ -11,17 +16,18 @@ export class UrlTree extends Tree { } export class UrlSegment { - constructor(public path: string, public parameters: {[key: string]: string}) {} + constructor(public path: string, public parameters: {[key: string]: string}, public outlet: string) {} toString() { - let params = []; + const params = []; for (let prop in this.parameters) { if (this.parameters.hasOwnProperty(prop)) { params.push(`${prop}=${this.parameters[prop]}`); } } const paramsString = params.length > 0 ? `(${params.join(',')})` : ''; - return `${this.path}${paramsString}`; + const outlet = this.outlet === PRIMARY_OUTLET ? '' : `${this.outlet}:`; + return `${outlet}${this.path}${paramsString}`; } } diff --git a/modules/@angular/router/test/url_serializer.spec.ts b/modules/@angular/router/test/url_serializer.spec.ts index f3298be503..6c695a0ee1 100644 --- a/modules/@angular/router/test/url_serializer.spec.ts +++ b/modules/@angular/router/test/url_serializer.spec.ts @@ -2,60 +2,61 @@ import {DefaultUrlSerializer, serializeSegment} from '../src/url_serializer'; import {UrlSegment} from '../src/url_tree'; describe('url serializer', () => { - let url = new DefaultUrlSerializer(); + const url = new DefaultUrlSerializer(); it('should parse the root url', () => { - let tree = url.parse("/"); + const tree = url.parse("/"); expectSegment(tree.root, ""); expect(url.serialize(tree)).toEqual(""); }); it('should parse non-empty urls', () => { - let tree = url.parse("one/two"); - let one = tree.firstChild(tree.root); + const tree = url.parse("one/two"); + const one = tree.firstChild(tree.root); + expectSegment(one, "one"); expectSegment(tree.firstChild(one), "two"); expect(url.serialize(tree)).toEqual("/one/two"); }); it("should parse multiple secondary segments", () => { - let tree = url.parse("/one/two(three//four)/five"); - let c = tree.children(tree.firstChild(tree.root)); + const tree = url.parse("/one/two(left:three//right:four)/five"); + const c = tree.children(tree.firstChild(tree.root)); expectSegment(c[0], "two"); - expectSegment(c[1], "three"); - expectSegment(c[2], "four"); + expectSegment(c[1], "left:three"); + expectSegment(c[2], "right:four"); expectSegment(tree.firstChild(c[0]), "five"); - expect(url.serialize(tree)).toEqual("/one/two(three//four)/five"); + expect(url.serialize(tree)).toEqual("/one/two(left:three//right:four)/five"); }); it("should parse secondary segments that have secondary segments", () => { - let tree = url.parse("/one(/two(/three))"); - let c = tree.children(tree.root); + const tree = url.parse("/one(left:two(right:three))"); + const c = tree.children(tree.root); expectSegment(c[0], "one"); - expectSegment(c[1], "two"); - expectSegment(c[2], "three"); + expectSegment(c[1], "left:two"); + expectSegment(c[2], "right:three"); - expect(url.serialize(tree)).toEqual("/one(two//three)"); + expect(url.serialize(tree)).toEqual("/one(left:two//right:three)"); }); it("should parse secondary segments that have children", () => { - let tree = url.parse("/one(/two/three)"); - let c = tree.children(tree.root); + const tree = url.parse("/one(left:two/three)"); + const c = tree.children(tree.root); expectSegment(c[0], "one"); - expectSegment(c[1], "two"); + expectSegment(c[1], "left:two"); expectSegment(tree.firstChild(c[1]), "three"); - expect(url.serialize(tree)).toEqual("/one(two/three)"); + expect(url.serialize(tree)).toEqual("/one(left:two/three)"); }); it("should parse an empty secondary segment group", () => { - let tree = url.parse("/one()"); - let c = tree.children(tree.root); + const tree = url.parse("/one()"); + const c = tree.children(tree.root); expectSegment(c[0], "one"); expect(tree.children(c[0]).length).toEqual(0); @@ -64,48 +65,48 @@ describe('url serializer', () => { }); it("should parse key-value matrix params", () => { - let tree = url.parse("/one;a=11a;b=11b(two;c=22//three;d=33)"); - let c = tree.children(tree.root); + const tree = url.parse("/one;a=11a;b=11b(left:two;c=22//right:three;d=33)"); + const c = tree.children(tree.root); expectSegment(c[0], "one;a=11a;b=11b"); - expectSegment(c[1], "two;c=22"); - expectSegment(c[2], "three;d=33"); + expectSegment(c[1], "left:two;c=22"); + expectSegment(c[2], "right:three;d=33"); - expect(url.serialize(tree)).toEqual("/one;a=11a;b=11b(two;c=22//three;d=33)"); + expect(url.serialize(tree)).toEqual("/one;a=11a;b=11b(left:two;c=22//right:three;d=33)"); }); it("should parse key only matrix params", () => { - let tree = url.parse("/one;a"); + const tree = url.parse("/one;a"); - let c = tree.firstChild(tree.root); + const c = tree.firstChild(tree.root); expectSegment(c, "one;a=true"); expect(url.serialize(tree)).toEqual("/one;a=true"); }); it("should parse query params", () => { - let tree = url.parse("/one?a=1&b=2"); + const tree = url.parse("/one?a=1&b=2"); expect(tree.queryParameters).toEqual({a: '1', b: '2'}); }); it("should parse key only query params", () => { - let tree = url.parse("/one?a"); + const tree = url.parse("/one?a"); expect(tree.queryParameters).toEqual({a: 'true'}); }); it("should serializer query params", () => { - let tree = url.parse("/one?a"); + const tree = url.parse("/one?a"); expect(url.serialize(tree)).toEqual("/one?a=true"); }); it("should parse fragment", () => { - let tree = url.parse("/one#two"); + const tree = url.parse("/one#two"); expect(tree.fragment).toEqual("two"); expect(url.serialize(tree)).toEqual("/one#two"); }); it("should parse empty fragment", () => { - let tree = url.parse("/one#"); + const tree = url.parse("/one#"); expect(tree.fragment).toEqual(""); expect(url.serialize(tree)).toEqual("/one#"); });