feat(router): add RouterUrlSerializer
This commit is contained in:
parent
6e1fed42b7
commit
79830f1c75
@ -1,11 +1,14 @@
|
|||||||
import {UrlSegment, Tree, TreeNode} from './segments';
|
import {UrlSegment, Tree, TreeNode, rootNode} from './segments';
|
||||||
import {BaseException} from 'angular2/src/facade/exceptions';
|
import {BaseException} from 'angular2/src/facade/exceptions';
|
||||||
import {isBlank, isPresent, RegExpWrapper} from 'angular2/src/facade/lang';
|
import {isBlank, isPresent, RegExpWrapper} from 'angular2/src/facade/lang';
|
||||||
import {DEFAULT_OUTLET_NAME} from './constants';
|
import {ListWrapper} from 'angular2/src/facade/collection';
|
||||||
|
|
||||||
export abstract class RouterUrlParser { abstract parse(url: string): Tree<UrlSegment>; }
|
export abstract class RouterUrlSerializer {
|
||||||
|
abstract parse(url: string): Tree<UrlSegment>;
|
||||||
|
abstract serialize(tree: Tree<UrlSegment>): string;
|
||||||
|
}
|
||||||
|
|
||||||
export class DefaultRouterUrlParser extends RouterUrlParser {
|
export class DefaultRouterUrlSerializer extends RouterUrlSerializer {
|
||||||
parse(url: string): Tree<UrlSegment> {
|
parse(url: string): Tree<UrlSegment> {
|
||||||
if (url.length === 0) {
|
if (url.length === 0) {
|
||||||
throw new BaseException(`Invalid url '${url}'`);
|
throw new BaseException(`Invalid url '${url}'`);
|
||||||
@ -13,6 +16,29 @@ export class DefaultRouterUrlParser extends RouterUrlParser {
|
|||||||
let root = new _UrlParser().parse(url);
|
let root = new _UrlParser().parse(url);
|
||||||
return new Tree<UrlSegment>(root);
|
return new Tree<UrlSegment>(root);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
serialize(tree: Tree<UrlSegment>): string { return _serializeUrlTreeNode(rootNode(tree)); }
|
||||||
|
}
|
||||||
|
|
||||||
|
function _serializeUrlTreeNode(node: TreeNode<UrlSegment>): string {
|
||||||
|
return `${node.value}${_serializeChildren(node)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _serializeUrlTreeNodes(nodes: TreeNode<UrlSegment>[]): string {
|
||||||
|
let main = nodes[0].value.toString();
|
||||||
|
let auxNodes = nodes.slice(1);
|
||||||
|
let aux = auxNodes.length > 0 ? `(${auxNodes.map(_serializeUrlTreeNode).join("//")})` : "";
|
||||||
|
let children = _serializeChildren(nodes[0]);
|
||||||
|
return `${main}${aux}${children}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _serializeChildren(node: TreeNode<UrlSegment>): string {
|
||||||
|
if (node.children.length > 0) {
|
||||||
|
let slash = isBlank(node.children[0].value.segment) ? "" : "/";
|
||||||
|
return `${slash}${_serializeUrlTreeNodes(node.children)}`;
|
||||||
|
} else {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var SEGMENT_RE = RegExpWrapper.create('^[^\\/\\(\\)\\?;=&#]+');
|
var SEGMENT_RE = RegExpWrapper.create('^[^\\/\\(\\)\\?;=&#]+');
|
||||||
@ -41,19 +67,19 @@ class _UrlParser {
|
|||||||
parse(url: string): TreeNode<UrlSegment> {
|
parse(url: string): TreeNode<UrlSegment> {
|
||||||
this._remaining = url;
|
this._remaining = url;
|
||||||
if (url == '' || url == '/') {
|
if (url == '' || url == '/') {
|
||||||
return new TreeNode<UrlSegment>(new UrlSegment('', {}, DEFAULT_OUTLET_NAME), []);
|
return new TreeNode<UrlSegment>(new UrlSegment('', {}, null), []);
|
||||||
} else {
|
} else {
|
||||||
return this.parseRoot();
|
return this.parseRoot();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
parseRoot(): TreeNode<UrlSegment> {
|
parseRoot(): TreeNode<UrlSegment> {
|
||||||
let segments = this.parseSegments(DEFAULT_OUTLET_NAME);
|
let segments = this.parseSegments();
|
||||||
let queryParams = this.peekStartsWith('?') ? this.parseQueryParams() : {};
|
let queryParams = this.peekStartsWith('?') ? this.parseQueryParams() : {};
|
||||||
return new TreeNode<UrlSegment>(new UrlSegment('', queryParams, DEFAULT_OUTLET_NAME), segments);
|
return new TreeNode<UrlSegment>(new UrlSegment('', queryParams, null), segments);
|
||||||
}
|
}
|
||||||
|
|
||||||
parseSegments(outletName: string): TreeNode<UrlSegment>[] {
|
parseSegments(outletName: string = null): TreeNode<UrlSegment>[] {
|
||||||
if (this._remaining.length == 0) {
|
if (this._remaining.length == 0) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
@ -70,7 +96,7 @@ class _UrlParser {
|
|||||||
path = parts[1];
|
path = parts[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
var matrixParams: {[key: string]: any} = {};
|
var matrixParams: {[key: string]: any} = null;
|
||||||
if (this.peekStartsWith(';')) {
|
if (this.peekStartsWith(';')) {
|
||||||
matrixParams = this.parseMatrixParams();
|
matrixParams = this.parseMatrixParams();
|
||||||
}
|
}
|
||||||
@ -83,12 +109,19 @@ class _UrlParser {
|
|||||||
var children: TreeNode<UrlSegment>[] = [];
|
var children: TreeNode<UrlSegment>[] = [];
|
||||||
if (this.peekStartsWith('/') && !this.peekStartsWith('//')) {
|
if (this.peekStartsWith('/') && !this.peekStartsWith('//')) {
|
||||||
this.capture('/');
|
this.capture('/');
|
||||||
children = this.parseSegments(DEFAULT_OUTLET_NAME);
|
children = this.parseSegments();
|
||||||
}
|
}
|
||||||
|
|
||||||
let segment = new UrlSegment(path, matrixParams, outletName);
|
if (isPresent(matrixParams)) {
|
||||||
let node = new TreeNode<UrlSegment>(segment, children);
|
let matrixParamsSegment = new UrlSegment(null, matrixParams, null);
|
||||||
return [node].concat(aux);
|
let matrixParamsNode = new TreeNode<UrlSegment>(matrixParamsSegment, children);
|
||||||
|
let segment = new UrlSegment(path, null, outletName);
|
||||||
|
return [new TreeNode<UrlSegment>(segment, [matrixParamsNode].concat(aux))];
|
||||||
|
} else {
|
||||||
|
let segment = new UrlSegment(path, null, outletName);
|
||||||
|
let node = new TreeNode<UrlSegment>(segment, children);
|
||||||
|
return [node].concat(aux);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
parseQueryParams(): {[key: string]: any} {
|
parseQueryParams(): {[key: string]: any} {
|
@ -1,116 +0,0 @@
|
|||||||
import {
|
|
||||||
ComponentFixture,
|
|
||||||
AsyncTestCompleter,
|
|
||||||
TestComponentBuilder,
|
|
||||||
beforeEach,
|
|
||||||
ddescribe,
|
|
||||||
xdescribe,
|
|
||||||
describe,
|
|
||||||
el,
|
|
||||||
expect,
|
|
||||||
iit,
|
|
||||||
inject,
|
|
||||||
beforeEachProviders,
|
|
||||||
it,
|
|
||||||
xit
|
|
||||||
} from 'angular2/testing_internal';
|
|
||||||
|
|
||||||
import {DefaultRouterUrlParser} from 'angular2/src/alt_router/router_url_parser';
|
|
||||||
import {UrlSegment} from 'angular2/src/alt_router/segments';
|
|
||||||
import {DEFAULT_OUTLET_NAME} from 'angular2/src/alt_router/constants';
|
|
||||||
|
|
||||||
export function main() {
|
|
||||||
describe('url parsing', () => {
|
|
||||||
let parser = new DefaultRouterUrlParser();
|
|
||||||
|
|
||||||
it('should throw on an empty urls', () => { expect(() => parser.parse("")).toThrow(); });
|
|
||||||
|
|
||||||
it('should parse the root url', () => {
|
|
||||||
let tree = parser.parse("/");
|
|
||||||
expectSegment(tree.root, "");
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should parse non-empty urls', () => {
|
|
||||||
let tree = parser.parse("one/two");
|
|
||||||
expectSegment(tree.firstChild(tree.root), "one");
|
|
||||||
expectSegment(tree.firstChild(tree.firstChild(tree.root)), "two");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should parse multiple aux routes", () => {
|
|
||||||
let tree = parser.parse("/one/two(/three//right:four)/five");
|
|
||||||
let c = tree.children(tree.firstChild(tree.root));
|
|
||||||
|
|
||||||
expectSegment(c[0], "two");
|
|
||||||
expectSegment(c[1], "aux:three");
|
|
||||||
expectSegment(c[2], "right:four");
|
|
||||||
|
|
||||||
expectSegment(tree.firstChild(c[0]), "five");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should parse aux routes that have aux routes", () => {
|
|
||||||
let tree = parser.parse("/one(/two(/three))");
|
|
||||||
let c = tree.children(tree.root);
|
|
||||||
|
|
||||||
expectSegment(c[0], "one");
|
|
||||||
expectSegment(c[1], "aux:two");
|
|
||||||
expectSegment(c[2], "aux:three");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should parse aux routes that have children", () => {
|
|
||||||
let tree = parser.parse("/one(/two/three)");
|
|
||||||
let c = tree.children(tree.root);
|
|
||||||
|
|
||||||
expectSegment(c[0], "one");
|
|
||||||
expectSegment(c[1], "aux:two");
|
|
||||||
expectSegment(tree.firstChild(c[1]), "three");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should parse an empty aux route definition", () => {
|
|
||||||
let tree = parser.parse("/one()");
|
|
||||||
let c = tree.children(tree.root);
|
|
||||||
|
|
||||||
expectSegment(c[0], "one");
|
|
||||||
expect(tree.children(c[0]).length).toEqual(0);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should parse key-value matrix params", () => {
|
|
||||||
let tree = parser.parse("/one;a=11a;b=11b(/two;c=22//right:three;d=33)");
|
|
||||||
|
|
||||||
let c = tree.children(tree.root);
|
|
||||||
expectSegment(c[0], "one;a=11a;b=11b");
|
|
||||||
expectSegment(c[1], "aux:two;c=22");
|
|
||||||
expectSegment(c[2], "right:three;d=33");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should parse key only matrix params", () => {
|
|
||||||
let tree = parser.parse("/one;a");
|
|
||||||
|
|
||||||
let c = tree.children(tree.root);
|
|
||||||
expectSegment(c[0], "one;a=true");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should parse key-value query params", () => {
|
|
||||||
let tree = parser.parse("/one?a=1&b=2");
|
|
||||||
expect(tree.root).toEqual(new UrlSegment("", {'a': '1', 'b': '2'}, DEFAULT_OUTLET_NAME));
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should parse key only query params", () => {
|
|
||||||
let tree = parser.parse("/one?a");
|
|
||||||
expect(tree.root).toEqual(new UrlSegment("", {'a': "true"}, DEFAULT_OUTLET_NAME));
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should parse a url with only query params", () => {
|
|
||||||
let tree = parser.parse("?a");
|
|
||||||
expect(tree.root).toEqual(new UrlSegment("", {'a': "true"}, DEFAULT_OUTLET_NAME));
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should allow slashes within query params", () => {
|
|
||||||
let tree = parser.parse("?a=http://boo");
|
|
||||||
expect(tree.root).toEqual(new UrlSegment("", {'a': "http://boo"}, DEFAULT_OUTLET_NAME));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function expectSegment(segment: UrlSegment, expected: string): void {
|
|
||||||
expect(segment.toString()).toEqual(expected);
|
|
||||||
}
|
|
136
modules/angular2/test/alt_router/router_url_serializer_spec.ts
Normal file
136
modules/angular2/test/alt_router/router_url_serializer_spec.ts
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
import {
|
||||||
|
ComponentFixture,
|
||||||
|
AsyncTestCompleter,
|
||||||
|
TestComponentBuilder,
|
||||||
|
beforeEach,
|
||||||
|
ddescribe,
|
||||||
|
xdescribe,
|
||||||
|
describe,
|
||||||
|
el,
|
||||||
|
expect,
|
||||||
|
iit,
|
||||||
|
inject,
|
||||||
|
beforeEachProviders,
|
||||||
|
it,
|
||||||
|
xit
|
||||||
|
} from 'angular2/testing_internal';
|
||||||
|
|
||||||
|
import {DefaultRouterUrlSerializer} from 'angular2/src/alt_router/router_url_serializer';
|
||||||
|
import {UrlSegment} from 'angular2/src/alt_router/segments';
|
||||||
|
|
||||||
|
export function main() {
|
||||||
|
describe('url parsing', () => {
|
||||||
|
let url = new DefaultRouterUrlSerializer();
|
||||||
|
|
||||||
|
it('should throw on an empty urls', () => { expect(() => url.parse("")).toThrow(); });
|
||||||
|
|
||||||
|
it('should parse the root url', () => {
|
||||||
|
let tree = url.parse("/");
|
||||||
|
expectSegment(tree.root, "");
|
||||||
|
expect(url.serialize(tree)).toEqual("");
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should parse non-empty urls', () => {
|
||||||
|
let tree = url.parse("one/two");
|
||||||
|
expectSegment(tree.firstChild(tree.root), "one");
|
||||||
|
expectSegment(tree.firstChild(tree.firstChild(tree.root)), "two");
|
||||||
|
expect(url.serialize(tree)).toEqual("/one/two");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should parse multiple aux routes", () => {
|
||||||
|
let tree = url.parse("/one/two(/three//right:four)/five");
|
||||||
|
let c = tree.children(tree.firstChild(tree.root));
|
||||||
|
|
||||||
|
expectSegment(c[0], "two");
|
||||||
|
expectSegment(c[1], "aux:three");
|
||||||
|
expectSegment(c[2], "right:four");
|
||||||
|
|
||||||
|
expectSegment(tree.firstChild(c[0]), "five");
|
||||||
|
|
||||||
|
expect(url.serialize(tree)).toEqual("/one/two(aux:three//right:four)/five");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should parse aux routes that have aux routes", () => {
|
||||||
|
let tree = url.parse("/one(/two(/three))");
|
||||||
|
let c = tree.children(tree.root);
|
||||||
|
|
||||||
|
expectSegment(c[0], "one");
|
||||||
|
expectSegment(c[1], "aux:two");
|
||||||
|
expectSegment(c[2], "aux:three");
|
||||||
|
|
||||||
|
expect(url.serialize(tree)).toEqual("/one(aux:two//aux:three)");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should parse aux routes that have children", () => {
|
||||||
|
let tree = url.parse("/one(/two/three)");
|
||||||
|
let c = tree.children(tree.root);
|
||||||
|
|
||||||
|
expectSegment(c[0], "one");
|
||||||
|
expectSegment(c[1], "aux:two");
|
||||||
|
expectSegment(tree.firstChild(c[1]), "three");
|
||||||
|
|
||||||
|
expect(url.serialize(tree)).toEqual("/one(aux:two/three)");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should parse an empty aux route definition", () => {
|
||||||
|
let tree = url.parse("/one()");
|
||||||
|
let c = tree.children(tree.root);
|
||||||
|
|
||||||
|
expectSegment(c[0], "one");
|
||||||
|
expect(tree.children(c[0]).length).toEqual(0);
|
||||||
|
|
||||||
|
expect(url.serialize(tree)).toEqual("/one");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should parse key-value matrix params", () => {
|
||||||
|
let tree = url.parse("/one;a=11a;b=11b(/two;c=22//right:three;d=33)");
|
||||||
|
|
||||||
|
let c = tree.firstChild(tree.root);
|
||||||
|
expectSegment(c, "one");
|
||||||
|
|
||||||
|
let c2 = tree.children(c);
|
||||||
|
expectSegment(c2[0], ";a=11a;b=11b");
|
||||||
|
expectSegment(c2[1], "aux:two");
|
||||||
|
expectSegment(c2[2], "right:three");
|
||||||
|
|
||||||
|
expectSegment(tree.firstChild(c2[1]), ";c=22");
|
||||||
|
expectSegment(tree.firstChild(c2[2]), ";d=33");
|
||||||
|
|
||||||
|
expect(url.serialize(tree)).toEqual("/one;a=11a;b=11b(aux:two;c=22//right:three;d=33)");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should parse key only matrix params", () => {
|
||||||
|
let tree = url.parse("/one;a");
|
||||||
|
|
||||||
|
let c = tree.firstChild(tree.root);
|
||||||
|
expectSegment(c, "one");
|
||||||
|
expectSegment(tree.firstChild(c), ";a=true");
|
||||||
|
|
||||||
|
expect(url.serialize(tree)).toEqual("/one;a=true");
|
||||||
|
});
|
||||||
|
|
||||||
|
// it("should parse key-value query params", () => {
|
||||||
|
// let tree = url.parse("/one?a=1&b=2");
|
||||||
|
// expect(tree.root).toEqual(new UrlSegment("", {'a': '1', 'b': '2'}, DEFAULT_OUTLET_NAME));
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// it("should parse key only query params", () => {
|
||||||
|
// let tree = url.parse("/one?a");
|
||||||
|
// expect(tree.root).toEqual(new UrlSegment("", {'a': "true"}, DEFAULT_OUTLET_NAME));
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// it("should parse a url with only query params", () => {
|
||||||
|
// let tree = url.parse("?a");
|
||||||
|
// expect(tree.root).toEqual(new UrlSegment("", {'a': "true"}, DEFAULT_OUTLET_NAME));
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// it("should allow slashes within query params", () => {
|
||||||
|
// let tree = url.parse("?a=http://boo");
|
||||||
|
// expect(tree.root).toEqual(new UrlSegment("", {'a': "http://boo"}, DEFAULT_OUTLET_NAME));
|
||||||
|
// });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function expectSegment(segment: UrlSegment, expected: string): void {
|
||||||
|
expect(segment.toString()).toEqual(expected);
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user