diff --git a/modules/@angular/router/src/link.ts b/modules/@angular/router/src/link.ts index a22288fdf1..b03bc721d5 100644 --- a/modules/@angular/router/src/link.ts +++ b/modules/@angular/router/src/link.ts @@ -1,11 +1,10 @@ import {Tree, TreeNode, UrlSegment, RouteSegment, rootNode, UrlTree, RouteTree} from './segments'; import {isBlank, isPresent, isString, isStringMap} from './facade/lang'; import {BaseException} from './facade/exceptions'; -import {ListWrapper} from './facade/collection'; +import {ListWrapper, StringMapWrapper} from './facade/collection'; // TODO: vsavkin: should reuse segments -export function link(segment: RouteSegment, routeTree: RouteTree, urlTree: UrlTree, - commands: any[]): UrlTree { +export function link(segment: RouteSegment, routeTree: RouteTree, urlTree: UrlTree, commands: any[]): UrlTree { if (commands.length === 0) return urlTree; let normalizedCommands = _normalizeCommands(commands); @@ -14,22 +13,20 @@ export function link(segment: RouteSegment, routeTree: RouteTree, urlTree: UrlTr } let startingNode = _findStartingNode(normalizedCommands, urlTree, segment, routeTree); - let updated = - normalizedCommands.commands.length > 0 ? - _updateMany(ListWrapper.clone(startingNode.children), normalizedCommands.commands) : - []; + let updated = normalizedCommands.commands.length > 0 ? + _updateMany(ListWrapper.clone(startingNode.children), normalizedCommands.commands) : []; let newRoot = _constructNewTree(rootNode(urlTree), startingNode, updated); return new UrlTree(newRoot); } -function _navigateToRoot(normalizedChange: _NormalizedNavigationCommands): boolean { - return normalizedChange.isAbsolute && normalizedChange.commands.length === 1 && - normalizedChange.commands[0] == "/"; +function _navigateToRoot(normalizedChange:_NormalizedNavigationCommands):boolean { + return normalizedChange.isAbsolute && normalizedChange.commands.length === 1 && normalizedChange.commands[0] == "/"; } class _NormalizedNavigationCommands { - constructor(public isAbsolute: boolean, public numberOfDoubleDots: number, + constructor(public isAbsolute: boolean, + public numberOfDoubleDots: number, public commands: any[]) {} } @@ -56,18 +53,18 @@ function _normalizeCommands(commands: any[]): _NormalizedNavigationCommands { // first exp is treated in a special way if (i == 0) { - if (j == 0 && cc == ".") { // './a' + if (j == 0 && cc == ".") { // './a' // skip it - } else if (j == 0 && cc == "") { // '/a' + } else if (j == 0 && cc == "") { // '/a' isAbsolute = true; - } else if (cc == "..") { // '../a' + } else if (cc == "..") { // '../a' numberOfDoubleDots++; } else if (cc != '') { res.push(cc); } } else { - if (cc != '') { + if (cc != ''){ res.push(cc); } } @@ -77,8 +74,7 @@ function _normalizeCommands(commands: any[]): _NormalizedNavigationCommands { return new _NormalizedNavigationCommands(isAbsolute, numberOfDoubleDots, res); } -function _findUrlSegment(segment: RouteSegment, routeTree: RouteTree, urlTree: UrlTree, - numberOfDoubleDots: number): UrlSegment { +function _findUrlSegment(segment: RouteSegment, routeTree: RouteTree, urlTree: UrlTree, numberOfDoubleDots: number): UrlSegment { let s = segment; while (s.urlSegments.length === 0) { s = routeTree.parent(s); @@ -91,13 +87,11 @@ function _findUrlSegment(segment: RouteSegment, routeTree: RouteTree, urlTree: U return path[path.length - 1 - numberOfDoubleDots]; } -function _findStartingNode(normalizedChange: _NormalizedNavigationCommands, urlTree: UrlTree, - segment: RouteSegment, routeTree: RouteTree): TreeNode { +function _findStartingNode(normalizedChange:_NormalizedNavigationCommands, urlTree:UrlTree, segment:RouteSegment, routeTree:RouteTree):TreeNode { if (normalizedChange.isAbsolute) { return rootNode(urlTree); } else { - let urlSegment = - _findUrlSegment(segment, routeTree, urlTree, normalizedChange.numberOfDoubleDots); + let urlSegment = _findUrlSegment(segment, routeTree, urlTree, normalizedChange.numberOfDoubleDots); return _findMatchingNode(urlSegment, rootNode(urlTree)); } } @@ -117,7 +111,7 @@ function _constructNewTree(node: TreeNode, original: TreeNode(node.value, updated); } else { return new TreeNode( - node.value, node.children.map(c => _constructNewTree(c, original, updated))); + node.value, node.children.map(c => _constructNewTree(c, original, updated))); } } @@ -134,7 +128,7 @@ function _update(node: TreeNode, commands: any[]): TreeNode(urlSegment, children); } else if (isBlank(node) && isStringMap(next)) { - let urlSegment = new UrlSegment(segment, next, outlet); + let urlSegment = new UrlSegment(segment, _stringify(next), outlet); return _recurse(urlSegment, node, rest.slice(1)); // different outlet => preserve the subtree @@ -143,23 +137,40 @@ function _update(node: TreeNode, commands: any[]): TreeNode, - rest: any[]): TreeNode { +function _stringify(params: {[key: string]: any}):{[key: string]: string} { + let res = {}; + StringMapWrapper.forEach(params, (v, k) => res[k] = v.toString()); + return res; +} + +function _compare(path: string, params: {[key: string]: any}, segment: UrlSegment): boolean { + return path == segment.segment && StringMapWrapper.equals(params, segment.parameters); +} + +function _recurse(urlSegment: UrlSegment, node: TreeNode, rest: any[]): TreeNode { if (rest.length === 0) { return new TreeNode(urlSegment, []); } diff --git a/modules/@angular/router/src/segments.ts b/modules/@angular/router/src/segments.ts index 15f2c46725..40c2a1d595 100644 --- a/modules/@angular/router/src/segments.ts +++ b/modules/@angular/router/src/segments.ts @@ -1,6 +1,6 @@ import {ComponentFactory, Type} from '@angular/core'; import {StringMapWrapper, ListWrapper} from './facade/collection'; -import {isBlank, isPresent, stringify} from './facade/lang'; +import {isBlank, isPresent, stringify, NumberWrapper} from './facade/lang'; export class Tree { /** @internal */ @@ -80,8 +80,8 @@ function _contains(tree: TreeNode, subtree: TreeNode): boolean { } function _equalValues(a: any, b: any): boolean { - if (a instanceof RouteSegment) return equalSegments(a, b); - if (a instanceof UrlSegment) return equalUrlSegments(a, b); + // if (a instanceof RouteSegment) return equalSegments(a, b); + // if (a instanceof UrlSegment) return equalUrlSegments(a, b); return a === b; } @@ -90,8 +90,8 @@ export class TreeNode { } export class UrlSegment { - constructor(public segment: any, public parameters: {[key: string]: any}, public outlet: string) { - } + constructor(public segment: any, public parameters: {[key: string]: string}, + public outlet: string) {} toString(): string { let outletPrefix = isBlank(this.outlet) ? "" : `${this.outlet}:`; @@ -112,7 +112,7 @@ export class RouteSegment { /** @internal */ _componentFactory: ComponentFactory; - constructor(public urlSegments: UrlSegment[], public parameters: {[key: string]: any}, + constructor(public urlSegments: UrlSegment[], public parameters: {[key: string]: string}, public outlet: string, type: Type, componentFactory: ComponentFactory) { this._type = type; this._componentFactory = componentFactory; @@ -122,6 +122,10 @@ export class RouteSegment { return isPresent(this.parameters) ? this.parameters[param] : null; } + getParamAsNumber(param: string): number { + return isPresent(this.parameters) ? NumberWrapper.parseFloat(this.parameters[param]) : null; + } + get type(): Type { return this._type; } get stringifiedUrlSegments(): string { return this.urlSegments.map(s => s.toString()).join("/"); } diff --git a/modules/@angular/router/test/link_spec.ts b/modules/@angular/router/test/link_spec.ts index cb9c2ee7e8..8d85b5aee4 100644 --- a/modules/@angular/router/test/link_spec.ts +++ b/modules/@angular/router/test/link_spec.ts @@ -69,6 +69,34 @@ export function main() { expect(parser.serialize(t)).toEqual("/a/b;aa=22;bb=33"); }); + describe("node reuse", () => { + it('should reuse nodes when path is the same', () => { + let p = parser.parse("/a/b"); + let tree = s(p.root); + let t = link(tree.root, tree, p, ['/a/c']); + + expect(t.root).toBe(p.root); + expect(t.firstChild(t.root)).toBe(p.firstChild(p.root)); + expect(t.firstChild(t.firstChild(t.root))).not.toBe(p.firstChild(p.firstChild(p.root))); + }); + + it("should create new node when params are the same", () => { + let p = parser.parse("/a;x=1"); + let tree = s(p.root); + let t = link(tree.root, tree, p, ['/a', {'x': 1}]); + + expect(t.firstChild(t.root)).toBe(p.firstChild(p.root)); + }); + + it("should create new node when params are different", () => { + let p = parser.parse("/a;x=1"); + let tree = s(p.root); + let t = link(tree.root, tree, p, ['/a', {'x': 2}]); + + expect(t.firstChild(t.root)).not.toBe(p.firstChild(p.root)); + }); + }); + describe("relative navigation", () => { it("should work", () => { let p = parser.parse("/a(ap)/c(cp)"); @@ -155,9 +183,9 @@ export function main() { let p = parser.parse("/a(ap)/c(cp)"); let c = p.firstChild(p.root); - let child = new RouteSegment([], {'one': 1}, null, null, null); + let child = new RouteSegment([], {'one':'1'}, null, null, null); let root = new TreeNode(new RouteSegment([c], {}, null, null, null), - [new TreeNode(child, [])]); + [new TreeNode(child, [])]); let tree = new RouteTree(root); let t = link(child, tree, p, ["./c2"]); diff --git a/modules/@angular/router/test/recognize_spec.ts b/modules/@angular/router/test/recognize_spec.ts index 06d2cb59fa..1c2bf2bf57 100644 --- a/modules/@angular/router/test/recognize_spec.ts +++ b/modules/@angular/router/test/recognize_spec.ts @@ -20,7 +20,7 @@ import {DefaultRouterUrlSerializer} from '../src/router_url_serializer'; import {DEFAULT_OUTLET_NAME} from '../src/constants'; export function main() { - describe('recognize', () => { + ddescribe('recognize', () => { it('should handle position args', inject([AsyncTestCompleter, ComponentResolver], (async, resolver) => { recognize(resolver, ComponentA, tree("b/paramB/c/paramC/d"))