feat(router): add interfaces for route definitions in RouteConfig

Closes #2261
This commit is contained in:
Brian Ford 2015-07-13 16:12:48 -07:00
parent 61c73576c8
commit 4d28167bc0
16 changed files with 457 additions and 227 deletions

View File

@ -1,4 +1,5 @@
import {RouteConfig as RouteConfigAnnotation} from './route_config_impl'; import {RouteConfig as RouteConfigAnnotation} from './route_config_impl';
import {makeDecorator} from 'angular2/src/util/decorators'; import {makeDecorator} from 'angular2/src/util/decorators';
export {Route, Redirect, AsyncRoute, RouteDefinition} from './route_config_impl';
export var RouteConfig = makeDecorator(RouteConfigAnnotation); export var RouteConfig = makeDecorator(RouteConfigAnnotation);

View File

@ -1,5 +1,7 @@
import {CONST} from 'angular2/src/facade/lang'; import {CONST, Type} from 'angular2/src/facade/lang';
import {List, Map} from 'angular2/src/facade/collection'; import {List} from 'angular2/src/facade/collection';
import {RouteDefinition} from './route_definition';
export {RouteDefinition} from './route_definition';
/** /**
* You use the RouteConfig annotation to add routes to a component. * You use the RouteConfig annotation to add routes to a component.
@ -11,5 +13,41 @@ import {List, Map} from 'angular2/src/facade/collection';
*/ */
@CONST() @CONST()
export class RouteConfig { export class RouteConfig {
constructor(public configs: List<Map<any, any>>) {} constructor(public configs: List<RouteDefinition>) {}
}
@CONST()
export class Route implements RouteDefinition {
path: string;
component: Type;
as: string;
constructor({path, component, as}: {path: string, component: Type, as?: string}) {
this.path = path;
this.component = component;
this.as = as;
}
}
@CONST()
export class AsyncRoute implements RouteDefinition {
path: string;
loader: Function;
as: string;
constructor({path, loader, as}: {path: string, loader: Function, as?: string}) {
this.path = path;
this.loader = loader;
this.as = as;
}
}
@CONST()
export class Redirect implements RouteDefinition {
path: string;
redirectTo: string;
as: string = null;
constructor({path, redirectTo}: {path: string, redirectTo: string}) {
this.path = path;
this.redirectTo = redirectTo;
}
} }

View File

@ -0,0 +1,7 @@
library angular2.src.router.route_config_normalizer;
import "route_config_decorator.dart";
RouteDefinition normalizeRouteConfig(RouteDefinition config) {
return config;
}

View File

@ -0,0 +1,46 @@
import {AsyncRoute, Route, Redirect, RouteDefinition} from './route_config_decorator';
import {ComponentDefinition} from './route_definition';
import {Type, BaseException} from 'angular2/src/facade/lang';
/**
* Given a JS Object that represents... returns a corresponding Route, AsyncRoute, or Redirect
*/
export function normalizeRouteConfig(config: RouteDefinition): RouteDefinition {
if (config instanceof Route || config instanceof Redirect || config instanceof AsyncRoute) {
return <RouteDefinition>config;
}
if ((!config.component) == (!config.redirectTo)) {
throw new BaseException(
`Route config should contain exactly one 'component', or 'redirectTo' property`);
}
if (config.component) {
if (typeof config.component == 'object') {
let componentDefinitionObject = <ComponentDefinition>config.component;
if (componentDefinitionObject.type == 'constructor') {
return new Route({
path: config.path,
component:<Type>componentDefinitionObject.constructor,
as: config.as
});
} else if (componentDefinitionObject.type == 'loader') {
return new AsyncRoute(
{path: config.path, loader: componentDefinitionObject.loader, as: config.as});
} else {
throw new BaseException(
`Invalid component type '${componentDefinitionObject.type}'. Valid types are "constructor" and "loader".`);
}
}
return new Route(<{
path: string;
component: Type;
as?: string
}>config);
}
if (config.redirectTo) {
return new Redirect({path: config.path, redirectTo: config.redirectTo});
}
return config;
}

View File

@ -0,0 +1,7 @@
library angular2.src.router.route_definition;
abstract class RouteDefinition {
final String path;
final String as;
const RouteDefinition({this.path, this.as});
}

View File

@ -0,0 +1,15 @@
import {CONST, Type} from 'angular2/src/facade/lang';
export interface RouteDefinition {
path: string;
component?: Type | ComponentDefinition;
loader?: Function;
redirectTo?: string;
as?: string;
}
export interface ComponentDefinition {
type: string;
loader?: Function;
component?: Type;
}

View File

@ -19,6 +19,7 @@ import {
import {PathRecognizer} from './path_recognizer'; import {PathRecognizer} from './path_recognizer';
import {RouteHandler} from './route_handler'; import {RouteHandler} from './route_handler';
import {Route, AsyncRoute, Redirect, RouteDefinition} from './route_config_impl';
import {AsyncRouteHandler} from './async_route_handler'; import {AsyncRouteHandler} from './async_route_handler';
import {SyncRouteHandler} from './sync_route_handler'; import {SyncRouteHandler} from './sync_route_handler';
@ -32,25 +33,27 @@ export class RouteRecognizer {
redirects: Map<string, string> = new Map(); redirects: Map<string, string> = new Map();
matchers: Map<RegExp, PathRecognizer> = new Map(); matchers: Map<RegExp, PathRecognizer> = new Map();
addRedirect(path: string, target: string): void { config(config: RouteDefinition): boolean {
if (path == '/') { var handler;
path = ''; if (config instanceof Redirect) {
let path = config.path == '/' ? '' : config.path;
this.redirects.set(path, config.redirectTo);
return true;
} else if (config instanceof Route) {
handler = new SyncRouteHandler(config.component);
} else if (config instanceof AsyncRoute) {
handler = new AsyncRouteHandler(config.loader);
} }
this.redirects.set(path, target); var recognizer = new PathRecognizer(config.path, handler);
}
addConfig(path: string, handlerObj: any, alias: string = null): boolean {
var handler = configObjToHandler(handlerObj['component']);
var recognizer = new PathRecognizer(path, handler);
MapWrapper.forEach(this.matchers, (matcher, _) => { MapWrapper.forEach(this.matchers, (matcher, _) => {
if (recognizer.regex.toString() == matcher.regex.toString()) { if (recognizer.regex.toString() == matcher.regex.toString()) {
throw new BaseException( throw new BaseException(
`Configuration '${path}' conflicts with existing route '${matcher.path}'`); `Configuration '${config.path}' conflicts with existing route '${matcher.path}'`);
} }
}); });
this.matchers.set(recognizer.regex, recognizer); this.matchers.set(recognizer.regex, recognizer);
if (isPresent(alias)) { if (isPresent(config.as)) {
this.names.set(alias, recognizer); this.names.set(config.as, recognizer);
} }
return recognizer.terminal; return recognizer.terminal;
} }

View File

@ -20,9 +20,10 @@ import {
BaseException, BaseException,
getTypeNameForDebugging getTypeNameForDebugging
} from 'angular2/src/facade/lang'; } from 'angular2/src/facade/lang';
import {RouteConfig} from './route_config_impl'; import {RouteConfig, AsyncRoute, Route, Redirect, RouteDefinition} from './route_config_impl';
import {reflector} from 'angular2/src/reflection/reflection'; import {reflector} from 'angular2/src/reflection/reflection';
import {Injectable} from 'angular2/di'; import {Injectable} from 'angular2/di';
import {normalizeRouteConfig} from './route_config_nomalizer';
/** /**
* The RouteRegistry holds route configurations for each component in an Angular app. * The RouteRegistry holds route configurations for each component in an Angular app.
@ -36,8 +37,8 @@ export class RouteRegistry {
/** /**
* Given a component and a configuration object, add the route to this registry * Given a component and a configuration object, add the route to this registry
*/ */
config(parentComponent, config: StringMap<string, any>): void { config(parentComponent, config: RouteDefinition): void {
assertValidConfig(config); config = normalizeRouteConfig(config);
var recognizer: RouteRecognizer = this._rules.get(parentComponent); var recognizer: RouteRecognizer = this._rules.get(parentComponent);
@ -46,22 +47,13 @@ export class RouteRegistry {
this._rules.set(parentComponent, recognizer); this._rules.set(parentComponent, recognizer);
} }
if (StringMapWrapper.contains(config, 'redirectTo')) { var terminal = recognizer.config(config);
recognizer.addRedirect(config['path'], config['redirectTo']);
return;
}
config = StringMapWrapper.merge( if (config instanceof Route) {
config, {'component': normalizeComponentDeclaration(config['component'])});
var component = config['component'];
var terminal = recognizer.addConfig(config['path'], config, config['as']);
if (component['type'] == 'constructor') {
if (terminal) { if (terminal) {
assertTerminalComponent(component['constructor'], config['path']); assertTerminalComponent(config.component, config.path);
} else { } else {
this.configFromComponent(component['constructor']); this.configFromComponent(config.component);
} }
} }
} }
@ -191,50 +183,6 @@ export class RouteRegistry {
} }
/*
* A config should have a "path" property, and exactly one of:
* - `component`
* - `redirectTo`
*/
var ALLOWED_TARGETS = ['component', 'redirectTo'];
function assertValidConfig(config: StringMap<string, any>): void {
if (!StringMapWrapper.contains(config, 'path')) {
throw new BaseException(`Route config should contain a "path" property`);
}
var targets = 0;
ListWrapper.forEach(ALLOWED_TARGETS, (target) => {
if (StringMapWrapper.contains(config, target)) {
targets += 1;
}
});
if (targets != 1) {
throw new BaseException(
`Route config should contain exactly one 'component', or 'redirectTo' property`);
}
}
/*
* Returns a StringMap like: `{ 'constructor': SomeType, 'type': 'constructor' }`
*/
var VALID_COMPONENT_TYPES = ['constructor', 'loader'];
function normalizeComponentDeclaration(config: any): StringMap<string, any> {
if (isType(config)) {
return {'constructor': config, 'type': 'constructor'};
} else if (isStringMap(config)) {
if (isBlank(config['type'])) {
throw new BaseException(
`Component declaration when provided as a map should include a 'type' property`);
}
var componentType = config['type'];
if (!ListWrapper.contains(VALID_COMPONENT_TYPES, componentType)) {
throw new BaseException(`Invalid component type '${componentType}'`);
}
return config;
} else {
throw new BaseException(`Component declaration should be either a Map or a Type`);
}
}
/* /*
* Given a list of instructions, returns the most specific instruction * Given a list of instructions, returns the most specific instruction
*/ */

