Skip to content

Commit 446657b

Browse files
committed
feat(router): update recognize to handle matrix parameters
1 parent 79830f1 commit 446657b

File tree

3 files changed

+65
-19
lines changed

3 files changed

+65
-19
lines changed

modules/angular2/src/alt_router/recognize.ts

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,13 @@ function _constructSegment(componentResolver: ComponentResolver,
4545
matched: _MatchResult): Promise<TreeNode<RouteSegment>[]> {
4646
return componentResolver.resolveComponent(matched.route.component)
4747
.then(factory => {
48+
let urlOutlet = matched.consumedUrlSegments[0].outlet;
4849
let segment = new RouteSegment(matched.consumedUrlSegments, matched.parameters,
49-
matched.consumedUrlSegments[0].outlet,
50+
isBlank(urlOutlet) ? DEFAULT_OUTLET_NAME : urlOutlet,
5051
matched.route.component, factory);
5152

52-
if (isPresent(matched.leftOverUrl)) {
53-
return _recognize(componentResolver, matched.route.component, matched.leftOverUrl)
53+
if (matched.leftOverUrl.length > 0) {
54+
return _recognizeMany(componentResolver, matched.route.component, matched.leftOverUrl)
5455
.then(children => [new TreeNode<RouteSegment>(segment, children)]);
5556
} else {
5657
return [new TreeNode<RouteSegment>(segment, [])];
@@ -78,12 +79,13 @@ function _matchWithParts(route: RouteMetadata, url: TreeNode<UrlSegment>): _Matc
7879

7980
let current = url;
8081
for (let i = 0; i < parts.length; ++i) {
82+
if (isBlank(current)) return null;
83+
8184
let p = parts[i];
8285
let isLastSegment = i === parts.length - 1;
8386
let isLastParent = i === parts.length - 2;
8487
let isPosParam = p.startsWith(":");
8588

86-
if (isBlank(current)) return null;
8789
if (!isPosParam && p != current.value.segment) return null;
8890
if (isLastSegment) {
8991
lastSegment = current;
@@ -101,10 +103,18 @@ function _matchWithParts(route: RouteMetadata, url: TreeNode<UrlSegment>): _Matc
101103
current = ListWrapper.first(current.children);
102104
}
103105

104-
let parameters = <{[key: string]: string}>StringMapWrapper.merge(lastSegment.value.parameters,
105-
positionalParams);
106+
if (isPresent(current) && isBlank(current.value.segment)) {
107+
lastParent = lastSegment;
108+
lastSegment = current;
109+
}
110+
111+
let p = lastSegment.value.parameters;
112+
let parameters =
113+
<{[key: string]: string}>StringMapWrapper.merge(isBlank(p) ? {} : p, positionalParams);
106114
let axuUrlSubtrees = isPresent(lastParent) ? lastParent.children.slice(1) : [];
107-
return new _MatchResult(route, consumedUrlSegments, parameters, current, axuUrlSubtrees);
115+
116+
return new _MatchResult(route, consumedUrlSegments, parameters, lastSegment.children,
117+
axuUrlSubtrees);
108118
}
109119

110120
function _checkOutletNameUniqueness(nodes: TreeNode<RouteSegment>[]): TreeNode<RouteSegment>[] {
@@ -123,8 +133,8 @@ function _checkOutletNameUniqueness(nodes: TreeNode<RouteSegment>[]): TreeNode<R
123133

124134
class _MatchResult {
125135
constructor(public route: RouteMetadata, public consumedUrlSegments: UrlSegment[],
126-
public parameters: {[key: string]: string}, public leftOverUrl: TreeNode<UrlSegment>,
127-
public aux: TreeNode<UrlSegment>[]) {}
136+
public parameters: {[key: string]: string},
137+
public leftOverUrl: TreeNode<UrlSegment>[], public aux: TreeNode<UrlSegment>[]) {}
128138
}
129139

130140
function _readMetadata(componentType: Type) {

modules/angular2/src/alt_router/segments.ts

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import {ComponentFactory} from 'angular2/core';
22
import {StringMapWrapper, ListWrapper} from 'angular2/src/facade/collection';
3-
import {Type, isBlank, isPresent} from 'angular2/src/facade/lang';
4-
import {DEFAULT_OUTLET_NAME} from './constants';
3+
import {Type, isBlank, isPresent, stringify} from 'angular2/src/facade/lang';
54

65
export class Tree<T> {
76
/** @internal */
@@ -59,18 +58,21 @@ export class TreeNode<T> {
5958
}
6059

6160
export class UrlSegment {
62-
constructor(public segment: string, public parameters: {[key: string]: string},
61+
constructor(public segment: any, public parameters: {[key: string]: string},
6362
public outlet: string) {}
6463

6564
toString(): string {
66-
let outletPrefix = this.outlet == DEFAULT_OUTLET_NAME ? "" : `${this.outlet}:`;
67-
return `${outletPrefix}${this.segment}${_serializeParams(this.parameters)}`;
65+
let outletPrefix = isBlank(this.outlet) ? "" : `${this.outlet}:`;
66+
let segmentPrefix = isBlank(this.segment) ? "" : this.segment;
67+
return `${outletPrefix}${segmentPrefix}${_serializeParams(this.parameters)}`;
6868
}
6969
}
7070

7171
function _serializeParams(params: {[key: string]: string}): string {
7272
let res = "";
73-
StringMapWrapper.forEach(params, (v, k) => res += `;${k}=${v}`);
73+
if (isPresent(params)) {
74+
StringMapWrapper.forEach(params, (v, k) => res += `;${k}=${v}`);
75+
}
7476
return res;
7577
}
7678

@@ -94,10 +96,23 @@ export class RouteSegment {
9496
get stringifiedUrlSegments(): string { return this.urlSegments.map(s => s.toString()).join("/"); }
9597
}
9698

99+
export function serializeRouteSegmentTree(tree: Tree<RouteSegment>): string {
100+
return _serializeRouteSegmentTree(tree._root);
101+
}
102+
103+
function _serializeRouteSegmentTree(node: TreeNode<RouteSegment>): string {
104+
let v = node.value;
105+
let children = node.children.map(c => _serializeRouteSegmentTree(c)).join(", ");
106+
return `${v.outlet}:${v.stringifiedUrlSegments}(${stringify(v.type)}) [${children}]`;
107+
}
108+
97109
export function equalSegments(a: RouteSegment, b: RouteSegment): boolean {
98110
if (isBlank(a) && !isBlank(b)) return false;
99111
if (!isBlank(a) && isBlank(b)) return false;
100-
return a._type === b._type && StringMapWrapper.equals(a.parameters, b.parameters);
112+
if (a._type !== b._type) return false;
113+
if (isBlank(a.parameters) && !isBlank(b.parameters)) return false;
114+
if (!isBlank(a.parameters) && isBlank(b.parameters)) return false;
115+
return StringMapWrapper.equals(a.parameters, b.parameters);
101116
}
102117

103118
export function routeSegmentComponentFactory(a: RouteSegment): ComponentFactory {

modules/angular2/test/alt_router/recognize_spec.ts

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import {recognize} from 'angular2/src/alt_router/recognize';
1919
import {Routes, Route} from 'angular2/alt_router';
2020
import {provide, Component, ComponentResolver} from 'angular2/core';
2121
import {UrlSegment, Tree} from 'angular2/src/alt_router/segments';
22-
import {DefaultRouterUrlParser} from 'angular2/src/alt_router/router_url_parser';
22+
import {DefaultRouterUrlSerializer} from 'angular2/src/alt_router/router_url_serializer';
2323
import {DEFAULT_OUTLET_NAME} from 'angular2/src/alt_router/constants';
2424

2525
export function main() {
@@ -100,6 +100,23 @@ export function main() {
100100
});
101101
}));
102102

103+
it('should handle non top-level aux routes',
104+
inject([AsyncTestCompleter, ComponentResolver], (async, resolver) => {
105+
recognize(resolver, ComponentA, tree('b/paramB/d(e)'))
106+
.then(r => {
107+
let c = r.children(r.firstChild(r.root));
108+
expect(stringifyUrl(c[0].urlSegments)).toEqual(["d"]);
109+
expect(c[0].outlet).toEqual(DEFAULT_OUTLET_NAME);
110+
expect(c[0].type).toBe(ComponentD);
111+
112+
expect(stringifyUrl(c[1].urlSegments)).toEqual(["e"]);
113+
expect(c[1].outlet).toEqual("aux");
114+
expect(c[1].type).toBe(ComponentE);
115+
116+
async.done();
117+
});
118+
}));
119+
103120
it('should handle matrix parameters',
104121
inject([AsyncTestCompleter, ComponentResolver], (async, resolver) => {
105122
recognize(resolver, ComponentA, tree("b/paramB;b1=1;b2=2(/d;d1=1;d2=2)"))
@@ -143,7 +160,7 @@ export function main() {
143160
}
144161

145162
function tree(url: string): Tree<UrlSegment> {
146-
return new DefaultRouterUrlParser().parse(url);
163+
return new DefaultRouterUrlSerializer().parse(url);
147164
}
148165

149166
function stringifyUrl(segments: UrlSegment[]): string[] {
@@ -164,7 +181,11 @@ class ComponentC {
164181
}
165182

166183
@Component({selector: 'b', template: 't'})
167-
@Routes([new Route({path: "c/:c", component: ComponentC})])
184+
@Routes([
185+
new Route({path: "d", component: ComponentD}),
186+
new Route({path: "e", component: ComponentE}),
187+
new Route({path: "c/:c", component: ComponentC})
188+
])
168189
class ComponentB {
169190
}
170191

0 commit comments

Comments
 (0)