feat(router): update recognize to handle matrix parameters

This commit is contained in:
vsavkin 2016-04-27 15:36:11 -07:00 committed by Victor Savkin
parent 79830f1c75
commit 446657bdd5
3 changed files with 65 additions and 19 deletions

View File

@ -45,12 +45,13 @@ function _constructSegment(componentResolver: ComponentResolver,
matched: _MatchResult): Promise<TreeNode<RouteSegment>[]> {
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<RouteSegment>(segment, children)]);
} else {
return [new TreeNode<RouteSegment>(segment, [])];
@ -78,12 +79,13 @@ function _matchWithParts(route: RouteMetadata, url: TreeNode<UrlSegment>): _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<UrlSegment>): _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<RouteSegment>[]): TreeNode<RouteSegment>[] {
@ -123,8 +133,8 @@ function _checkOutletNameUniqueness(nodes: TreeNode<RouteSegment>[]): TreeNode<R
class _MatchResult {
constructor(public route: RouteMetadata, public consumedUrlSegments: UrlSegment[],
public parameters: {[key: string]: string}, public leftOverUrl: TreeNode<UrlSegment>,
public aux: TreeNode<UrlSegment>[]) {}
public parameters: {[key: string]: string},
public leftOverUrl: TreeNode<UrlSegment>[], public aux: TreeNode<UrlSegment>[]) {}
}
function _readMetadata(componentType: Type) {

View File

@ -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<T> {
/** @internal */
@ -59,18 +58,21 @@ export class TreeNode<T> {
}
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<RouteSegment>): string {
return _serializeRouteSegmentTree(tree._root);
}
function _serializeRouteSegmentTree(node: TreeNode<RouteSegment>): 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 {

View File

@ -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<UrlSegment> {
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 {
}