refactor(router): RouteData as a type

BREAKING CHANGE

The ROUTE_DATA token has been removed and replaced with a type RouteData,
allowing a type injection like we do with RouteParams.

Before:

    constructor(routeParams: RouteParams, @Inject(ROUTE_DATA) routeData) {
      let id = routeParams.get('id');
      let name = ROUTE_DATA.name;
    }

After:

    constructor(routeParams: RouteParams, routeData: RouteData) {
      let id = routeParams.get('id');
      let name = routeData.get('name');
    }

Fixes #4392

Closes #4428
This commit is contained in:
cexbrayat
2015-09-30 14:48:58 +02:00
committed by Brian Ford
parent fbe748f273
commit b87da8f47c
11 changed files with 79 additions and 67 deletions

View File

@ -7,7 +7,7 @@ export class AsyncRouteHandler implements RouteHandler {
_resolvedComponent: Promise<any> = null;
componentType: Type;
constructor(private _loader: Function, public data?: Object) {}
constructor(private _loader: Function, public data?: {[key: string]: any}) {}
resolveComponentType(): Promise<any> {
if (isPresent(this._resolvedComponent)) {

View File

@ -1,6 +1,6 @@
import {Map, MapWrapper, StringMapWrapper, ListWrapper} from 'angular2/src/core/facade/collection';
import {unimplemented} from 'angular2/src/core/facade/exceptions';
import {isPresent, isBlank, normalizeBlank, Type} from 'angular2/src/core/facade/lang';
import {isPresent, isBlank, normalizeBlank, Type, CONST_EXPR} from 'angular2/src/core/facade/lang';
import {Promise} from 'angular2/src/core/facade/async';
import {PathRecognizer} from './path_recognizer';
@ -41,6 +41,44 @@ export class RouteParams {
get(param: string): string { return normalizeBlank(StringMapWrapper.get(this.params, param)); }
}
/**
* `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();
/**
* `Instruction` is a tree of {@link ComponentInstruction}s with all the information needed
* to transition each component in the app to a given route, including all auxiliary routes.
@ -176,23 +214,30 @@ export abstract class ComponentInstruction {
/**
* Returns the route data of the given route that was specified in the {@link RouteDefinition},
* or `null` if no route data was specified.
* or an empty object if no route data was specified.
*/
abstract routeData(): Object;
get routeData(): RouteData { return unimplemented(); };
}
export class ComponentInstruction_ extends ComponentInstruction {
private _routeData: RouteData;
constructor(urlPath: string, urlParams: string[], private _recognizer: PathRecognizer,
params: {[key: string]: any} = null) {
super();
this.urlPath = urlPath;
this.urlParams = urlParams;
this.params = params;
if (isPresent(this._recognizer.handler.data)) {
this._routeData = new RouteData(this._recognizer.handler.data);
} else {
this._routeData = BLANK_ROUTE_DATA;
}
}
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; }
routeData(): Object { return this._recognizer.handler.data; }
get routeData(): RouteData { return this._routeData; }
}

View File

@ -20,7 +20,7 @@ export class RouteConfig {
* - `component` a component type.
* - `as` is an optional `CamelCase` string representing the name of the route.
* - `data` is an optional property of any type representing arbitrary route metadata for the given
* route. It is injectable via the {@link ROUTE_DATA} token.
* route. It is injectable via {@link RouteData}.
*
* ### Example
* ```
@ -34,7 +34,7 @@ export class RouteConfig {
*/
@CONST()
export class Route implements RouteDefinition {
data: any;
data: {[key: string]: any};
path: string;
component: Type;
as: string;
@ -42,7 +42,7 @@ export class Route implements RouteDefinition {
loader: Function;
redirectTo: string;
constructor({path, component, as, data}:
{path: string, component: Type, as?: string, data?: any}) {
{path: string, component: Type, as?: string, data?: {[key: string]: any}}) {
this.path = path;
this.component = component;
this.as = as;
@ -60,7 +60,7 @@ export class Route implements RouteDefinition {
* - `component` a component type.
* - `as` is an optional `CamelCase` string representing the name of the route.
* - `data` is an optional property of any type representing arbitrary route metadata for the given
* route. It is injectable via the {@link ROUTE_DATA} token.
* route. It is injectable via {@link RouteData}.
*
* ### Example
* ```
@ -74,7 +74,7 @@ export class Route implements RouteDefinition {
*/
@CONST()
export class AuxRoute implements RouteDefinition {
data: any = null;
data: {[key: string]: any} = null;
path: string;
component: Type;
as: string;
@ -97,7 +97,7 @@ export class AuxRoute implements RouteDefinition {
* - `loader` is a function that returns a promise that resolves to a component.
* - `as` is an optional `CamelCase` string representing the name of the route.
* - `data` is an optional property of any type representing arbitrary route metadata for the given
* route. It is injectable via the {@link ROUTE_DATA} token.
* route. It is injectable via {@link RouteData}.
*
* ### Example
* ```
@ -111,11 +111,12 @@ export class AuxRoute implements RouteDefinition {
*/
@CONST()
export class AsyncRoute implements RouteDefinition {
data: any;
data: {[key: string]: any};
path: string;
loader: Function;
as: string;
constructor({path, loader, as, data}: {path: string, loader: Function, as?: string, data?: any}) {
constructor({path, loader, as, data}:
{path: string, loader: Function, as?: string, data?: {[key: string]: any}}) {
this.path = path;
this.loader = loader;
this.as = as;

View File

@ -1,4 +0,0 @@
import {OpaqueToken} from 'angular2/angular2';
import {CONST_EXPR} from 'angular2/src/core/facade/lang';
export const ROUTE_DATA: OpaqueToken = CONST_EXPR(new OpaqueToken('routeData'));

View File

@ -4,5 +4,5 @@ import {Type} from 'angular2/src/core/facade/lang';
export interface RouteHandler {
componentType: Type;
resolveComponentType(): Promise<any>;
data?: Object;
data?: {[key: string]: any};
}

View File

@ -15,8 +15,7 @@ import {
} from 'angular2/angular2';
import * as routerMod from './router';
import {ComponentInstruction, RouteParams} from './instruction';
import {ROUTE_DATA} from './route_data';
import {ComponentInstruction, RouteParams, RouteData} from './instruction';
import * as hookMod from './lifecycle_annotations';
import {hasLifecycleHook} from './route_lifecycle_reflector';
@ -58,7 +57,7 @@ export class RouterOutlet {
var childRouter = this._parentRouter.childRouter(componentType);
var providers = Injector.resolve([
provide(ROUTE_DATA, {useValue: nextInstruction.routeData()}),
provide(RouteData, {useValue: nextInstruction.routeData}),
provide(RouteParams, {useValue: new RouteParams(nextInstruction.params)}),
provide(routerMod.Router, {useValue: childRouter})
]);

View File

@ -6,7 +6,7 @@ export class SyncRouteHandler implements RouteHandler {
/** @internal */
_resolvedComponent: Promise<any> = null;
constructor(public componentType: Type, public data?: Object) {
constructor(public componentType: Type, public data?: {[key: string]: any}) {
this._resolvedComponent = PromiseWrapper.resolve(componentType);
}