X Tutup
Skip to content

Commit 4d28167

Browse files
committed
feat(router): add interfaces for route definitions in RouteConfig
Closes #2261
1 parent 61c7357 commit 4d28167

16 files changed

+457
-227
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {RouteConfig as RouteConfigAnnotation} from './route_config_impl';
22
import {makeDecorator} from 'angular2/src/util/decorators';
33

4+
export {Route, Redirect, AsyncRoute, RouteDefinition} from './route_config_impl';
45
export var RouteConfig = makeDecorator(RouteConfigAnnotation);

modules/angular2/src/router/route_config_impl.ts

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
import {CONST} from 'angular2/src/facade/lang';
2-
import {List, Map} from 'angular2/src/facade/collection';
1+
import {CONST, Type} from 'angular2/src/facade/lang';
2+
import {List} from 'angular2/src/facade/collection';
3+
import {RouteDefinition} from './route_definition';
4+
export {RouteDefinition} from './route_definition';
35

46
/**
57
* You use the RouteConfig annotation to add routes to a component.
@@ -11,5 +13,41 @@ import {List, Map} from 'angular2/src/facade/collection';
1113
*/
1214
@CONST()
1315
export class RouteConfig {
14-
constructor(public configs: List<Map<any, any>>) {}
16+
constructor(public configs: List<RouteDefinition>) {}
17+
}
18+
19+
20+
@CONST()
21+
export class Route implements RouteDefinition {
22+
path: string;
23+
component: Type;
24+
as: string;
25+
constructor({path, component, as}: {path: string, component: Type, as?: string}) {
26+
this.path = path;
27+
this.component = component;
28+
this.as = as;
29+
}
30+
}
31+
32+
@CONST()
33+
export class AsyncRoute implements RouteDefinition {
34+
path: string;
35+
loader: Function;
36+
as: string;
37+
constructor({path, loader, as}: {path: string, loader: Function, as?: string}) {
38+
this.path = path;
39+
this.loader = loader;
40+
this.as = as;
41+
}
42+
}
43+
44+
@CONST()
45+
export class Redirect implements RouteDefinition {
46+
path: string;
47+
redirectTo: string;
48+
as: string = null;
49+
constructor({path, redirectTo}: {path: string, redirectTo: string}) {
50+
this.path = path;
51+
this.redirectTo = redirectTo;
52+
}
1553
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
library angular2.src.router.route_config_normalizer;
2+
3+
import "route_config_decorator.dart";
4+
5+
RouteDefinition normalizeRouteConfig(RouteDefinition config) {
6+
return config;
7+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import {AsyncRoute, Route, Redirect, RouteDefinition} from './route_config_decorator';
2+
import {ComponentDefinition} from './route_definition';
3+
import {Type, BaseException} from 'angular2/src/facade/lang';
4+
5+
/**
6+
* Given a JS Object that represents... returns a corresponding Route, AsyncRoute, or Redirect
7+
*/
8+
export function normalizeRouteConfig(config: RouteDefinition): RouteDefinition {
9+
if (config instanceof Route || config instanceof Redirect || config instanceof AsyncRoute) {
10+
return <RouteDefinition>config;
11+
}
12+
13+
if ((!config.component) == (!config.redirectTo)) {
14+
throw new BaseException(
15+
`Route config should contain exactly one 'component', or 'redirectTo' property`);
16+
}
17+
if (config.component) {
18+
if (typeof config.component == 'object') {
19+
let componentDefinitionObject = <ComponentDefinition>config.component;
20+
if (componentDefinitionObject.type == 'constructor') {
21+
return new Route({
22+
path: config.path,
23+
component:<Type>componentDefinitionObject.constructor,
24+
as: config.as
25+
});
26+
} else if (componentDefinitionObject.type == 'loader') {
27+
return new AsyncRoute(
28+
{path: config.path, loader: componentDefinitionObject.loader, as: config.as});
29+
} else {
30+
throw new BaseException(
31+
`Invalid component type '${componentDefinitionObject.type}'. Valid types are "constructor" and "loader".`);
32+
}
33+
}
34+
return new Route(<{
35+
path: string;
36+
component: Type;
37+
as?: string
38+
}>config);
39+
}
40+
41+
if (config.redirectTo) {
42+
return new Redirect({path: config.path, redirectTo: config.redirectTo});
43+
}
44+
45+
return config;
46+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
library angular2.src.router.route_definition;
2+
3+
abstract class RouteDefinition {
4+
final String path;
5+
final String as;
6+
const RouteDefinition({this.path, this.as});
7+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import {CONST, Type} from 'angular2/src/facade/lang';
2+
3+
export interface RouteDefinition {
4+
path: string;
5+
component?: Type | ComponentDefinition;
6+
loader?: Function;
7+
redirectTo?: string;
8+
as?: string;
9+
}
10+
11+
export interface ComponentDefinition {
12+
type: string;
13+
loader?: Function;
14+
component?: Type;
15+
}

modules/angular2/src/router/route_recognizer.ts

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919

2020
import {PathRecognizer} from './path_recognizer';
2121
import {RouteHandler} from './route_handler';
22+
import {Route, AsyncRoute, Redirect, RouteDefinition} from './route_config_impl';
2223
import {AsyncRouteHandler} from './async_route_handler';
2324
import {SyncRouteHandler} from './sync_route_handler';
2425

@@ -32,25 +33,27 @@ export class RouteRecognizer {
3233
redirects: Map<string, string> = new Map();
3334
matchers: Map<RegExp, PathRecognizer> = new Map();
3435

35-
addRedirect(path: string, target: string): void {
36-
if (path == '/') {
37-
path = '';
36+
config(config: RouteDefinition): boolean {
37+
var handler;
38+
if (config instanceof Redirect) {
39+
let path = config.path == '/' ? '' : config.path;
40+
this.redirects.set(path, config.redirectTo);
41+
return true;
42+
} else if (config instanceof Route) {
43+
handler = new SyncRouteHandler(config.component);
44+
} else if (config instanceof AsyncRoute) {
45+
handler = new AsyncRouteHandler(config.loader);
3846
}
39-
this.redirects.set(path, target);
40-
}
41-
42-
addConfig(path: string, handlerObj: any, alias: string = null): boolean {
43-
var handler = configObjToHandler(handlerObj['component']);
44-
var recognizer = new PathRecognizer(path, handler);
47+
var recognizer = new PathRecognizer(config.path, handler);
4548
MapWrapper.forEach(this.matchers, (matcher, _) => {
4649
if (recognizer.regex.toString() == matcher.regex.toString()) {
4750
throw new BaseException(
48-
`Configuration '${path}' conflicts with existing route '${matcher.path}'`);
51+
`Configuration '${config.path}' conflicts with existing route '${matcher.path}'`);
4952
}
5053
});
5154
this.matchers.set(recognizer.regex, recognizer);
52-
if (isPresent(alias)) {
53-
this.names.set(alias, recognizer);
55+
if (isPresent(config.as)) {
56+
this.names.set(config.as, recognizer);
5457
}
5558
return recognizer.terminal;
5659
}

modules/angular2/src/router/route_registry.ts

Lines changed: 8 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,10 @@ import {
2020
BaseException,
2121
getTypeNameForDebugging
2222
} from 'angular2/src/facade/lang';
23-
import {RouteConfig} from './route_config_impl';
23+
import {RouteConfig, AsyncRoute, Route, Redirect, RouteDefinition} from './route_config_impl';
2424
import {reflector} from 'angular2/src/reflection/reflection';
2525
import {Injectable} from 'angular2/di';
26+
import {normalizeRouteConfig} from './route_config_nomalizer';
2627

2728
/**
2829
* The RouteRegistry holds route configurations for each component in an Angular app.
@@ -36,8 +37,8 @@ export class RouteRegistry {
3637
/**
3738
* Given a component and a configuration object, add the route to this registry
3839
*/
39-
config(parentComponent, config: StringMap<string, any>): void {
40-
assertValidConfig(config);
40+
config(parentComponent, config: RouteDefinition): void {
41+
config = normalizeRouteConfig(config);
4142

4243
var recognizer: RouteRecognizer = this._rules.get(parentComponent);
4344

@@ -46,22 +47,13 @@ export class RouteRegistry {
4647
this._rules.set(parentComponent, recognizer);
4748
}
4849

49-
if (StringMapWrapper.contains(config, 'redirectTo')) {
50-
recognizer.addRedirect(config['path'], config['redirectTo']);
51-
return;
52-
}
53-
54-
config = StringMapWrapper.merge(
55-
config, {'component': normalizeComponentDeclaration(config['component'])});
50+
var terminal = recognizer.config(config);
5651

57-
var component = config['component'];
58-
var terminal = recognizer.addConfig(config['path'], config, config['as']);
59-
60-
if (component['type'] == 'constructor') {
52+
if (config instanceof Route) {
6153
if (terminal) {
62-
assertTerminalComponent(component['constructor'], config['path']);
54+
assertTerminalComponent(config.component, config.path);
6355
} else {
64-
this.configFromComponent(component['constructor']);
56+
this.configFromComponent(config.component);
6557
}
6658
}
6759
}
@@ -191,50 +183,6 @@ export class RouteRegistry {
191183
}
192184

193185

194-
/*
195-
* A config should have a "path" property, and exactly one of:
196-
* - `component`
197-
* - `redirectTo`
198-
*/
199-
var ALLOWED_TARGETS = ['component', 'redirectTo'];
200-
function assertValidConfig(config: StringMap<string, any>): void {
201-
if (!StringMapWrapper.contains(config, 'path')) {
202-
throw new BaseException(`Route config should contain a "path" property`);
203-
}
204-
var targets = 0;
205-
ListWrapper.forEach(ALLOWED_TARGETS, (target) => {
206-
if (StringMapWrapper.contains(config, target)) {
207-
targets += 1;
208-
}
209-
});
210-
if (targets != 1) {
211-
throw new BaseException(
212-
`Route config should contain exactly one 'component', or 'redirectTo' property`);
213-
}
214-
}
215-
216-
/*
217-
* Returns a StringMap like: `{ 'constructor': SomeType, 'type': 'constructor' }`
218-
*/
219-
var VALID_COMPONENT_TYPES = ['constructor', 'loader'];
220-
function normalizeComponentDeclaration(config: any): StringMap<string, any> {
221-
if (isType(config)) {
222-
return {'constructor': config, 'type': 'constructor'};
223-
} else if (isStringMap(config)) {
224-
if (isBlank(config['type'])) {
225-
throw new BaseException(
226-
`Component declaration when provided as a map should include a 'type' property`);
227-
}
228-
var componentType = config['type'];
229-
if (!ListWrapper.contains(VALID_COMPONENT_TYPES, componentType)) {
230-
throw new BaseException(`Invalid component type '${componentType}'`);
231-
}
232-
return config;
233-
} else {
234-
throw new BaseException(`Component declaration should be either a Map or a Type`);
235-
}
236-
}
237-
238186
/*
239187
* Given a list of instructions, returns the most specific instruction
240188
*/

modules/angular2/src/router/router.ts

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {Instruction} from './instruction';
1616
import {RouterOutlet} from './router_outlet';
1717
import {Location} from './location';
1818
import {getCanActivateHook} from './route_lifecycle_reflector';
19+
import {RouteDefinition} from './route_config_impl';
1920

2021
let _resolveToTrue = PromiseWrapper.resolve(true);
2122
let _resolveToFalse = PromiseWrapper.resolve(false);
@@ -79,25 +80,15 @@ export class Router {
7980
* # Usage
8081
*
8182
* ```
82-
* router.config({ 'path': '/', 'component': IndexCmp});
83-
* ```
84-
*
85-
* Or:
86-
*
87-
* ```
8883
* router.config([
8984
* { 'path': '/', 'component': IndexComp },
9085
* { 'path': '/user/:id', 'component': UserComp },
9186
* ]);
9287
* ```
9388
*/
94-
config(config: StringMap<string, any>| List<StringMap<string, any>>): Promise<any> {
95-
if (isArray(config)) {
96-
(<List<any>>config)
97-
.forEach((configObject) => { this.registry.config(this.hostComponent, configObject); });
98-
} else {
99-
this.registry.config(this.hostComponent, config);
100-
}
89+
config(definitions: List<RouteDefinition>): Promise<any> {
90+
definitions.forEach(
91+
(routeDefinition) => { this.registry.config(this.hostComponent, routeDefinition); });
10192
return this.renavigate();
10293
}
10394

0 commit comments

Comments
 (0)
X Tutup