View File

@ -16,6 +16,7 @@ import {Instruction} from './instruction';
import {RouterOutlet} from './router_outlet'; import {RouterOutlet} from './router_outlet';
import {Location} from './location'; import {Location} from './location';
import {getCanActivateHook} from './route_lifecycle_reflector'; import {getCanActivateHook} from './route_lifecycle_reflector';
import {RouteDefinition} from './route_config_impl';
let _resolveToTrue = PromiseWrapper.resolve(true); let _resolveToTrue = PromiseWrapper.resolve(true);
let _resolveToFalse = PromiseWrapper.resolve(false); let _resolveToFalse = PromiseWrapper.resolve(false);
@ -79,25 +80,15 @@ export class Router {
* # Usage * # Usage
* *
* ``` * ```
* router.config({ 'path': '/', 'component': IndexCmp});
* ```
*
* Or:
*
* ```
* router.config([ * router.config([
* { 'path': '/', 'component': IndexComp }, * { 'path': '/', 'component': IndexComp },
* { 'path': '/user/:id', 'component': UserComp }, * { 'path': '/user/:id', 'component': UserComp },
* ]); * ]);
* ``` * ```
*/ */
config(config: StringMap<string, any>| List<StringMap<string, any>>): Promise<any> { config(definitions: List<RouteDefinition>): Promise<any> {
if (isArray(config)) { definitions.forEach(
(<List<any>>config) (routeDefinition) => { this.registry.config(this.hostComponent, routeDefinition); });
.forEach((configObject) => { this.registry.config(this.hostComponent, configObject); });
} else {
this.registry.config(this.hostComponent, config);
}
return this.renavigate(); return this.renavigate();
} }

View File

@ -24,7 +24,7 @@ import {Promise, PromiseWrapper, EventEmitter, ObservableWrapper} from 'angular2
import {RootRouter} from 'angular2/src/router/router'; import {RootRouter} from 'angular2/src/router/router';
import {Pipeline} from 'angular2/src/router/pipeline'; import {Pipeline} from 'angular2/src/router/pipeline';
import {Router, RouterOutlet, RouterLink, RouteParams} from 'angular2/router'; import {Router, RouterOutlet, RouterLink, RouteParams} from 'angular2/router';
import {RouteConfig} from 'angular2/src/router/route_config_decorator'; import {RouteConfig, Route, AsyncRoute, Redirect} from 'angular2/src/router/route_config_decorator';
import {DOM} from 'angular2/src/dom/dom_adapter'; import {DOM} from 'angular2/src/dom/dom_adapter';
@ -81,7 +81,7 @@ export function main() {
it('should work in a simple case', inject([AsyncTestCompleter], (async) => { it('should work in a simple case', inject([AsyncTestCompleter], (async) => {
compile() compile()
.then((_) => rtr.config({'path': '/test', 'component': HelloCmp})) .then((_) => rtr.config([new Route({path: '/test', component: HelloCmp})]))
.then((_) => rtr.navigate('/test')) .then((_) => rtr.navigate('/test'))
.then((_) => { .then((_) => {
rootTC.detectChanges(); rootTC.detectChanges();
@ -94,7 +94,7 @@ export function main() {
it('should navigate between components with different parameters', it('should navigate between components with different parameters',
inject([AsyncTestCompleter], (async) => { inject([AsyncTestCompleter], (async) => {
compile() compile()
.then((_) => rtr.config({'path': '/user/:name', 'component': UserCmp})) .then((_) => rtr.config([new Route({path: '/user/:name', component: UserCmp})]))
.then((_) => rtr.navigate('/user/brian')) .then((_) => rtr.navigate('/user/brian'))
.then((_) => { .then((_) => {
rootTC.detectChanges(); rootTC.detectChanges();
@ -111,7 +111,7 @@ export function main() {
it('should work with child routers', inject([AsyncTestCompleter], (async) => { it('should work with child routers', inject([AsyncTestCompleter], (async) => {
compile('outer { <router-outlet></router-outlet> }') compile('outer { <router-outlet></router-outlet> }')
.then((_) => rtr.config({'path': '/a/...', 'component': ParentCmp})) .then((_) => rtr.config([new Route({path: '/a/...', component: ParentCmp})]))
.then((_) => rtr.navigate('/a/b')) .then((_) => rtr.navigate('/a/b'))
.then((_) => { .then((_) => {
rootTC.detectChanges(); rootTC.detectChanges();
@ -123,8 +123,10 @@ export function main() {
it('should work with redirects', inject([AsyncTestCompleter, Location], (async, location) => { it('should work with redirects', inject([AsyncTestCompleter, Location], (async, location) => {
compile() compile()
.then((_) => rtr.config({'path': '/original', 'redirectTo': '/redirected'})) .then((_) => rtr.config([
.then((_) => rtr.config({'path': '/redirected', 'component': A})) new Redirect({path: '/original', redirectTo: '/redirected'}),
new Route({path: '/redirected', component: A})
]))
.then((_) => rtr.navigate('/original')) .then((_) => rtr.navigate('/original'))
.then((_) => { .then((_) => {
rootTC.detectChanges(); rootTC.detectChanges();
@ -142,7 +144,7 @@ export function main() {
inject([AsyncTestCompleter], (async) => { inject([AsyncTestCompleter], (async) => {
location.setBaseHref('/my/base'); location.setBaseHref('/my/base');
compile('<a href="hello" [router-link]="[\'./user\']"></a>') compile('<a href="hello" [router-link]="[\'./user\']"></a>')
.then((_) => rtr.config({'path': '/user', 'component': UserCmp, 'as': 'user'})) .then((_) => rtr.config([new Route({path: '/user', component: UserCmp, as: 'user'})]))
.then((_) => rtr.navigate('/a/b')) .then((_) => rtr.navigate('/a/b'))
.then((_) => { .then((_) => {
rootTC.detectChanges(); rootTC.detectChanges();
@ -154,7 +156,7 @@ export function main() {
it('should generate link hrefs without params', inject([AsyncTestCompleter], (async) => { it('should generate link hrefs without params', inject([AsyncTestCompleter], (async) => {
compile('<a href="hello" [router-link]="[\'./user\']"></a>') compile('<a href="hello" [router-link]="[\'./user\']"></a>')
.then((_) => rtr.config({'path': '/user', 'component': UserCmp, 'as': 'user'})) .then((_) => rtr.config([new Route({path: '/user', component: UserCmp, as: 'user'})]))
.then((_) => rtr.navigate('/a/b')) .then((_) => rtr.navigate('/a/b'))
.then((_) => { .then((_) => {
rootTC.detectChanges(); rootTC.detectChanges();
@ -166,7 +168,7 @@ export function main() {
it('should reuse common parent components', inject([AsyncTestCompleter], (async) => { it('should reuse common parent components', inject([AsyncTestCompleter], (async) => {
compile() compile()
.then((_) => rtr.config({'path': '/team/:id/...', 'component': TeamCmp})) .then((_) => rtr.config([new Route({path: '/team/:id/...', component: TeamCmp})]))
.then((_) => rtr.navigate('/team/angular/user/rado')) .then((_) => rtr.navigate('/team/angular/user/rado'))
.then((_) => { .then((_) => {
rootTC.detectChanges(); rootTC.detectChanges();
@ -185,7 +187,8 @@ export function main() {
it('should generate link hrefs with params', inject([AsyncTestCompleter], (async) => { it('should generate link hrefs with params', inject([AsyncTestCompleter], (async) => {
compile('<a href="hello" [router-link]="[\'./user\', {name: name}]">{{name}}</a>') compile('<a href="hello" [router-link]="[\'./user\', {name: name}]">{{name}}</a>')
.then((_) => rtr.config({'path': '/user/:name', 'component': UserCmp, 'as': 'user'})) .then((_) => rtr.config(
[new Route({path: '/user/:name', component: UserCmp, as: 'user'})]))
.then((_) => rtr.navigate('/a/b')) .then((_) => rtr.navigate('/a/b'))
.then((_) => { .then((_) => {
rootTC.componentInstance.name = 'brian'; rootTC.componentInstance.name = 'brian';
@ -202,7 +205,7 @@ export function main() {
inject([AsyncTestCompleter], (async) => { inject([AsyncTestCompleter], (async) => {
compile() compile()
.then((_) => rtr.config( .then((_) => rtr.config(
{'path': '/page/:number', 'component': SiblingPageCmp, 'as': 'page'})) [new Route({path: '/page/:number', component: SiblingPageCmp, as: 'page'})]))
.then((_) => rtr.navigate('/page/1')) .then((_) => rtr.navigate('/page/1'))
.then((_) => { .then((_) => {
rootTC.detectChanges(); rootTC.detectChanges();
@ -217,8 +220,8 @@ export function main() {
it('should generate relative links preserving the existing parent route', it('should generate relative links preserving the existing parent route',
inject([AsyncTestCompleter], (async) => { inject([AsyncTestCompleter], (async) => {
compile() compile()
.then((_) => .then((_) => rtr.config(
rtr.config({'path': '/book/:title/...', 'component': BookCmp, 'as': 'book'})) [new Route({path: '/book/:title/...', component: BookCmp, as: 'book'})]))
.then((_) => rtr.navigate('/book/1984/page/1')) .then((_) => rtr.navigate('/book/1984/page/1'))
.then((_) => { .then((_) => {
rootTC.detectChanges(); rootTC.detectChanges();
@ -239,7 +242,7 @@ export function main() {
it('should call the onActivate hook', inject([AsyncTestCompleter], (async) => { it('should call the onActivate hook', inject([AsyncTestCompleter], (async) => {
compile() compile()
.then((_) => rtr.config({'path': '/...', 'component': LifecycleCmp})) .then((_) => rtr.config([new Route({path: '/...', component: LifecycleCmp})]))
.then((_) => rtr.navigate('/on-activate')) .then((_) => rtr.navigate('/on-activate'))
.then((_) => { .then((_) => {
rootTC.detectChanges(); rootTC.detectChanges();
@ -252,7 +255,7 @@ export function main() {
it('should wait for a parent component\'s onActivate hook to resolve before calling its child\'s', it('should wait for a parent component\'s onActivate hook to resolve before calling its child\'s',
inject([AsyncTestCompleter], (async) => { inject([AsyncTestCompleter], (async) => {
compile() compile()
.then((_) => rtr.config({'path': '/...', 'component': LifecycleCmp})) .then((_) => rtr.config([new Route({path: '/...', component: LifecycleCmp})]))
.then((_) => { .then((_) => {
ObservableWrapper.subscribe(eventBus, (ev) => { ObservableWrapper.subscribe(eventBus, (ev) => {
if (ev.startsWith('parent activate')) { if (ev.startsWith('parent activate')) {
@ -272,7 +275,7 @@ export function main() {
it('should call the onDeactivate hook', inject([AsyncTestCompleter], (async) => { it('should call the onDeactivate hook', inject([AsyncTestCompleter], (async) => {
compile() compile()
.then((_) => rtr.config({'path': '/...', 'component': LifecycleCmp})) .then((_) => rtr.config([new Route({path: '/...', component: LifecycleCmp})]))
.then((_) => rtr.navigate('/on-deactivate')) .then((_) => rtr.navigate('/on-deactivate'))
.then((_) => rtr.navigate('/a')) .then((_) => rtr.navigate('/a'))
.then((_) => { .then((_) => {
@ -286,7 +289,7 @@ export function main() {
it('should wait for a child component\'s onDeactivate hook to resolve before calling its parent\'s', it('should wait for a child component\'s onDeactivate hook to resolve before calling its parent\'s',
inject([AsyncTestCompleter], (async) => { inject([AsyncTestCompleter], (async) => {
compile() compile()
.then((_) => rtr.config({'path': '/...', 'component': LifecycleCmp})) .then((_) => rtr.config([new Route({path: '/...', component: LifecycleCmp})]))
.then((_) => rtr.navigate('/parent-deactivate/child-deactivate')) .then((_) => rtr.navigate('/parent-deactivate/child-deactivate'))
.then((_) => { .then((_) => {
ObservableWrapper.subscribe(eventBus, (ev) => { ObservableWrapper.subscribe(eventBus, (ev) => {
@ -309,7 +312,7 @@ export function main() {
it('should reuse a component when the canReuse hook returns false', it('should reuse a component when the canReuse hook returns false',
inject([AsyncTestCompleter], (async) => { inject([AsyncTestCompleter], (async) => {
compile() compile()
.then((_) => rtr.config({'path': '/...', 'component': LifecycleCmp})) .then((_) => rtr.config([new Route({path: '/...', component: LifecycleCmp})]))
.then((_) => rtr.navigate('/on-reuse/1/a')) .then((_) => rtr.navigate('/on-reuse/1/a'))
.then((_) => { .then((_) => {
rootTC.detectChanges(); rootTC.detectChanges();
@ -331,7 +334,7 @@ export function main() {
it('should not reuse a component when the canReuse hook returns false', it('should not reuse a component when the canReuse hook returns false',
inject([AsyncTestCompleter], (async) => { inject([AsyncTestCompleter], (async) => {
compile() compile()
.then((_) => rtr.config({'path': '/...', 'component': LifecycleCmp})) .then((_) => rtr.config([new Route({path: '/...', component: LifecycleCmp})]))
.then((_) => rtr.navigate('/never-reuse/1/a')) .then((_) => rtr.navigate('/never-reuse/1/a'))
.then((_) => { .then((_) => {
rootTC.detectChanges(); rootTC.detectChanges();
@ -351,7 +354,7 @@ export function main() {
it('should navigate when canActivate returns true', inject([AsyncTestCompleter], (async) => { it('should navigate when canActivate returns true', inject([AsyncTestCompleter], (async) => {
compile() compile()
.then((_) => rtr.config({'path': '/...', 'component': LifecycleCmp})) .then((_) => rtr.config([new Route({path: '/...', component: LifecycleCmp})]))
.then((_) => { .then((_) => {
ObservableWrapper.subscribe(eventBus, (ev) => { ObservableWrapper.subscribe(eventBus, (ev) => {
if (ev.startsWith('canActivate')) { if (ev.startsWith('canActivate')) {
@ -371,7 +374,7 @@ export function main() {
it('should not navigate when canActivate returns false', it('should not navigate when canActivate returns false',
inject([AsyncTestCompleter], (async) => { inject([AsyncTestCompleter], (async) => {
compile() compile()
.then((_) => rtr.config({'path': '/...', 'component': LifecycleCmp})) .then((_) => rtr.config([new Route({path: '/...', component: LifecycleCmp})]))
.then((_) => { .then((_) => {
ObservableWrapper.subscribe(eventBus, (ev) => { ObservableWrapper.subscribe(eventBus, (ev) => {
if (ev.startsWith('canActivate')) { if (ev.startsWith('canActivate')) {
@ -391,7 +394,7 @@ export function main() {
it('should navigate away when canDeactivate returns true', it('should navigate away when canDeactivate returns true',
inject([AsyncTestCompleter], (async) => { inject([AsyncTestCompleter], (async) => {
compile() compile()
.then((_) => rtr.config({'path': '/...', 'component': LifecycleCmp})) .then((_) => rtr.config([new Route({path: '/...', component: LifecycleCmp})]))
.then((_) => rtr.navigate('/can-deactivate/a')) .then((_) => rtr.navigate('/can-deactivate/a'))
.then((_) => { .then((_) => {
rootTC.detectChanges(); rootTC.detectChanges();
@ -416,7 +419,7 @@ export function main() {
it('should not navigate away when canDeactivate returns false', it('should not navigate away when canDeactivate returns false',
inject([AsyncTestCompleter], (async) => { inject([AsyncTestCompleter], (async) => {
compile() compile()
.then((_) => rtr.config({'path': '/...', 'component': LifecycleCmp})) .then((_) => rtr.config([new Route({path: '/...', component: LifecycleCmp})]))
.then((_) => rtr.navigate('/can-deactivate/a')) .then((_) => rtr.navigate('/can-deactivate/a'))
.then((_) => { .then((_) => {
rootTC.detectChanges(); rootTC.detectChanges();
@ -442,7 +445,7 @@ export function main() {
it('should run activation and deactivation hooks in the correct order', it('should run activation and deactivation hooks in the correct order',
inject([AsyncTestCompleter], (async) => { inject([AsyncTestCompleter], (async) => {
compile() compile()
.then((_) => rtr.config({'path': '/...', 'component': LifecycleCmp})) .then((_) => rtr.config([new Route({path: '/...', component: LifecycleCmp})]))
.then((_) => rtr.navigate('/activation-hooks/child')) .then((_) => rtr.navigate('/activation-hooks/child'))
.then((_) => { .then((_) => {
expect(log).toEqual('canActivate child: null -> /child;' + expect(log).toEqual('canActivate child: null -> /child;' +
@ -464,7 +467,7 @@ export function main() {
it('should only run reuse hooks when reusing', inject([AsyncTestCompleter], (async) => { it('should only run reuse hooks when reusing', inject([AsyncTestCompleter], (async) => {
compile() compile()
.then((_) => rtr.config({'path': '/...', 'component': LifecycleCmp})) .then((_) => rtr.config([new Route({path: '/...', component: LifecycleCmp})]))
.then((_) => rtr.navigate('/reuse-hooks/1')) .then((_) => rtr.navigate('/reuse-hooks/1'))
.then((_) => { .then((_) => {
expect(log).toEqual('canActivate: null -> /reuse-hooks/1;' + expect(log).toEqual('canActivate: null -> /reuse-hooks/1;' +
@ -488,7 +491,7 @@ export function main() {
it('should not run reuse hooks when not reusing', inject([AsyncTestCompleter], (async) => { it('should not run reuse hooks when not reusing', inject([AsyncTestCompleter], (async) => {
compile() compile()
.then((_) => rtr.config({'path': '/...', 'component': LifecycleCmp})) .then((_) => rtr.config([new Route({path: '/...', component: LifecycleCmp})]))
.then((_) => rtr.navigate('/reuse-hooks/1')) .then((_) => rtr.navigate('/reuse-hooks/1'))
.then((_) => { .then((_) => {
expect(log).toEqual('canActivate: null -> /reuse-hooks/1;' + expect(log).toEqual('canActivate: null -> /reuse-hooks/1;' +
@ -524,7 +527,8 @@ export function main() {
it('should navigate to link hrefs without params', inject([AsyncTestCompleter], (async) => { it('should navigate to link hrefs without params', inject([AsyncTestCompleter], (async) => {
compile('<a href="hello" [router-link]="[\'./user\']"></a>') compile('<a href="hello" [router-link]="[\'./user\']"></a>')
.then((_) => rtr.config({'path': '/user', 'component': UserCmp, 'as': 'user'})) .then((_) =>
rtr.config([new Route({path: '/user', component: UserCmp, as: 'user'})]))
.then((_) => rtr.navigate('/a/b')) .then((_) => rtr.navigate('/a/b'))
.then((_) => { .then((_) => {
rootTC.detectChanges(); rootTC.detectChanges();
@ -545,7 +549,8 @@ export function main() {
inject([AsyncTestCompleter], (async) => { inject([AsyncTestCompleter], (async) => {
location.setBaseHref('/base'); location.setBaseHref('/base');
compile('<a href="hello" [router-link]="[\'./user\']"></a>') compile('<a href="hello" [router-link]="[\'./user\']"></a>')
.then((_) => rtr.config({'path': '/user', 'component': UserCmp, 'as': 'user'})) .then((_) =>
rtr.config([new Route({path: '/user', component: UserCmp, as: 'user'})]))
.then((_) => rtr.navigate('/a/b')) .then((_) => rtr.navigate('/a/b'))
.then((_) => { .then((_) => {
rootTC.detectChanges(); rootTC.detectChanges();
@ -615,7 +620,7 @@ class SiblingPageCmp {
<router-outlet></router-outlet>`, <router-outlet></router-outlet>`,
directives: [RouterLink, RouterOutlet] directives: [RouterLink, RouterOutlet]
}) })
@RouteConfig([{path: '/page/:number', component: SiblingPageCmp, 'as': 'page'}]) @RouteConfig([new Route({path: '/page/:number', component: SiblingPageCmp, as: 'page'})])
class BookCmp { class BookCmp {
title: string; title: string;
constructor(params: RouteParams) { this.title = params.get('title'); } constructor(params: RouteParams) { this.title = params.get('title'); }
@ -624,7 +629,7 @@ class BookCmp {
@Component({selector: 'parent-cmp'}) @Component({selector: 'parent-cmp'})
@View({template: "inner { <router-outlet></router-outlet> }", directives: [RouterOutlet]}) @View({template: "inner { <router-outlet></router-outlet> }", directives: [RouterOutlet]})
@RouteConfig([{path: '/b', component: HelloCmp}]) @RouteConfig([new Route({path: '/b', component: HelloCmp})])
class ParentCmp { class ParentCmp {
constructor() {} constructor() {}
} }
@ -632,7 +637,7 @@ class ParentCmp {
@Component({selector: 'team-cmp'}) @Component({selector: 'team-cmp'})
@View({template: "team {{id}} { <router-outlet></router-outlet> }", directives: [RouterOutlet]}) @View({template: "team {{id}} { <router-outlet></router-outlet> }", directives: [RouterOutlet]})
@RouteConfig([{path: '/user/:name', component: UserCmp}]) @RouteConfig([new Route({path: '/user/:name', component: UserCmp})])
class TeamCmp { class TeamCmp {
id: string; id: string;
constructor(params: RouteParams) { constructor(params: RouteParams) {
@ -662,7 +667,7 @@ class ActivateCmp implements OnActivate {
@Component({selector: 'parent-activate-cmp'}) @Component({selector: 'parent-activate-cmp'})
@View({template: `parent {<router-outlet></router-outlet>}`, directives: [RouterOutlet]}) @View({template: `parent {<router-outlet></router-outlet>}`, directives: [RouterOutlet]})
@RouteConfig([{path: '/child-activate', component: ActivateCmp}]) @RouteConfig([new Route({path: '/child-activate', component: ActivateCmp})])
class ParentActivateCmp implements OnActivate { class ParentActivateCmp implements OnActivate {
onActivate(next: Instruction, prev: Instruction): Promise<any> { onActivate(next: Instruction, prev: Instruction): Promise<any> {
completer = PromiseWrapper.completer(); completer = PromiseWrapper.completer();
@ -689,14 +694,14 @@ class WaitDeactivateCmp implements OnDeactivate {
@Component({selector: 'parent-deactivate-cmp'}) @Component({selector: 'parent-deactivate-cmp'})
@View({template: `parent {<router-outlet></router-outlet>}`, directives: [RouterOutlet]}) @View({template: `parent {<router-outlet></router-outlet>}`, directives: [RouterOutlet]})
@RouteConfig([{path: '/child-deactivate', component: WaitDeactivateCmp}]) @RouteConfig([new Route({path: '/child-deactivate', component: WaitDeactivateCmp})])
class ParentDeactivateCmp implements OnDeactivate { class ParentDeactivateCmp implements OnDeactivate {
onDeactivate(next: Instruction, prev: Instruction) { logHook('parent deactivate', next, prev); } onDeactivate(next: Instruction, prev: Instruction) { logHook('parent deactivate', next, prev); }
} }
@Component({selector: 'reuse-cmp'}) @Component({selector: 'reuse-cmp'})
@View({template: `reuse {<router-outlet></router-outlet>}`, directives: [RouterOutlet]}) @View({template: `reuse {<router-outlet></router-outlet>}`, directives: [RouterOutlet]})
@RouteConfig([{path: '/a', component: A}, {path: '/b', component: B}]) @RouteConfig([new Route({path: '/a', component: A}), new Route({path: '/b', component: B})])
class ReuseCmp implements OnReuse, CanReuse { class ReuseCmp implements OnReuse, CanReuse {
constructor() { cmpInstanceCount += 1; } constructor() { cmpInstanceCount += 1; }
canReuse(next: Instruction, prev: Instruction) { return true; } canReuse(next: Instruction, prev: Instruction) { return true; }
@ -705,7 +710,7 @@ class ReuseCmp implements OnReuse, CanReuse {
@Component({selector: 'never-reuse-cmp'}) @Component({selector: 'never-reuse-cmp'})
@View({template: `reuse {<router-outlet></router-outlet>}`, directives: [RouterOutlet]}) @View({template: `reuse {<router-outlet></router-outlet>}`, directives: [RouterOutlet]})
@RouteConfig([{path: '/a', component: A}, {path: '/b', component: B}]) @RouteConfig([new Route({path: '/a', component: A}), new Route({path: '/b', component: B})])
class NeverReuseCmp implements OnReuse, CanReuse { class NeverReuseCmp implements OnReuse, CanReuse {
constructor() { cmpInstanceCount += 1; } constructor() { cmpInstanceCount += 1; }
canReuse(next: Instruction, prev: Instruction) { return false; } canReuse(next: Instruction, prev: Instruction) { return false; }
@ -714,7 +719,7 @@ class NeverReuseCmp implements OnReuse, CanReuse {
@Component({selector: 'can-activate-cmp'}) @Component({selector: 'can-activate-cmp'})
@View({template: `canActivate {<router-outlet></router-outlet>}`, directives: [RouterOutlet]}) @View({template: `canActivate {<router-outlet></router-outlet>}`, directives: [RouterOutlet]})
@RouteConfig([{path: '/a', component: A}, {path: '/b', component: B}]) @RouteConfig([new Route({path: '/a', component: A}), new Route({path: '/b', component: B})])
@CanActivate(CanActivateCmp.canActivate) @CanActivate(CanActivateCmp.canActivate)
class CanActivateCmp { class CanActivateCmp {
static canActivate(next: Instruction, prev: Instruction) { static canActivate(next: Instruction, prev: Instruction) {
@ -726,7 +731,7 @@ class CanActivateCmp {
@Component({selector: 'can-deactivate-cmp'}) @Component({selector: 'can-deactivate-cmp'})
@View({template: `canDeactivate {<router-outlet></router-outlet>}`, directives: [RouterOutlet]}) @View({template: `canDeactivate {<router-outlet></router-outlet>}`, directives: [RouterOutlet]})
@RouteConfig([{path: '/a', component: A}, {path: '/b', component: B}]) @RouteConfig([new Route({path: '/a', component: A}), new Route({path: '/b', component: B})])
class CanDeactivateCmp implements CanDeactivate { class CanDeactivateCmp implements CanDeactivate {
canDeactivate(next: Instruction, prev: Instruction) { canDeactivate(next: Instruction, prev: Instruction) {
completer = PromiseWrapper.completer(); completer = PromiseWrapper.completer();
@ -756,7 +761,7 @@ class AllHooksChildCmp implements CanDeactivate, OnDeactivate, OnActivate {
@Component({selector: 'all-hooks-parent-cmp'}) @Component({selector: 'all-hooks-parent-cmp'})
@View({template: `<router-outlet></router-outlet>`, directives: [RouterOutlet]}) @View({template: `<router-outlet></router-outlet>`, directives: [RouterOutlet]})
@RouteConfig([{path: '/child', component: AllHooksChildCmp}]) @RouteConfig([new Route({path: '/child', component: AllHooksChildCmp})])
@CanActivate(AllHooksParentCmp.canActivate) @CanActivate(AllHooksParentCmp.canActivate)
class AllHooksParentCmp implements CanDeactivate, OnDeactivate, OnActivate { class AllHooksParentCmp implements CanDeactivate, OnDeactivate, OnActivate {
canDeactivate(next: Instruction, prev: Instruction) { canDeactivate(next: Instruction, prev: Instruction) {
@ -804,17 +809,17 @@ class ReuseHooksCmp implements OnActivate, OnReuse, OnDeactivate, CanReuse, CanD
@Component({selector: 'lifecycle-cmp'}) @Component({selector: 'lifecycle-cmp'})
@View({template: `<router-outlet></router-outlet>`, directives: [RouterOutlet]}) @View({template: `<router-outlet></router-outlet>`, directives: [RouterOutlet]})
@RouteConfig([ @RouteConfig([
{path: '/a', component: A}, new Route({path: '/a', component: A}),
{path: '/on-activate', component: ActivateCmp}, new Route({path: '/on-activate', component: ActivateCmp}),
{path: '/parent-activate/...', component: ParentActivateCmp}, new Route({path: '/parent-activate/...', component: ParentActivateCmp}),
{path: '/on-deactivate', component: DeactivateCmp}, new Route({path: '/on-deactivate', component: DeactivateCmp}),
{path: '/parent-deactivate/...', component: ParentDeactivateCmp}, new Route({path: '/parent-deactivate/...', component: ParentDeactivateCmp}),
{path: '/on-reuse/:number/...', component: ReuseCmp}, new Route({path: '/on-reuse/:number/...', component: ReuseCmp}),
{path: '/never-reuse/:number/...', component: NeverReuseCmp}, new Route({path: '/never-reuse/:number/...', component: NeverReuseCmp}),
{path: '/can-activate/...', component: CanActivateCmp}, new Route({path: '/can-activate/...', component: CanActivateCmp}),
{path: '/can-deactivate/...', component: CanDeactivateCmp}, new Route({path: '/can-deactivate/...', component: CanDeactivateCmp}),
{path: '/activation-hooks/...', component: AllHooksParentCmp}, new Route({path: '/activation-hooks/...', component: AllHooksParentCmp}),
{path: '/reuse-hooks/:number', component: ReuseHooksCmp} new Route({path: '/reuse-hooks/:number', component: ReuseHooksCmp})
]) ])
class LifecycleCmp { class LifecycleCmp {
} }

View File

@ -0,0 +1,9 @@
library angular2.test.router.route_config_spec;
/**
* This is intentionally left blank. `route_config_spec.ts` contains tests specific
* to using untyped objects for users writing idiomatic ES5 or TS.
* The rest of the router tests have typed annotations, so there's no need to add
* additional tests here for Dart.
*/
main () {}

View File

@ -0,0 +1,151 @@
import {
AsyncTestCompleter,
beforeEach,
ddescribe,
describe,
expect,
iit,
inject,
it,
xdescribe,
xit,
} from 'angular2/test_lib';
import {bootstrap} from 'angular2/core';
import {Component, Directive, View} from 'angular2/annotations';
import {DOM} from 'angular2/src/dom/dom_adapter';
import {bind} from 'angular2/di';
import {DOCUMENT_TOKEN} from 'angular2/src/render/dom/dom_renderer';
import {
routerInjectables,
Router,
RouteConfig,
appBaseHrefToken,
routerDirectives
} from 'angular2/router';
import {LocationStrategy} from 'angular2/src/router/location_strategy';
import {MockLocationStrategy} from 'angular2/src/mock/mock_location_strategy';
export function main() {
describe('RouteConfig with POJO arguments', () => {
var fakeDoc, el, testBindings;
beforeEach(() => {
fakeDoc = DOM.createHtmlDocument();
el = DOM.createElement('app-cmp', fakeDoc);
DOM.appendChild(fakeDoc.body, el);
testBindings = [
routerInjectables,
bind(LocationStrategy).toClass(MockLocationStrategy),
bind(DOCUMENT_TOKEN).toValue(fakeDoc)
];
});
it('should bootstrap an app with a hierarchy', inject([AsyncTestCompleter], (async) => {
bootstrap(HierarchyAppCmp, testBindings)
.then((applicationRef) => {
var router = applicationRef.hostComponent.router;
router.subscribe((_) => {
expect(el).toHaveText('root { parent { hello } }');
expect(applicationRef.hostComponent.location.path()).toEqual('/parent/child');
async.done();
});
router.navigate('/parent/child');
});
}));
it('should work in an app with redirects', inject([AsyncTestCompleter], (async) => {
bootstrap(RedirectAppCmp, testBindings)
.then((applicationRef) => {
var router = applicationRef.hostComponent.router;
router.subscribe((_) => {
expect(el).toHaveText('root { hello }');
expect(applicationRef.hostComponent.location.path()).toEqual('/after');
async.done();
});
router.navigate('/before');
});
}));
it('should work in an app with async components', inject([AsyncTestCompleter], (async) => {
bootstrap(AsyncAppCmp, testBindings)
.then((applicationRef) => {
var router = applicationRef.hostComponent.router;
router.subscribe((_) => {
expect(el).toHaveText('root { hello }');
expect(applicationRef.hostComponent.location.path()).toEqual('/hello');
async.done();
});
router.navigate('/hello');
});
}));
it('should work in an app with a constructor component',
inject([AsyncTestCompleter], (async) => {
bootstrap(ExplicitConstructorAppCmp, testBindings)
.then((applicationRef) => {
var router = applicationRef.hostComponent.router;
router.subscribe((_) => {
expect(el).toHaveText('root { hello }');
expect(applicationRef.hostComponent.location.path()).toEqual('/hello');
async.done();
});
router.navigate('/hello');
});
}));
// TODO: test apps with wrong configs
});
}
@Component({selector: 'hello-cmp'})
@View({template: 'hello'})
class HelloCmp {
}
@Component({selector: 'app-cmp'})
@View({template: `root { <router-outlet></router-outlet> }`, directives: routerDirectives})
@RouteConfig([{path: '/before', redirectTo: '/after'}, {path: '/after', component: HelloCmp}])
class RedirectAppCmp {
constructor(public router: Router, public location: LocationStrategy) {}
}
function HelloLoader(): Promise<any> {
return Promise.resolve(HelloCmp);
}
@Component({selector: 'app-cmp'})
@View({template: `root { <router-outlet></router-outlet> }`, directives: routerDirectives})
@RouteConfig([
{path: '/hello', component: {type: 'loader', loader: HelloLoader}},
])
class AsyncAppCmp {
constructor(public router: Router, public location: LocationStrategy) {}
}
@Component({selector: 'app-cmp'})
@View({template: `root { <router-outlet></router-outlet> }`, directives: routerDirectives})
@RouteConfig([
{path: '/hello', component: {type: 'constructor', constructor: HelloCmp}},
])
class ExplicitConstructorAppCmp {
constructor(public router: Router, public location: LocationStrategy) {}
}
@Component({selector: 'parent-cmp'})
@View({template: `parent { <router-outlet></router-outlet> }`, directives: routerDirectives})
@RouteConfig([{path: '/child', component: HelloCmp}])
class ParentCmp {
}
@Component({selector: 'app-cmp'})
@View({template: `root { <router-outlet></router-outlet> }`, directives: routerDirectives})
@RouteConfig([{path: '/parent/...', component: ParentCmp}])
class HierarchyAppCmp {
constructor(public router: Router, public location: LocationStrategy) {}
}

View File

@ -14,88 +14,88 @@ import {Map, StringMap, StringMapWrapper} from 'angular2/src/facade/collection';
import {RouteRecognizer, RouteMatch} from 'angular2/src/router/route_recognizer'; import {RouteRecognizer, RouteMatch} from 'angular2/src/router/route_recognizer';
import {Route, Redirect} from 'angular2/src/router/route_config_decorator';
export function main() { export function main() {
describe('RouteRecognizer', () => { describe('RouteRecognizer', () => {
var recognizer; var recognizer;
var handler = {'component': DummyCmpA};
var handler2 = {'component': DummyCmpB};
beforeEach(() => { recognizer = new RouteRecognizer(); }); beforeEach(() => { recognizer = new RouteRecognizer(); });
it('should recognize a static segment', () => { it('should recognize a static segment', () => {
recognizer.addConfig('/test', handler); recognizer.config(new Route({path: '/test', component: DummyCmpA}));
var solution = recognizer.recognize('/test')[0]; var solution = recognizer.recognize('/test')[0];
expect(getComponentType(solution)).toEqual(handler['component']); expect(getComponentType(solution)).toEqual(DummyCmpA);
}); });
it('should recognize a single slash', () => { it('should recognize a single slash', () => {
recognizer.addConfig('/', handler); recognizer.config(new Route({path: '/', component: DummyCmpA}));
var solution = recognizer.recognize('/')[0]; var solution = recognizer.recognize('/')[0];
expect(getComponentType(solution)).toEqual(handler['component']); expect(getComponentType(solution)).toEqual(DummyCmpA);
}); });
it('should recognize a dynamic segment', () => { it('should recognize a dynamic segment', () => {
recognizer.addConfig('/user/:name', handler); recognizer.config(new Route({path: '/user/:name', component: DummyCmpA}));
var solution = recognizer.recognize('/user/brian')[0]; var solution = recognizer.recognize('/user/brian')[0];
expect(getComponentType(solution)).toEqual(handler['component']); expect(getComponentType(solution)).toEqual(DummyCmpA);
expect(solution.params()).toEqual({'name': 'brian'}); expect(solution.params()).toEqual({'name': 'brian'});
}); });
it('should recognize a star segment', () => { it('should recognize a star segment', () => {
recognizer.addConfig('/first/*rest', handler); recognizer.config(new Route({path: '/first/*rest', component: DummyCmpA}));
var solution = recognizer.recognize('/first/second/third')[0]; var solution = recognizer.recognize('/first/second/third')[0];
expect(getComponentType(solution)).toEqual(handler['component']); expect(getComponentType(solution)).toEqual(DummyCmpA);
expect(solution.params()).toEqual({'rest': 'second/third'}); expect(solution.params()).toEqual({'rest': 'second/third'});
}); });
it('should throw when given two routes that start with the same static segment', () => { it('should throw when given two routes that start with the same static segment', () => {
recognizer.addConfig('/hello', handler); recognizer.config(new Route({path: '/hello', component: DummyCmpA}));
expect(() => recognizer.addConfig('/hello', handler2)) expect(() => recognizer.config(new Route({path: '/hello', component: DummyCmpB})))
.toThrowError('Configuration \'/hello\' conflicts with existing route \'/hello\''); .toThrowError('Configuration \'/hello\' conflicts with existing route \'/hello\'');
}); });
it('should throw when given two routes that have dynamic segments in the same order', () => { it('should throw when given two routes that have dynamic segments in the same order', () => {
recognizer.addConfig('/hello/:person/how/:doyoudou', handler); recognizer.config(new Route({path: '/hello/:person/how/:doyoudou', component: DummyCmpA}));
expect(() => recognizer.addConfig('/hello/:friend/how/:areyou', handler2)) expect(() => recognizer.config(
new Route({path: '/hello/:friend/how/:areyou', component: DummyCmpA})))
.toThrowError( .toThrowError(
'Configuration \'/hello/:friend/how/:areyou\' conflicts with existing route \'/hello/:person/how/:doyoudou\''); 'Configuration \'/hello/:friend/how/:areyou\' conflicts with existing route \'/hello/:person/how/:doyoudou\'');
}); });
it('should recognize redirects', () => { it('should recognize redirects', () => {
recognizer.addRedirect('/a', '/b'); recognizer.config(new Redirect({path: '/a', redirectTo: '/b'}));
recognizer.addConfig('/b', handler); recognizer.config(new Route({path: '/b', component: DummyCmpA}));
var solutions = recognizer.recognize('/a'); var solutions = recognizer.recognize('/a');
expect(solutions.length).toBe(1); expect(solutions.length).toBe(1);
var solution = solutions[0]; var solution = solutions[0];
expect(getComponentType(solution)).toEqual(handler['component']); expect(getComponentType(solution)).toEqual(DummyCmpA);
expect(solution.matchedUrl).toEqual('/b'); expect(solution.matchedUrl).toEqual('/b');
}); });
it('should not perform root URL redirect on a non-root route', () => { it('should not perform root URL redirect on a non-root route', () => {
recognizer.addRedirect('/', '/foo'); recognizer.config(new Redirect({path: '/', redirectTo: '/foo'}));
recognizer.addConfig('/bar', handler); recognizer.config(new Route({path: '/bar', component: DummyCmpA}));
var solutions = recognizer.recognize('/bar'); var solutions = recognizer.recognize('/bar');
expect(solutions.length).toBe(1); expect(solutions.length).toBe(1);
var solution = solutions[0]; var solution = solutions[0];
expect(getComponentType(solution)).toEqual(handler['component']); expect(getComponentType(solution)).toEqual(DummyCmpA);
expect(solution.matchedUrl).toEqual('/bar'); expect(solution.matchedUrl).toEqual('/bar');
}); });
it('should perform a root URL redirect when only a slash or an empty string is being processed', it('should perform a root URL redirect when only a slash or an empty string is being processed',
() => { () => {
recognizer.addRedirect('/', '/matias'); recognizer.config(new Redirect({path: '/', redirectTo: '/matias'}));
recognizer.addConfig('/matias', handler); recognizer.config(new Route({path: '/matias', component: DummyCmpA}));
recognizer.config(new Route({path: '/fatias', component: DummyCmpA}));
recognizer.addConfig('/fatias', handler);
var solutions; var solutions;
@ -110,17 +110,17 @@ export function main() {
}); });
it('should generate URLs with params', () => { it('should generate URLs with params', () => {
recognizer.addConfig('/app/user/:name', handler, 'user'); recognizer.config(new Route({path: '/app/user/:name', component: DummyCmpA, as: 'user'}));
expect(recognizer.generate('user', {'name': 'misko'})['url']).toEqual('app/user/misko'); expect(recognizer.generate('user', {'name': 'misko'})['url']).toEqual('app/user/misko');
}); });
it('should generate URLs with numeric params', () => { it('should generate URLs with numeric params', () => {
recognizer.addConfig('/app/page/:number', handler, 'page'); recognizer.config(new Route({path: '/app/page/:number', component: DummyCmpA, as: 'page'}));
expect(recognizer.generate('page', {'number': 42})['url']).toEqual('app/page/42'); expect(recognizer.generate('page', {'number': 42})['url']).toEqual('app/page/42');
}); });
it('should throw in the absence of required params URLs', () => { it('should throw in the absence of required params URLs', () => {
recognizer.addConfig('app/user/:name', handler, 'user'); recognizer.config(new Route({path: 'app/user/:name', component: DummyCmpA, as: 'user'}));
expect(() => recognizer.generate('user', {})['url']) expect(() => recognizer.generate('user', {})['url'])
.toThrowError('Route generator for \'name\' was not included in parameters passed.'); .toThrowError('Route generator for \'name\' was not included in parameters passed.');
}); });
@ -128,7 +128,7 @@ export function main() {
describe('matrix params', () => { describe('matrix params', () => {
it('should recognize matrix parameters within the URL path', () => { it('should recognize matrix parameters within the URL path', () => {
var recognizer = new RouteRecognizer(); var recognizer = new RouteRecognizer();
recognizer.addConfig('profile/:name', handler, 'user'); recognizer.config(new Route({path: 'profile/:name', component: DummyCmpA, as: 'user'}));
var solution = recognizer.recognize('/profile/matsko;comments=all')[0]; var solution = recognizer.recognize('/profile/matsko;comments=all')[0];
var params = solution.params(); var params = solution.params();
@ -139,7 +139,7 @@ export function main() {
it('should recognize multiple matrix params and set parameters that contain no value to true', it('should recognize multiple matrix params and set parameters that contain no value to true',
() => { () => {
var recognizer = new RouteRecognizer(); var recognizer = new RouteRecognizer();
recognizer.addConfig('/profile/hello', handler, 'user'); recognizer.config(new Route({path: '/profile/hello', component: DummyCmpA, as: 'user'}));
var solution = var solution =
recognizer.recognize('/profile/hello;modal;showAll=true;hideAll=false')[0]; recognizer.recognize('/profile/hello;modal;showAll=true;hideAll=false')[0];
@ -152,7 +152,7 @@ export function main() {
it('should only consider the matrix parameters at the end of the path handler', () => { it('should only consider the matrix parameters at the end of the path handler', () => {
var recognizer = new RouteRecognizer(); var recognizer = new RouteRecognizer();
recognizer.addConfig('/profile/hi/:name', handler, 'user'); recognizer.config(new Route({path: '/profile/hi/:name', component: DummyCmpA, as: 'user'}));
var solution = recognizer.recognize('/profile;a=1/hi;b=2;c=3/william;d=4')[0]; var solution = recognizer.recognize('/profile;a=1/hi;b=2;c=3/william;d=4')[0];
var params = solution.params(); var params = solution.params();
@ -162,7 +162,8 @@ export function main() {
it('should generate and populate the given static-based route with matrix params', () => { it('should generate and populate the given static-based route with matrix params', () => {
var recognizer = new RouteRecognizer(); var recognizer = new RouteRecognizer();
recognizer.addConfig('forum/featured', handler, 'forum-page'); recognizer.config(
new Route({path: 'forum/featured', component: DummyCmpA, as: 'forum-page'}));
var params = StringMapWrapper.create(); var params = StringMapWrapper.create();
params['start'] = 10; params['start'] = 10;
@ -174,7 +175,8 @@ export function main() {
it('should generate and populate the given dynamic-based route with matrix params', () => { it('should generate and populate the given dynamic-based route with matrix params', () => {
var recognizer = new RouteRecognizer(); var recognizer = new RouteRecognizer();
recognizer.addConfig('forum/:topic', handler, 'forum-page'); recognizer.config(
new Route({path: 'forum/:topic', component: DummyCmpA, as: 'forum-page'}));
var params = StringMapWrapper.create(); var params = StringMapWrapper.create();
params['topic'] = 'crazy'; params['topic'] = 'crazy';
@ -188,7 +190,8 @@ export function main() {
it('should not apply any matrix params if a dynamic route segment takes up the slot when a path is generated', it('should not apply any matrix params if a dynamic route segment takes up the slot when a path is generated',
() => { () => {
var recognizer = new RouteRecognizer(); var recognizer = new RouteRecognizer();
recognizer.addConfig('hello/:name', handler, 'profile-page'); recognizer.config(
new Route({path: 'hello/:name', component: DummyCmpA, as: 'profile-page'}));
var params = StringMapWrapper.create(); var params = StringMapWrapper.create();
params['name'] = 'matsko'; params['name'] = 'matsko';

View File

@ -13,7 +13,7 @@ import {
import {Promise, PromiseWrapper} from 'angular2/src/facade/async'; import {Promise, PromiseWrapper} from 'angular2/src/facade/async';
import {RouteRegistry} from 'angular2/src/router/route_registry'; import {RouteRegistry} from 'angular2/src/router/route_registry';
import {RouteConfig} from 'angular2/src/router/route_config_decorator'; import {RouteConfig, Route, AsyncRoute} from 'angular2/src/router/route_config_decorator';
export function main() { export function main() {
describe('RouteRegistry', () => { describe('RouteRegistry', () => {
@ -22,8 +22,8 @@ export function main() {
beforeEach(() => { registry = new RouteRegistry(); }); beforeEach(() => { registry = new RouteRegistry(); });
it('should match the full URL', inject([AsyncTestCompleter], (async) => { it('should match the full URL', inject([AsyncTestCompleter], (async) => {
registry.config(RootHostCmp, {'path': '/', 'component': DummyCmpA}); registry.config(RootHostCmp, new Route({path: '/', component: DummyCmpA}));
registry.config(RootHostCmp, {'path': '/test', 'component': DummyCmpB}); registry.config(RootHostCmp, new Route({path: '/test', component: DummyCmpB}));
registry.recognize('/test', RootHostCmp) registry.recognize('/test', RootHostCmp)
.then((instruction) => { .then((instruction) => {
@ -34,7 +34,7 @@ export function main() {
it('should generate URLs starting at the given component', () => { it('should generate URLs starting at the given component', () => {
registry.config(RootHostCmp, registry.config(RootHostCmp,
{'path': '/first/...', 'component': DummyParentCmp, 'as': 'firstCmp'}); new Route({path: '/first/...', component: DummyParentCmp, as: 'firstCmp'}));
expect(registry.generate(['firstCmp', 'secondCmp'], RootHostCmp)).toEqual('first/second'); expect(registry.generate(['firstCmp', 'secondCmp'], RootHostCmp)).toEqual('first/second');
expect(registry.generate(['secondCmp'], DummyParentCmp)).toEqual('second'); expect(registry.generate(['secondCmp'], DummyParentCmp)).toEqual('second');
@ -43,7 +43,7 @@ export function main() {
it('should generate URLs with params', () => { it('should generate URLs with params', () => {
registry.config( registry.config(
RootHostCmp, RootHostCmp,
{'path': '/first/:param/...', 'component': DummyParentParamCmp, 'as': 'firstCmp'}); new Route({path: '/first/:param/...', component: DummyParentParamCmp, as: 'firstCmp'}));
var url = var url =
registry.generate(['firstCmp', {param: 'one'}, 'secondCmp', {param: 'two'}], RootHostCmp); registry.generate(['firstCmp', {param: 'one'}, 'secondCmp', {param: 'two'}], RootHostCmp);
@ -52,11 +52,9 @@ export function main() {
it('should generate URLs of loaded components after they are loaded', it('should generate URLs of loaded components after they are loaded',
inject([AsyncTestCompleter], (async) => { inject([AsyncTestCompleter], (async) => {
registry.config(RootHostCmp, { registry.config(
'path': '/first/...', RootHostCmp,
'component': {'type': 'loader', 'loader': AsyncParentLoader}, new AsyncRoute({path: '/first/...', loader: AsyncParentLoader, as: 'firstCmp'}));
'as': 'firstCmp'
});
expect(() => registry.generate(['firstCmp', 'secondCmp'], RootHostCmp)) expect(() => registry.generate(['firstCmp', 'secondCmp'], RootHostCmp))
.toThrowError('Could not find route named "secondCmp".'); .toThrowError('Could not find route named "secondCmp".');
@ -77,8 +75,8 @@ export function main() {
it('should prefer static segments to dynamic', inject([AsyncTestCompleter], (async) => { it('should prefer static segments to dynamic', inject([AsyncTestCompleter], (async) => {
registry.config(RootHostCmp, {'path': '/:site', 'component': DummyCmpB}); registry.config(RootHostCmp, new Route({path: '/:site', component: DummyCmpB}));
registry.config(RootHostCmp, {'path': '/home', 'component': DummyCmpA}); registry.config(RootHostCmp, new Route({path: '/home', component: DummyCmpA}));
registry.recognize('/home', RootHostCmp) registry.recognize('/home', RootHostCmp)
.then((instruction) => { .then((instruction) => {
@ -88,8 +86,8 @@ export function main() {
})); }));
it('should prefer dynamic segments to star', inject([AsyncTestCompleter], (async) => { it('should prefer dynamic segments to star', inject([AsyncTestCompleter], (async) => {
registry.config(RootHostCmp, {'path': '/:site', 'component': DummyCmpA}); registry.config(RootHostCmp, new Route({path: '/:site', component: DummyCmpA}));
registry.config(RootHostCmp, {'path': '/*site', 'component': DummyCmpB}); registry.config(RootHostCmp, new Route({path: '/*site', component: DummyCmpB}));
registry.recognize('/home', RootHostCmp) registry.recognize('/home', RootHostCmp)
.then((instruction) => { .then((instruction) => {
@ -99,8 +97,8 @@ export function main() {
})); }));
it('should prefer routes with more dynamic segments', inject([AsyncTestCompleter], (async) => { it('should prefer routes with more dynamic segments', inject([AsyncTestCompleter], (async) => {
registry.config(RootHostCmp, {'path': '/:first/*rest', 'component': DummyCmpA}); registry.config(RootHostCmp, new Route({path: '/:first/*rest', component: DummyCmpA}));
registry.config(RootHostCmp, {'path': '/*all', 'component': DummyCmpB}); registry.config(RootHostCmp, new Route({path: '/*all', component: DummyCmpB}));
registry.recognize('/some/path', RootHostCmp) registry.recognize('/some/path', RootHostCmp)
.then((instruction) => { .then((instruction) => {
@ -110,8 +108,8 @@ export function main() {
})); }));
it('should prefer routes with more static segments', inject([AsyncTestCompleter], (async) => { it('should prefer routes with more static segments', inject([AsyncTestCompleter], (async) => {
registry.config(RootHostCmp, {'path': '/first/:second', 'component': DummyCmpA}); registry.config(RootHostCmp, new Route({path: '/first/:second', component: DummyCmpA}));
registry.config(RootHostCmp, {'path': '/:first/:second', 'component': DummyCmpB}); registry.config(RootHostCmp, new Route({path: '/:first/:second', component: DummyCmpB}));
registry.recognize('/first/second', RootHostCmp) registry.recognize('/first/second', RootHostCmp)
.then((instruction) => { .then((instruction) => {
@ -122,8 +120,10 @@ export function main() {
it('should prefer routes with static segments before dynamic segments', it('should prefer routes with static segments before dynamic segments',
inject([AsyncTestCompleter], (async) => { inject([AsyncTestCompleter], (async) => {
registry.config(RootHostCmp, {'path': '/first/second/:third', 'component': DummyCmpB}); registry.config(RootHostCmp,
registry.config(RootHostCmp, {'path': '/first/:second/third', 'component': DummyCmpA}); new Route({path: '/first/second/:third', component: DummyCmpB}));
registry.config(RootHostCmp,
new Route({path: '/first/:second/third', component: DummyCmpA}));
registry.recognize('/first/second/third', RootHostCmp) registry.recognize('/first/second/third', RootHostCmp)
.then((instruction) => { .then((instruction) => {
@ -133,7 +133,7 @@ export function main() {
})); }));
it('should match the full URL using child components', inject([AsyncTestCompleter], (async) => { it('should match the full URL using child components', inject([AsyncTestCompleter], (async) => {
registry.config(RootHostCmp, {'path': '/first/...', 'component': DummyParentCmp}); registry.config(RootHostCmp, new Route({path: '/first/...', component: DummyParentCmp}));
registry.recognize('/first/second', RootHostCmp) registry.recognize('/first/second', RootHostCmp)
.then((instruction) => { .then((instruction) => {
@ -145,7 +145,7 @@ export function main() {
it('should match the URL using async child components', it('should match the URL using async child components',
inject([AsyncTestCompleter], (async) => { inject([AsyncTestCompleter], (async) => {
registry.config(RootHostCmp, {'path': '/first/...', 'component': DummyAsyncCmp}); registry.config(RootHostCmp, new Route({path: '/first/...', component: DummyAsyncCmp}));
registry.recognize('/first/second', RootHostCmp) registry.recognize('/first/second', RootHostCmp)
.then((instruction) => { .then((instruction) => {
@ -157,9 +157,8 @@ export function main() {
it('should match the URL using an async parent component', it('should match the URL using an async parent component',
inject([AsyncTestCompleter], (async) => { inject([AsyncTestCompleter], (async) => {
registry.config( registry.config(RootHostCmp,
RootHostCmp, new AsyncRoute({path: '/first/...', loader: AsyncParentLoader}));
{'path': '/first/...', 'component': {'loader': AsyncParentLoader, 'type': 'loader'}});
registry.recognize('/first/second', RootHostCmp) registry.recognize('/first/second', RootHostCmp)
.then((instruction) => { .then((instruction) => {
@ -169,31 +168,36 @@ export function main() {
}); });
})); }));
it('should throw when a config does not have a component or redirectTo property', () => { // TODO: not sure what to do with these tests
expect(() => registry.config(RootHostCmp, {'path': '/some/path'})) // it('should throw when a config does not have a component or redirectTo property', () => {
.toThrowError( // expect(() => registry.config(rootHostComponent, {'path': '/some/path'}))
'Route config should contain exactly one \'component\', or \'redirectTo\' property'); // .toThrowError(
}); // 'Route config should contain exactly one \'component\', or \'redirectTo\'
// property');
it('should throw when a config has an invalid component type', () => { //});
expect(() => registry.config( //
RootHostCmp, // it('should throw when a config has an invalid component type', () => {
{'path': '/some/path', 'component': {'type': 'intentionallyWrongComponentType'}})) // expect(() => registry.config(
.toThrowError('Invalid component type \'intentionallyWrongComponentType\''); // rootHostComponent,
}); // {'path': '/some/path', 'component': {'type':
// 'intentionallyWrongComponentType'}}))
// .toThrowError('Invalid component type \'intentionallyWrongComponentType\'');
//});
it('should throw when a parent config is missing the `...` suffix any of its children add routes', it('should throw when a parent config is missing the `...` suffix any of its children add routes',
() => { () => {
expect(() => registry.config(RootHostCmp, {'path': '/', 'component': DummyParentCmp})) expect(() =>
registry.config(RootHostCmp, new Route({path: '/', component: DummyParentCmp})))
.toThrowError( .toThrowError(
'Child routes are not allowed for "/". Use "..." on the parent\'s route path.'); 'Child routes are not allowed for "/". Use "..." on the parent\'s route path.');
}); });
it('should throw when a parent config uses `...` suffix before the end of the route', () => { it('should throw when a parent config uses `...` suffix before the end of the route', () => {
expect(() => registry.config(RootHostCmp, expect(() => registry.config(RootHostCmp,
{'path': '/home/.../fun/', 'component': DummyParentCmp})) new Route({path: '/home/.../fun/', component: DummyParentCmp})))
.toThrowError('Unexpected "..." before the end of the path for "home/.../fun/".'); .toThrowError('Unexpected "..." before the end of the path for "home/.../fun/".');
}); });
}); });
} }
@ -207,18 +211,18 @@ function AsyncChildLoader() {
class RootHostCmp {} class RootHostCmp {}
@RouteConfig([{'path': '/second', 'component': {'loader': AsyncChildLoader, 'type': 'loader'}}]) @RouteConfig([new AsyncRoute({path: '/second', loader: AsyncChildLoader})])
class DummyAsyncCmp { class DummyAsyncCmp {
} }
class DummyCmpA {} class DummyCmpA {}
class DummyCmpB {} class DummyCmpB {}
@RouteConfig([{'path': '/second', 'component': DummyCmpB, 'as': 'secondCmp'}]) @RouteConfig([new Route({path: '/second', component: DummyCmpB, as: 'secondCmp'})])
class DummyParentCmp { class DummyParentCmp {
} }
@RouteConfig([{'path': '/second/:param', 'component': DummyCmpB, 'as': 'secondCmp'}]) @RouteConfig([new Route({path: '/second/:param', component: DummyCmpB, as: 'secondCmp'})])
class DummyParentParamCmp { class DummyParentParamCmp {
} }

View File

@ -16,7 +16,7 @@ import {Component, Directive, View} from 'angular2/src/core/annotations/decorato
import {DOM} from 'angular2/src/dom/dom_adapter'; import {DOM} from 'angular2/src/dom/dom_adapter';
import {bind} from 'angular2/di'; import {bind} from 'angular2/di';
import {DOCUMENT_TOKEN} from 'angular2/src/render/dom/dom_renderer'; import {DOCUMENT_TOKEN} from 'angular2/src/render/dom/dom_renderer';
import {RouteConfig} from 'angular2/src/router/route_config_decorator'; import {RouteConfig, Route, Redirect} from 'angular2/src/router/route_config_decorator';
import {PromiseWrapper} from 'angular2/src/facade/async'; import {PromiseWrapper} from 'angular2/src/facade/async';
import {BaseException} from 'angular2/src/facade/lang'; import {BaseException} from 'angular2/src/facade/lang';
import {routerInjectables, Router, appBaseHrefToken, routerDirectives} from 'angular2/router'; import {routerInjectables, Router, appBaseHrefToken, routerDirectives} from 'angular2/router';
@ -101,20 +101,20 @@ class HelloCmp {
@Component({selector: 'app-cmp'}) @Component({selector: 'app-cmp'})
@View({template: "outer { <router-outlet></router-outlet> }", directives: routerDirectives}) @View({template: "outer { <router-outlet></router-outlet> }", directives: routerDirectives})
@RouteConfig([{path: '/', component: HelloCmp}]) @RouteConfig([new Route({path: '/', component: HelloCmp})])
class AppCmp { class AppCmp {
constructor(public router: Router, public location: LocationStrategy) {} constructor(public router: Router, public location: LocationStrategy) {}
} }
@Component({selector: 'parent-cmp'}) @Component({selector: 'parent-cmp'})
@View({template: `parent { <router-outlet></router-outlet> }`, directives: routerDirectives}) @View({template: `parent { <router-outlet></router-outlet> }`, directives: routerDirectives})
@RouteConfig([{path: '/child', component: HelloCmp}]) @RouteConfig([new Route({path: '/child', component: HelloCmp})])
class ParentCmp { class ParentCmp {
} }
@Component({selector: 'app-cmp'}) @Component({selector: 'app-cmp'})
@View({template: `root { <router-outlet></router-outlet> }`, directives: routerDirectives}) @View({template: `root { <router-outlet></router-outlet> }`, directives: routerDirectives})
@RouteConfig([{path: '/parent/...', component: ParentCmp}]) @RouteConfig([new Route({path: '/parent/...', component: ParentCmp})])
class HierarchyAppCmp { class HierarchyAppCmp {
constructor(public router: Router, public location: LocationStrategy) {} constructor(public router: Router, public location: LocationStrategy) {}
} }
@ -126,8 +126,8 @@ class BrokenCmp {
} }
@Component({selector: 'app-cmp'}) @Component({selector: 'app-cmp'})
@View({template: "outer { <router-outlet></router-outlet> }", directives: routerDirectives}) @View({template: `outer { <router-outlet></router-outlet> }`, directives: routerDirectives})
@RouteConfig([{path: '/cause-error', component: BrokenCmp}]) @RouteConfig([new Route({path: '/cause-error', component: BrokenCmp})])
class BrokenAppCmp { class BrokenAppCmp {
constructor(public router: Router, public location: LocationStrategy) {} constructor(public router: Router, public location: LocationStrategy) {}
} }

View File

@ -22,7 +22,7 @@ import {SpyLocation} from 'angular2/src/mock/location_mock';
import {Location} from 'angular2/src/router/location'; import {Location} from 'angular2/src/router/location';
import {RouteRegistry} from 'angular2/src/router/route_registry'; import {RouteRegistry} from 'angular2/src/router/route_registry';
import {RouteConfig} from 'angular2/src/router/route_config_decorator'; import {RouteConfig, Route} from 'angular2/src/router/route_config_decorator';
import {DirectiveResolver} from 'angular2/src/core/compiler/directive_resolver'; import {DirectiveResolver} from 'angular2/src/core/compiler/directive_resolver';
import {bind} from 'angular2/di'; import {bind} from 'angular2/di';
@ -52,7 +52,7 @@ export function main() {
it('should navigate based on the initial URL state', inject([AsyncTestCompleter], (async) => { it('should navigate based on the initial URL state', inject([AsyncTestCompleter], (async) => {
var outlet = makeDummyOutlet(); var outlet = makeDummyOutlet();
router.config({'path': '/', 'component': DummyComponent}) router.config([new Route({path: '/', component: DummyComponent})])
.then((_) => router.registerOutlet(outlet)) .then((_) => router.registerOutlet(outlet))
.then((_) => { .then((_) => {
expect(outlet.spy('commit')).toHaveBeenCalled(); expect(outlet.spy('commit')).toHaveBeenCalled();
@ -67,7 +67,7 @@ export function main() {
var outlet = makeDummyOutlet(); var outlet = makeDummyOutlet();
router.registerOutlet(outlet) router.registerOutlet(outlet)
.then((_) => router.config({'path': '/a', 'component': DummyComponent})) .then((_) => router.config([new Route({path: '/a', component: DummyComponent})]))
.then((_) => router.navigate('/a')) .then((_) => router.navigate('/a'))
.then((_) => { .then((_) => {
expect(outlet.spy('commit')).toHaveBeenCalled(); expect(outlet.spy('commit')).toHaveBeenCalled();
@ -84,7 +84,7 @@ export function main() {
.then((_) => router.navigate('/a')) .then((_) => router.navigate('/a'))
.then((_) => { .then((_) => {
expect(outlet.spy('commit')).not.toHaveBeenCalled(); expect(outlet.spy('commit')).not.toHaveBeenCalled();
return router.config({'path': '/a', 'component': DummyComponent}); return router.config([new Route({path: '/a', component: DummyComponent})]);
}) })
.then((_) => { .then((_) => {
expect(outlet.spy('commit')).toHaveBeenCalled(); expect(outlet.spy('commit')).toHaveBeenCalled();
@ -109,7 +109,7 @@ export function main() {
it('should generate URLs from the root component when the path starts with /', () => { it('should generate URLs from the root component when the path starts with /', () => {
router.config({'path': '/first/...', 'component': DummyParentComp, 'as': 'firstCmp'}); router.config([new Route({path: '/first/...', component: DummyParentComp, as: 'firstCmp'})]);
expect(router.generate(['/firstCmp', 'secondCmp'])).toEqual('/first/second'); expect(router.generate(['/firstCmp', 'secondCmp'])).toEqual('/first/second');
expect(router.generate(['/firstCmp', 'secondCmp'])).toEqual('/first/second'); expect(router.generate(['/firstCmp', 'secondCmp'])).toEqual('/first/second');
@ -118,7 +118,8 @@ export function main() {
describe('matrix params', () => { describe('matrix params', () => {
it('should apply inline matrix params for each router path within the generated URL', () => { it('should apply inline matrix params for each router path within the generated URL', () => {
router.config({'path': '/first/...', 'component': DummyParentComp, 'as': 'firstCmp'}); router.config(
[new Route({path: '/first/...', component: DummyParentComp, as: 'firstCmp'})]);
var path = var path =
router.generate(['/firstCmp', {'key': 'value'}, 'secondCmp', {'project': 'angular'}]); router.generate(['/firstCmp', {'key': 'value'}, 'secondCmp', {'project': 'angular'}]);
@ -127,8 +128,9 @@ export function main() {
it('should apply inline matrix params for each router path within the generated URL and also include named params', it('should apply inline matrix params for each router path within the generated URL and also include named params',
() => { () => {
router.config( router.config([
{'path': '/first/:token/...', 'component': DummyParentComp, 'as': 'firstCmp'}); new Route({path: '/first/:token/...', component: DummyParentComp, as: 'firstCmp'})
]);
var path = var path =
router.generate(['/firstCmp', {'token': 'min'}, 'secondCmp', {'author': 'max'}]); router.generate(['/firstCmp', {'token': 'min'}, 'secondCmp', {'author': 'max'}]);
@ -146,7 +148,7 @@ class DummyOutlet extends SpyObject {
class DummyComponent {} class DummyComponent {}
@RouteConfig([{'path': '/second', 'component': DummyComponent, 'as': 'secondCmp'}]) @RouteConfig([new Route({path: '/second', component: DummyComponent, as: 'secondCmp'})])
class DummyParentComp { class DummyParentComp {
} }