From 446657bdd5039169660cf9e2b77c350a849fa044 Mon Sep 17 00:00:00 2001 From: vsavkin Date: Wed, 27 Apr 2016 15:36:11 -0700 Subject: [PATCH] feat(router): update recognize to handle matrix parameters --- modules/angular2/src/alt_router/recognize.ts | 28 ++++++++++++------ modules/angular2/src/alt_router/segments.ts | 29 ++++++++++++++----- .../test/alt_router/recognize_spec.ts | 27 +++++++++++++++-- 3 files changed, 65 insertions(+), 19 deletions(-) diff --git a/modules/angular2/src/alt_router/recognize.ts b/modules/angular2/src/alt_router/recognize.ts index 61de5d2fe8..5adb71fd15 100644 --- a/modules/angular2/src/alt_router/recognize.ts +++ b/modules/angular2/src/alt_router/recognize.ts @@ -45,12 +45,13 @@ function _constructSegment(componentResolver: ComponentResolver, matched: _MatchResult): Promise[]> { return componentResolver.resolveComponent(matched.route.component) .then(factory => { + let urlOutlet = matched.consumedUrlSegments[0].outlet; let segment = new RouteSegment(matched.consumedUrlSegments, matched.parameters, - matched.consumedUrlSegments[0].outlet, + isBlank(urlOutlet) ? DEFAULT_OUTLET_NAME : urlOutlet, matched.route.component, factory); - if (isPresent(matched.leftOverUrl)) { - return _recognize(componentResolver, matched.route.component, matched.leftOverUrl) + if (matched.leftOverUrl.length > 0) { + return _recognizeMany(componentResolver, matched.route.component, matched.leftOverUrl) .then(children => [new TreeNode(segment, children)]); } else { return [new TreeNode(segment, [])]; @@ -78,12 +79,13 @@ function _matchWithParts(route: RouteMetadata, url: TreeNode): _Matc let current = url; for (let i = 0; i < parts.length; ++i) { + if (isBlank(current)) return null; + let p = parts[i]; let isLastSegment = i === parts.length - 1; let isLastParent = i === parts.length - 2; let isPosParam = p.startsWith(":"); - if (isBlank(current)) return null; if (!isPosParam && p != current.value.segment) return null; if (isLastSegment) { lastSegment = current; @@ -101,10 +103,18 @@ function _matchWithParts(route: RouteMetadata, url: TreeNode): _Matc current = ListWrapper.first(current.children); } - let parameters = <{[key: string]: string}>StringMapWrapper.merge(lastSegment.value.parameters, - positionalParams); + if (isPresent(current) && isBlank(current.value.segment)) { + lastParent = lastSegment; + lastSegment = current; + } + + let p = lastSegment.value.parameters; + let parameters = + <{[key: string]: string}>StringMapWrapper.merge(isBlank(p) ? {} : p, positionalParams); let axuUrlSubtrees = isPresent(lastParent) ? lastParent.children.slice(1) : []; - return new _MatchResult(route, consumedUrlSegments, parameters, current, axuUrlSubtrees); + + return new _MatchResult(route, consumedUrlSegments, parameters, lastSegment.children, + axuUrlSubtrees); } function _checkOutletNameUniqueness(nodes: TreeNode[]): TreeNode[] { @@ -123,8 +133,8 @@ function _checkOutletNameUniqueness(nodes: TreeNode[]): TreeNode, - public aux: TreeNode[]) {} + public parameters: {[key: string]: string}, + public leftOverUrl: TreeNode[], public aux: TreeNode[]) {} } function _readMetadata(componentType: Type) { diff --git a/modules/angular2/src/alt_router/segments.ts b/modules/angular2/src/alt_router/segments.ts index 4d39f4a851..0559e58dd1 100644 --- a/modules/angular2/src/alt_router/segments.ts +++ b/modules/angular2/src/alt_router/segments.ts @@ -1,7 +1,6 @@ import {ComponentFactory} from 'angular2/core'; import {StringMapWrapper, ListWrapper} from 'angular2/src/facade/collection'; -import {Type, isBlank, isPresent} from 'angular2/src/facade/lang'; -import {DEFAULT_OUTLET_NAME} from './constants'; +import {Type, isBlank, isPresent, stringify} from 'angular2/src/facade/lang'; export class Tree { /** @internal */ @@ -59,18 +58,21 @@ export class TreeNode { } export class UrlSegment { - constructor(public segment: string, public parameters: {[key: string]: string}, + constructor(public segment: any, public parameters: {[key: string]: string}, public outlet: string) {} toString(): string { - let outletPrefix = this.outlet == DEFAULT_OUTLET_NAME ? "" : `${this.outlet}:`; - return `${outletPrefix}${this.segment}${_serializeParams(this.parameters)}`; + let outletPrefix = isBlank(this.outlet) ? "" : `${this.outlet}:`; + let segmentPrefix = isBlank(this.segment) ? "" : this.segment; + return `${outletPrefix}${segmentPrefix}${_serializeParams(this.parameters)}`; } } function _serializeParams(params: {[key: string]: string}): string { let res = ""; - StringMapWrapper.forEach(params, (v, k) => res += `;${k}=${v}`); + if (isPresent(params)) { + StringMapWrapper.forEach(params, (v, k) => res += `;${k}=${v}`); + } return res; } @@ -94,10 +96,23 @@ export class RouteSegment { get stringifiedUrlSegments(): string { return this.urlSegments.map(s => s.toString()).join("/"); } } +export function serializeRouteSegmentTree(tree: Tree): string { + return _serializeRouteSegmentTree(tree._root); +} + +function _serializeRouteSegmentTree(node: TreeNode): string { + let v = node.value; + let children = node.children.map(c => _serializeRouteSegmentTree(c)).join(", "); + return `${v.outlet}:${v.stringifiedUrlSegments}(${stringify(v.type)}) [${children}]`; +} + export function equalSegments(a: RouteSegment, b: RouteSegment): boolean { if (isBlank(a) && !isBlank(b)) return false; if (!isBlank(a) && isBlank(b)) return false; - return a._type === b._type && StringMapWrapper.equals(a.parameters, b.parameters); + if (a._type !== b._type) return false; + if (isBlank(a.parameters) && !isBlank(b.parameters)) return false; + if (!isBlank(a.parameters) && isBlank(b.parameters)) return false; + return StringMapWrapper.equals(a.parameters, b.parameters); } export function routeSegmentComponentFactory(a: RouteSegment): ComponentFactory { diff --git a/modules/angular2/test/alt_router/recognize_spec.ts b/modules/angular2/test/alt_router/recognize_spec.ts index d161a7f75c..454d8958d2 100644 --- a/modules/angular2/test/alt_router/recognize_spec.ts +++ b/modules/angular2/test/alt_router/recognize_spec.ts @@ -19,7 +19,7 @@ import {recognize} from 'angular2/src/alt_router/recognize'; import {Routes, Route} from 'angular2/alt_router'; import {provide, Component, ComponentResolver} from 'angular2/core'; import {UrlSegment, Tree} from 'angular2/src/alt_router/segments'; -import {DefaultRouterUrlParser} from 'angular2/src/alt_router/router_url_parser'; +import {DefaultRouterUrlSerializer} from 'angular2/src/alt_router/router_url_serializer'; import {DEFAULT_OUTLET_NAME} from 'angular2/src/alt_router/constants'; export function main() { @@ -100,6 +100,23 @@ export function main() { }); })); + it('should handle non top-level aux routes', + inject([AsyncTestCompleter, ComponentResolver], (async, resolver) => { + recognize(resolver, ComponentA, tree('b/paramB/d(e)')) + .then(r => { + let c = r.children(r.firstChild(r.root)); + expect(stringifyUrl(c[0].urlSegments)).toEqual(["d"]); + expect(c[0].outlet).toEqual(DEFAULT_OUTLET_NAME); + expect(c[0].type).toBe(ComponentD); + + expect(stringifyUrl(c[1].urlSegments)).toEqual(["e"]); + expect(c[1].outlet).toEqual("aux"); + expect(c[1].type).toBe(ComponentE); + + async.done(); + }); + })); + it('should handle matrix parameters', inject([AsyncTestCompleter, ComponentResolver], (async, resolver) => { recognize(resolver, ComponentA, tree("b/paramB;b1=1;b2=2(/d;d1=1;d2=2)")) @@ -143,7 +160,7 @@ export function main() { } function tree(url: string): Tree { - return new DefaultRouterUrlParser().parse(url); + return new DefaultRouterUrlSerializer().parse(url); } function stringifyUrl(segments: UrlSegment[]): string[] { @@ -164,7 +181,11 @@ class ComponentC { } @Component({selector: 'b', template: 't'}) -@Routes([new Route({path: "c/:c", component: ComponentC})]) +@Routes([ + new Route({path: "d", component: ComponentD}), + new Route({path: "e", component: ComponentE}), + new Route({path: "c/:c", component: ComponentC}) +]) class ComponentB { }