2015-10-02 16:47:54 -07:00
|
|
|
import {Map, MapWrapper, StringMapWrapper, ListWrapper} from 'angular2/src/core/facade/collection';
|
2015-10-06 06:53:39 -07:00
|
|
|
import {unimplemented} from 'angular2/src/core/facade/exceptions';
|
2015-09-30 14:48:58 +02:00
|
|
|
import {isPresent, isBlank, normalizeBlank, Type, CONST_EXPR} from 'angular2/src/core/facade/lang';
|
2015-08-20 14:28:25 -07:00
|
|
|
import {Promise} from 'angular2/src/core/facade/async';
|
2015-06-30 13:18:51 -07:00
|
|
|
|
|
|
|
import {PathRecognizer} from './path_recognizer';
|
2015-07-17 13:36:53 -07:00
|
|
|
import {Url} from './url_parser';
|
2015-04-17 09:59:56 -07:00
|
|
|
|
2015-09-21 19:06:53 -07:00
|
|
|
/**
|
|
|
|
* `RouteParams` is an immutable map of parameters for the given route
|
|
|
|
* based on the url matcher and optional parameters for that route.
|
|
|
|
*
|
|
|
|
* You can inject `RouteParams` into the constructor of a component to use it.
|
|
|
|
*
|
2015-10-19 15:37:32 +01:00
|
|
|
* ### Example
|
2015-09-21 19:06:53 -07:00
|
|
|
*
|
|
|
|
* ```
|
2015-10-11 07:41:19 -07:00
|
|
|
* import {bootstrap, Component} from 'angular2/angular2';
|
2015-10-10 22:11:13 -07:00
|
|
|
* import {Router, ROUTER_DIRECTIVES, ROUTER_PROVIDERS, RouteConfig} from 'angular2/router';
|
2015-09-21 19:06:53 -07:00
|
|
|
*
|
2015-10-11 07:41:19 -07:00
|
|
|
* @Component({directives: [ROUTER_DIRECTIVES]})
|
2015-09-21 19:06:53 -07:00
|
|
|
* @RouteConfig([
|
|
|
|
* {path: '/user/:id', component: UserCmp, as: 'UserCmp'},
|
|
|
|
* ])
|
|
|
|
* class AppCmp {}
|
|
|
|
*
|
2015-10-11 07:41:19 -07:00
|
|
|
* @Component({ template: 'user: {{id}}' })
|
2015-09-21 19:06:53 -07:00
|
|
|
* class UserCmp {
|
2015-10-23 11:02:02 +02:00
|
|
|
* id: string;
|
2015-09-21 19:06:53 -07:00
|
|
|
* constructor(params: RouteParams) {
|
|
|
|
* this.id = params.get('id');
|
|
|
|
* }
|
|
|
|
* }
|
|
|
|
*
|
2015-10-10 22:11:13 -07:00
|
|
|
* bootstrap(AppCmp, ROUTER_PROVIDERS);
|
2015-09-21 19:06:53 -07:00
|
|
|
* ```
|
|
|
|
*/
|
2015-04-17 09:59:56 -07:00
|
|
|
export class RouteParams {
|
2015-10-02 16:47:54 -07:00
|
|
|
constructor(public params: {[key: string]: string}) {}
|
2015-05-14 15:24:35 +02:00
|
|
|
|
2015-05-29 14:58:41 -07:00
|
|
|
get(param: string): string { return normalizeBlank(StringMapWrapper.get(this.params, param)); }
|
2015-04-17 09:59:56 -07:00
|
|
|
}
|
|
|
|
|
2015-09-30 14:48:58 +02:00
|
|
|
/**
|
|
|
|
* `RouteData` is an immutable map of additional data you can configure in your {@link Route}.
|
|
|
|
*
|
|
|
|
* You can inject `RouteData` into the constructor of a component to use it.
|
|
|
|
*
|
|
|
|
* ## Example
|
|
|
|
*
|
|
|
|
* ```
|
|
|
|
* import {bootstrap, Component, View} from 'angular2/angular2';
|
|
|
|
* import {Router, ROUTER_DIRECTIVES, routerBindings, RouteConfig} from 'angular2/router';
|
|
|
|
*
|
|
|
|
* @Component({...})
|
|
|
|
* @View({directives: [ROUTER_DIRECTIVES]})
|
|
|
|
* @RouteConfig([
|
|
|
|
* {path: '/user/:id', component: UserCmp, as: 'UserCmp', data: {isAdmin: true}},
|
|
|
|
* ])
|
|
|
|
* class AppCmp {}
|
|
|
|
*
|
|
|
|
* @Component({...})
|
|
|
|
* @View({ template: 'user: {{isAdmin}}' })
|
|
|
|
* class UserCmp {
|
|
|
|
* string: isAdmin;
|
|
|
|
* constructor(data: RouteData) {
|
|
|
|
* this.isAdmin = data.get('isAdmin');
|
|
|
|
* }
|
|
|
|
* }
|
|
|
|
*
|
|
|
|
* bootstrap(AppCmp, routerBindings(AppCmp));
|
|
|
|
* ```
|
|
|
|
*/
|
|
|
|
export class RouteData {
|
|
|
|
constructor(public data: {[key: string]: any} = CONST_EXPR({})) {}
|
|
|
|
|
|
|
|
get(key: string): any { return normalizeBlank(StringMapWrapper.get(this.data, key)); }
|
|
|
|
}
|
|
|
|
|
|
|
|
var BLANK_ROUTE_DATA = new RouteData();
|
|
|
|
|
2015-05-15 02:05:57 -07:00
|
|
|
/**
|
2015-09-21 19:06:53 -07:00
|
|
|
* `Instruction` is a tree of {@link ComponentInstruction}s with all the information needed
|
2015-07-17 13:36:53 -07:00
|
|
|
* to transition each component in the app to a given route, including all auxiliary routes.
|
|
|
|
*
|
2015-09-21 19:06:53 -07:00
|
|
|
* `Instruction`s can be created using {@link Router#generate}, and can be used to
|
|
|
|
* perform route changes with {@link Router#navigateByInstruction}.
|
|
|
|
*
|
2015-10-19 15:37:32 +01:00
|
|
|
* ### Example
|
2015-09-21 19:06:53 -07:00
|
|
|
*
|
|
|
|
* ```
|
2015-10-11 07:41:19 -07:00
|
|
|
* import {bootstrap, Component} from 'angular2/angular2';
|
2015-10-10 22:11:13 -07:00
|
|
|
* import {Router, ROUTER_DIRECTIVES, ROUTER_PROVIDERS, RouteConfig} from 'angular2/router';
|
2015-09-21 19:06:53 -07:00
|
|
|
*
|
2015-10-11 07:41:19 -07:00
|
|
|
* @Component({directives: [ROUTER_DIRECTIVES]})
|
2015-09-21 19:06:53 -07:00
|
|
|
* @RouteConfig([
|
|
|
|
* {...},
|
|
|
|
* ])
|
|
|
|
* class AppCmp {
|
|
|
|
* constructor(router: Router) {
|
|
|
|
* var instruction = router.generate(['/MyRoute']);
|
|
|
|
* router.navigateByInstruction(instruction);
|
|
|
|
* }
|
|
|
|
* }
|
|
|
|
*
|
2015-10-10 22:11:13 -07:00
|
|
|
* bootstrap(AppCmp, ROUTER_PROVIDERS);
|
2015-09-21 19:06:53 -07:00
|
|
|
* ```
|
2015-05-15 02:05:57 -07:00
|
|
|
*/
|
2015-04-17 09:59:56 -07:00
|
|
|
export class Instruction {
|
2015-07-17 13:36:53 -07:00
|
|
|
constructor(public component: ComponentInstruction, public child: Instruction,
|
2015-10-02 16:47:54 -07:00
|
|
|
public auxInstruction: {[key: string]: Instruction}) {}
|
2015-07-17 13:36:53 -07:00
|
|
|
|
2015-09-21 19:06:53 -07:00
|
|
|
/**
|
|
|
|
* Returns a new instruction that shares the state of the existing instruction, but with
|
|
|
|
* the given child {@link Instruction} replacing the existing child.
|
|
|
|
*/
|
2015-07-17 13:36:53 -07:00
|
|
|
replaceChild(child: Instruction): Instruction {
|
|
|
|
return new Instruction(this.component, child, this.auxInstruction);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Represents a partially completed instruction during recognition that only has the
|
|
|
|
* primary (non-aux) route instructions matched.
|
|
|
|
*
|
|
|
|
* `PrimaryInstruction` is an internal class used by `RouteRecognizer` while it's
|
|
|
|
* figuring out where to navigate.
|
|
|
|
*/
|
|
|
|
export class PrimaryInstruction {
|
|
|
|
constructor(public component: ComponentInstruction, public child: PrimaryInstruction,
|
2015-08-28 11:29:19 -07:00
|
|
|
public auxUrls: Url[]) {}
|
2015-07-17 13:36:53 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
export function stringifyInstruction(instruction: Instruction): string {
|
2015-09-23 00:10:26 -07:00
|
|
|
return stringifyInstructionPath(instruction) + stringifyInstructionQuery(instruction);
|
|
|
|
}
|
2015-07-17 13:36:53 -07:00
|
|
|
|
2015-09-23 00:10:26 -07:00
|
|
|
export function stringifyInstructionPath(instruction: Instruction): string {
|
2015-07-17 13:36:53 -07:00
|
|
|
return instruction.component.urlPath + stringifyAux(instruction) +
|
2015-10-30 17:03:19 -07:00
|
|
|
stringifyPrimaryPrefixed(instruction.child);
|
2015-09-23 00:10:26 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
export function stringifyInstructionQuery(instruction: Instruction): string {
|
|
|
|
return instruction.component.urlParams.length > 0 ?
|
|
|
|
('?' + instruction.component.urlParams.join('&')) :
|
|
|
|
'';
|
2015-07-17 13:36:53 -07:00
|
|
|
}
|
|
|
|
|
2015-10-30 17:03:19 -07:00
|
|
|
function stringifyPrimaryPrefixed(instruction: Instruction): string {
|
|
|
|
var primary = stringifyPrimary(instruction);
|
|
|
|
if (primary.length > 0) {
|
|
|
|
primary = '/' + primary;
|
|
|
|
}
|
|
|
|
return primary;
|
|
|
|
}
|
|
|
|
|
2015-07-17 13:36:53 -07:00
|
|
|
function stringifyPrimary(instruction: Instruction): string {
|
|
|
|
if (isBlank(instruction)) {
|
|
|
|
return '';
|
2015-06-30 13:18:51 -07:00
|
|
|
}
|
2015-07-17 13:36:53 -07:00
|
|
|
var params = instruction.component.urlParams.length > 0 ?
|
|
|
|
(';' + instruction.component.urlParams.join(';')) :
|
|
|
|
'';
|
2015-10-30 17:03:19 -07:00
|
|
|
return instruction.component.urlPath + params + stringifyAux(instruction) +
|
|
|
|
stringifyPrimaryPrefixed(instruction.child);
|
2015-07-17 13:36:53 -07:00
|
|
|
}
|
2015-06-30 13:18:51 -07:00
|
|
|
|
2015-07-17 13:36:53 -07:00
|
|
|
function stringifyAux(instruction: Instruction): string {
|
|
|
|
var routes = [];
|
|
|
|
StringMapWrapper.forEach(instruction.auxInstruction, (auxInstruction, _) => {
|
|
|
|
routes.push(stringifyPrimary(auxInstruction));
|
|
|
|
});
|
|
|
|
if (routes.length > 0) {
|
|
|
|
return '(' + routes.join('//') + ')';
|
2015-04-17 09:59:56 -07:00
|
|
|
}
|
2015-07-17 13:36:53 -07:00
|
|
|
return '';
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A `ComponentInstruction` represents the route state for a single component. An `Instruction` is
|
|
|
|
* composed of a tree of these `ComponentInstruction`s.
|
|
|
|
*
|
|
|
|
* `ComponentInstructions` is a public API. Instances of `ComponentInstruction` are passed
|
|
|
|
* to route lifecycle hooks, like {@link CanActivate}.
|
2015-08-30 21:20:18 -07:00
|
|
|
*
|
|
|
|
* `ComponentInstruction`s are [https://en.wikipedia.org/wiki/Hash_consing](hash consed). You should
|
2015-09-16 23:28:16 -07:00
|
|
|
* never construct one yourself with "new." Instead, rely on {@link Router/PathRecognizer} to
|
2015-09-21 19:06:53 -07:00
|
|
|
* construct `ComponentInstruction`s.
|
2015-08-30 21:20:18 -07:00
|
|
|
*
|
|
|
|
* You should not modify this object. It should be treated as immutable.
|
2015-07-17 13:36:53 -07:00
|
|
|
*/
|
2015-10-06 06:53:39 -07:00
|
|
|
export abstract class ComponentInstruction {
|
2015-07-17 13:36:53 -07:00
|
|
|
reuse: boolean = false;
|
2015-10-06 06:53:39 -07:00
|
|
|
public urlPath: string;
|
|
|
|
public urlParams: string[];
|
|
|
|
public params: {[key: string]: any};
|
2015-07-17 13:36:53 -07:00
|
|
|
|
2015-09-21 19:06:53 -07:00
|
|
|
/**
|
|
|
|
* Returns the component type of the represented route, or `null` if this instruction
|
|
|
|
* hasn't been resolved.
|
|
|
|
*/
|
2015-10-06 06:53:39 -07:00
|
|
|
get componentType() { return unimplemented(); };
|
2015-07-17 13:36:53 -07:00
|
|
|
|
2015-09-21 19:06:53 -07:00
|
|
|
/**
|
|
|
|
* Returns a promise that will resolve to component type of the represented route.
|
|
|
|
* If this instruction references an {@link AsyncRoute}, the `loader` function of that route
|
|
|
|
* will run.
|
|
|
|
*/
|
2015-10-06 06:53:39 -07:00
|
|
|
abstract resolveComponentType(): Promise<Type>;
|
2015-07-17 13:36:53 -07:00
|
|
|
|
2015-09-21 19:06:53 -07:00
|
|
|
/**
|
|
|
|
* Returns the specificity of the route associated with this `Instruction`.
|
|
|
|
*/
|
2015-10-06 06:53:39 -07:00
|
|
|
get specificity() { return unimplemented(); };
|
2015-07-17 13:36:53 -07:00
|
|
|
|
2015-09-21 19:06:53 -07:00
|
|
|
/**
|
|
|
|
* Returns `true` if the component type of this instruction has no child {@link RouteConfig},
|
|
|
|
* or `false` if it does.
|
|
|
|
*/
|
2015-10-06 06:53:39 -07:00
|
|
|
get terminal() { return unimplemented(); };
|
2015-08-10 20:29:40 -04:00
|
|
|
|
2015-09-21 19:06:53 -07:00
|
|
|
/**
|
|
|
|
* Returns the route data of the given route that was specified in the {@link RouteDefinition},
|
2015-09-30 14:48:58 +02:00
|
|
|
* or an empty object if no route data was specified.
|
2015-09-21 19:06:53 -07:00
|
|
|
*/
|
2015-09-30 14:48:58 +02:00
|
|
|
get routeData(): RouteData { return unimplemented(); };
|
2015-10-06 06:53:39 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
export class ComponentInstruction_ extends ComponentInstruction {
|
2015-09-30 14:48:58 +02:00
|
|
|
private _routeData: RouteData;
|
|
|
|
|
2015-10-06 06:53:39 -07:00
|
|
|
constructor(urlPath: string, urlParams: string[], private _recognizer: PathRecognizer,
|
|
|
|
params: {[key: string]: any} = null) {
|
|
|
|
super();
|
|
|
|
this.urlPath = urlPath;
|
|
|
|
this.urlParams = urlParams;
|
|
|
|
this.params = params;
|
2015-09-30 14:48:58 +02:00
|
|
|
if (isPresent(this._recognizer.handler.data)) {
|
|
|
|
this._routeData = new RouteData(this._recognizer.handler.data);
|
|
|
|
} else {
|
|
|
|
this._routeData = BLANK_ROUTE_DATA;
|
|
|
|
}
|
2015-10-06 06:53:39 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
get componentType() { return this._recognizer.handler.componentType; }
|
|
|
|
resolveComponentType(): Promise<Type> { return this._recognizer.handler.resolveComponentType(); }
|
|
|
|
get specificity() { return this._recognizer.specificity; }
|
|
|
|
get terminal() { return this._recognizer.terminal; }
|
2015-09-30 14:48:58 +02:00
|
|
|
get routeData(): RouteData { return this._routeData; }
|
2015-04-17 09:59:56 -07:00
|
|
|
}
|