19
modules/angular2/src/router/helpers.ts
Normal file
19
modules/angular2/src/router/helpers.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import {isPresent} from 'angular2/src/facade/lang';
|
||||
|
||||
export function parseAndAssignParamString(splitToken: string, paramString: string,
|
||||
keyValueMap: StringMap<string, string>): void {
|
||||
var first = paramString[0];
|
||||
if (first == '?' || first == ';') {
|
||||
paramString = paramString.substring(1);
|
||||
}
|
||||
|
||||
paramString.split(splitToken)
|
||||
.forEach((entry) => {
|
||||
var tuple = entry.split('=');
|
||||
var key = tuple[0];
|
||||
if (!isPresent(keyValueMap[key])) {
|
||||
var value = tuple.length > 1 ? tuple[1] : true;
|
||||
keyValueMap[key] = value;
|
||||
}
|
||||
});
|
||||
}
|
@ -27,10 +27,9 @@ export class Instruction {
|
||||
reuse: boolean = false;
|
||||
specificity: number;
|
||||
|
||||
private _params: StringMap<string, string>;
|
||||
|
||||
constructor(public component: any, public capturedUrl: string,
|
||||
private _recognizer: PathRecognizer, public child: Instruction = null) {
|
||||
private _recognizer: PathRecognizer, public child: Instruction = null,
|
||||
private _params: StringMap<string, any> = null) {
|
||||
this.accumulatedUrl = capturedUrl;
|
||||
this.specificity = _recognizer.specificity;
|
||||
if (isPresent(child)) {
|
||||
|
@ -17,7 +17,7 @@ import {
|
||||
ListWrapper
|
||||
} from 'angular2/src/facade/collection';
|
||||
import {IMPLEMENTS} from 'angular2/src/facade/lang';
|
||||
|
||||
import {parseAndAssignParamString} from 'angular2/src/router/helpers';
|
||||
import {escapeRegex} from './url';
|
||||
import {RouteHandler} from './route_handler';
|
||||
|
||||
@ -63,19 +63,6 @@ function normalizeString(obj: any): string {
|
||||
}
|
||||
}
|
||||
|
||||
function parseAndAssignMatrixParams(keyValueMap, matrixString) {
|
||||
if (matrixString[0] == ';') {
|
||||
matrixString = matrixString.substring(1);
|
||||
}
|
||||
|
||||
matrixString.split(';').forEach((entry) => {
|
||||
var tuple = entry.split('=');
|
||||
var key = tuple[0];
|
||||
var value = tuple.length > 1 ? tuple[1] : true;
|
||||
keyValueMap[key] = value;
|
||||
});
|
||||
}
|
||||
|
||||
class ContinuationSegment extends Segment {}
|
||||
|
||||
class StaticSegment extends Segment {
|
||||
@ -198,7 +185,10 @@ export class PathRecognizer {
|
||||
specificity: number;
|
||||
terminal: boolean = true;
|
||||
|
||||
constructor(public path: string, public handler: RouteHandler) {
|
||||
static matrixRegex: RegExp = RegExpWrapper.create('^(.*\/[^\/]+?)(;[^\/]+)?\/?$');
|
||||
static queryRegex: RegExp = RegExpWrapper.create('^(.*\/[^\/]+?)(\\?[^\/]+)?$');
|
||||
|
||||
constructor(public path: string, public handler: RouteHandler, public isRoot: boolean = false) {
|
||||
assertPath(path);
|
||||
var parsed = parsePathString(path);
|
||||
var specificity = parsed['specificity'];
|
||||
@ -228,16 +218,16 @@ export class PathRecognizer {
|
||||
var containsStarSegment =
|
||||
segmentsLimit >= 0 && this.segments[segmentsLimit] instanceof StarSegment;
|
||||
|
||||
var matrixString;
|
||||
var paramsString, useQueryString = this.isRoot && this.terminal;
|
||||
if (!containsStarSegment) {
|
||||
var matches =
|
||||
RegExpWrapper.firstMatch(RegExpWrapper.create('^(.*\/[^\/]+?)(;[^\/]+)?\/?$'), url);
|
||||
var matches = RegExpWrapper.firstMatch(
|
||||
useQueryString ? PathRecognizer.queryRegex : PathRecognizer.matrixRegex, url);
|
||||
if (isPresent(matches)) {
|
||||
url = matches[1];
|
||||
matrixString = matches[2];
|
||||
paramsString = matches[2];
|
||||
}
|
||||
|
||||
url = StringWrapper.replaceAll(url, /(;[^\/]+)(?=(\/|\Z))/g, '');
|
||||
url = StringWrapper.replaceAll(url, /(;[^\/]+)(?=(\/|$))/g, '');
|
||||
}
|
||||
|
||||
var params = StringMapWrapper.create();
|
||||
@ -256,8 +246,11 @@ export class PathRecognizer {
|
||||
}
|
||||
}
|
||||
|
||||
if (isPresent(matrixString) && matrixString.length > 0 && matrixString[0] == ';') {
|
||||
parseAndAssignMatrixParams(params, matrixString);
|
||||
if (isPresent(paramsString) && paramsString.length > 0) {
|
||||
var expectedStartingValue = useQueryString ? '?' : ';';
|
||||
if (paramsString[0] == expectedStartingValue) {
|
||||
parseAndAssignParamString(expectedStartingValue, paramsString, params);
|
||||
}
|
||||
}
|
||||
|
||||
return params;
|
||||
@ -266,6 +259,7 @@ export class PathRecognizer {
|
||||
generate(params: StringMap<string, any>): string {
|
||||
var paramTokens = new TouchMap(params);
|
||||
var applyLeadingSlash = false;
|
||||
var useQueryString = this.isRoot && this.terminal;
|
||||
|
||||
var url = '';
|
||||
for (var i = 0; i < this.segments.length; i++) {
|
||||
@ -279,12 +273,23 @@ export class PathRecognizer {
|
||||
}
|
||||
|
||||
var unusedParams = paramTokens.getUnused();
|
||||
StringMapWrapper.forEach(unusedParams, (value, key) => {
|
||||
url += ';' + key;
|
||||
if (isPresent(value)) {
|
||||
url += '=' + value;
|
||||
}
|
||||
});
|
||||
if (!StringMapWrapper.isEmpty(unusedParams)) {
|
||||
url += useQueryString ? '?' : ';';
|
||||
var paramToken = useQueryString ? '&' : ';';
|
||||
var i = 0;
|
||||
StringMapWrapper.forEach(unusedParams, (value, key) => {
|
||||
if (i++ > 0) {
|
||||
url += paramToken;
|
||||
}
|
||||
url += key;
|
||||
if (!isPresent(value) && useQueryString) {
|
||||
value = 'true';
|
||||
}
|
||||
if (isPresent(value)) {
|
||||
url += '=' + value;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (applyLeadingSlash) {
|
||||
url += '/';
|
||||
|
@ -22,6 +22,7 @@ import {RouteHandler} from './route_handler';
|
||||
import {Route, AsyncRoute, Redirect, RouteDefinition} from './route_config_impl';
|
||||
import {AsyncRouteHandler} from './async_route_handler';
|
||||
import {SyncRouteHandler} from './sync_route_handler';
|
||||
import {parseAndAssignParamString} from 'angular2/src/router/helpers';
|
||||
|
||||
/**
|
||||
* `RouteRecognizer` is responsible for recognizing routes for a single component.
|
||||
@ -33,6 +34,8 @@ export class RouteRecognizer {
|
||||
redirects: Map<string, string> = new Map();
|
||||
matchers: Map<RegExp, PathRecognizer> = new Map();
|
||||
|
||||
constructor(public isRoot: boolean = false) {}
|
||||
|
||||
config(config: RouteDefinition): boolean {
|
||||
var handler;
|
||||
if (config instanceof Redirect) {
|
||||
@ -44,7 +47,7 @@ export class RouteRecognizer {
|
||||
} else if (config instanceof AsyncRoute) {
|
||||
handler = new AsyncRouteHandler(config.loader);
|
||||
}
|
||||
var recognizer = new PathRecognizer(config.path, handler);
|
||||
var recognizer = new PathRecognizer(config.path, handler, this.isRoot);
|
||||
MapWrapper.forEach(this.matchers, (matcher, _) => {
|
||||
if (recognizer.regex.toString() == matcher.regex.toString()) {
|
||||
throw new BaseException(
|
||||
@ -80,6 +83,17 @@ export class RouteRecognizer {
|
||||
}
|
||||
});
|
||||
|
||||
var queryParams = StringMapWrapper.create();
|
||||
var queryString = '';
|
||||
var queryIndex = url.indexOf('?');
|
||||
if (queryIndex >= 0) {
|
||||
queryString = url.substring(queryIndex + 1);
|
||||
url = url.substring(0, queryIndex);
|
||||
}
|
||||
if (this.isRoot && queryString.length > 0) {
|
||||
parseAndAssignParamString('&', queryString, queryParams);
|
||||
}
|
||||
|
||||
MapWrapper.forEach(this.matchers, (pathRecognizer, regex) => {
|
||||
var match;
|
||||
if (isPresent(match = RegExpWrapper.firstMatch(regex, url))) {
|
||||
@ -89,7 +103,12 @@ export class RouteRecognizer {
|
||||
matchedUrl = match[0];
|
||||
unmatchedUrl = url.substring(match[0].length);
|
||||
}
|
||||
solutions.push(new RouteMatch(pathRecognizer, matchedUrl, unmatchedUrl));
|
||||
var params = null;
|
||||
if (pathRecognizer.terminal && !StringMapWrapper.isEmpty(queryParams)) {
|
||||
params = queryParams;
|
||||
matchedUrl += '?' + queryString;
|
||||
}
|
||||
solutions.push(new RouteMatch(pathRecognizer, matchedUrl, unmatchedUrl, params));
|
||||
}
|
||||
});
|
||||
|
||||
@ -109,10 +128,22 @@ export class RouteRecognizer {
|
||||
}
|
||||
|
||||
export class RouteMatch {
|
||||
constructor(public recognizer: PathRecognizer, public matchedUrl: string,
|
||||
public unmatchedUrl: string) {}
|
||||
private _params: StringMap<string, any>;
|
||||
private _paramsParsed: boolean = false;
|
||||
|
||||
params(): StringMap<string, string> { return this.recognizer.parseParams(this.matchedUrl); }
|
||||
constructor(public recognizer: PathRecognizer, public matchedUrl: string,
|
||||
public unmatchedUrl: string, p: StringMap<string, any> = null) {
|
||||
this._params = isPresent(p) ? p : StringMapWrapper.create();
|
||||
}
|
||||
|
||||
params(): StringMap<string, any> {
|
||||
if (!this._paramsParsed) {
|
||||
this._paramsParsed = true;
|
||||
StringMapWrapper.forEach(this.recognizer.parseParams(this.matchedUrl),
|
||||
(value, key) => { StringMapWrapper.set(this._params, key, value); });
|
||||
}
|
||||
return this._params;
|
||||
}
|
||||
}
|
||||
|
||||
function configObjToHandler(config: any): RouteHandler {
|
||||
|
@ -37,13 +37,13 @@ export class RouteRegistry {
|
||||
/**
|
||||
* Given a component and a configuration object, add the route to this registry
|
||||
*/
|
||||
config(parentComponent: any, config: RouteDefinition): void {
|
||||
config(parentComponent: any, config: RouteDefinition, isRootLevelRoute: boolean = false): void {
|
||||
config = normalizeRouteConfig(config);
|
||||
|
||||
var recognizer: RouteRecognizer = this._rules.get(parentComponent);
|
||||
|
||||
if (isBlank(recognizer)) {
|
||||
recognizer = new RouteRecognizer();
|
||||
recognizer = new RouteRecognizer(isRootLevelRoute);
|
||||
this._rules.set(parentComponent, recognizer);
|
||||
}
|
||||
|
||||
@ -61,7 +61,7 @@ export class RouteRegistry {
|
||||
/**
|
||||
* Reads the annotations of a component and configures the registry based on them
|
||||
*/
|
||||
configFromComponent(component: any): void {
|
||||
configFromComponent(component: any, isRootComponent: boolean = false): void {
|
||||
if (!isType(component)) {
|
||||
return;
|
||||
}
|
||||
@ -77,7 +77,8 @@ export class RouteRegistry {
|
||||
var annotation = annotations[i];
|
||||
|
||||
if (annotation instanceof RouteConfig) {
|
||||
ListWrapper.forEach(annotation.configs, (config) => this.config(component, config));
|
||||
ListWrapper.forEach(annotation.configs,
|
||||
(config) => this.config(component, config, isRootComponent));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -120,7 +121,8 @@ export class RouteRegistry {
|
||||
|
||||
if (partialMatch.unmatchedUrl.length == 0) {
|
||||
if (recognizer.terminal) {
|
||||
return new Instruction(componentType, partialMatch.matchedUrl, recognizer);
|
||||
return new Instruction(componentType, partialMatch.matchedUrl, recognizer, null,
|
||||
partialMatch.params());
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
@ -87,8 +87,9 @@ export class Router {
|
||||
* ```
|
||||
*/
|
||||
config(definitions: List<RouteDefinition>): Promise<any> {
|
||||
definitions.forEach(
|
||||
(routeDefinition) => { this.registry.config(this.hostComponent, routeDefinition); });
|
||||
definitions.forEach((routeDefinition) => {
|
||||
this.registry.config(this.hostComponent, routeDefinition, this instanceof RootRouter);
|
||||
});
|
||||
return this.renavigate();
|
||||
}
|
||||
|
||||
@ -290,7 +291,7 @@ export class RootRouter extends Router {
|
||||
super(registry, pipeline, null, hostComponent);
|
||||
this._location = location;
|
||||
this._location.subscribe((change) => this.navigate(change['url']));
|
||||
this.registry.configFromComponent(hostComponent);
|
||||
this.registry.configFromComponent(hostComponent, true);
|
||||
this.navigate(location.path());
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user