docs(router): add api docs

This commit is contained in:
vsavkin 2016-05-03 11:35:07 -07:00
parent 9f784dcc5a
commit b98c9e74e1
13 changed files with 272 additions and 14 deletions

View File

@ -1,7 +1,7 @@
/**
* @module
* @description
* Alternative implementation of the router. Experimental.
* Maps application URLs into application states, to support deep-linking and navigation.
*/
export {Router, RouterOutletMap} from './src/router';
@ -15,4 +15,24 @@ export {ROUTER_PROVIDERS} from './src/router_providers';
import {RouterOutlet} from './src/directives/router_outlet';
import {RouterLink} from './src/directives/router_link';
/**
* A list of directives. To use the router directives like {@link RouterOutlet} and
* {@link RouterLink}, add this to your `directives` array in the {@link View} decorator of your
* component.
*
* ```
* import {Component} from '@angular/core';
* import {ROUTER_DIRECTIVES, Routes} from '@angular/router-deprecated';
*
* @Component({directives: [ROUTER_DIRECTIVES]})
* @RouteConfig([
* {...},
* ])
* class AppCmp {
* // ...
* }
*
* bootstrap(AppCmp);
* ```
*/
export const ROUTER_DIRECTIVES: any[] = /*@ts2dart_const*/[RouterOutlet, RouterLink];

View File

@ -1 +1,5 @@
/**
* Name of the default outlet outlet.
* @type {string}
*/
export const DEFAULT_OUTLET_NAME = "__DEFAULT";

View File

@ -19,16 +19,49 @@ import {RouteSegment, UrlSegment, Tree} from '../segments';
import {isString, isArray, isPresent} from '../facade/lang';
import {ObservableWrapper} from '../facade/async';
/**
* The RouterLink directive lets you link to specific parts of your app.
*
* Consider the following route configuration:
* ```
* @Routes([
* { path: '/user', component: UserCmp }
* ]);
* class MyComp {}
* ```
*
* When linking to this `User` route, you can write:
*
* ```
* <a [routerLink]="['/user']">link to user component</a>
* ```
*
* RouterLink expects the value to be an array of path segments, followed by the params
* for that level of routing. For instance `['/team', {teamId: 1}, 'user', {userId: 2}]`
* means that we want to generate a link to `/team;teamId=1/user;userId=2`.
*
* The first segment name can be prepended with `/`, `./`, or `../`.
* If the segment begins with `/`, the router will look up the route from the root of the app.
* If the segment begins with `./`, or doesn't begin with a slash, the router will
* instead look in the current component's children for the route.
* And if the segment begins with `../`, the router will go up one segment in the url.
*
* See {@link Router.createUrlTree} for more information.
*/
@Directive({selector: '[routerLink]'})
export class RouterLink implements OnDestroy {
@Input() target: string;
private _commands: any[] = [];
private _subscription: any;
// the url displayed on the anchor element.
@HostBinding() href: string;
@HostBinding('class.router-link-active') isActive: boolean = false;
constructor(@Optional() private _routeSegment: RouteSegment, private _router: Router) {
// because auxiliary links take existing primary and auxiliary routes into account,
// we need to update the link whenever params or other routes change.
this._subscription =
ObservableWrapper.subscribe(_router.changes, (_) => { this._updateTargetUrlAndHref(); });
}
@ -48,6 +81,7 @@ export class RouterLink implements OnDestroy {
@HostListener("click")
onClick(): boolean {
// If no target, or if target is _self, prevent default browser behavior
if (!isString(this.target) || this.target == '_self') {
this._router.navigate(this._commands, this._routeSegment);
return false;

View File

@ -13,6 +13,21 @@ import {RouterOutletMap} from '../router';
import {DEFAULT_OUTLET_NAME} from '../constants';
import {isPresent, isBlank} from '../facade/lang';
/**
* A router outlet is a placeholder that Angular dynamically fills based on the application's route.
*
* ## Use
*
* ```
* <router-outlet></router-outlet>
* ```
*
* Outlets can be named.
*
* ```
* <router-outlet name="right"></router-outlet>
* ```
*/
@Directive({selector: 'router-outlet'})
export class RouterOutlet {
private _loaded: ComponentRef<any>;
@ -28,10 +43,19 @@ export class RouterOutlet {
this._loaded = null;
}
/**
* Returns the loaded component.
*/
get loadedComponent(): Object { return isPresent(this._loaded) ? this._loaded.instance : null; }
/**
* Returns true is the outlet is not empty.
*/
get isLoaded(): boolean { return isPresent(this._loaded); }
/**
* Called by the Router to instantiate a new component.
*/
load(factory: ComponentFactory<any>, providers: ResolvedReflectiveProvider[],
outletMap: RouterOutletMap): ComponentRef<any> {
this.outletMap = outletMap;

View File

@ -1,10 +1,26 @@
import {RouteSegment, Tree, RouteTree} from './segments';
/**
* Defines route lifecycle method `routerOnActivate`, which is called by the router at the end of a
* successful route navigation.
*
* The `routerOnActivate` hook is called with the current and previous {@link RouteSegment}s of the
* component and with the corresponding route trees.
*/
export interface OnActivate {
routerOnActivate(curr: RouteSegment, prev?: RouteSegment, currTree?: RouteTree,
prevTree?: RouteTree): void;
}
/**
* Defines route lifecycle method `routerOnDeactivate`, which is called by the router before
* destroying a component as part of a route change.
*
* The `routerOnDeactivate` hook is called with two {@link RouteTree}s, representing the current
* and the future state of the application.
*
* `routerOnDeactivate` must return a promise. The route change will wait until the promise settles.
*/
export interface CanDeactivate {
routerCanDeactivate(currTree?: RouteTree, futureTree?: RouteTree): Promise<boolean>;
}

View File

@ -3,6 +3,7 @@ import {isBlank, isPresent, isString, isStringMap} from './facade/lang';
import {BaseException} from './facade/exceptions';
import {ListWrapper} from './facade/collection';
// TODO: vsavkin: should reuse segments
export function link(segment: RouteSegment, routeTree: RouteTree, urlTree: UrlTree, commands: any[]): UrlTree {
if (commands.length === 0) return urlTree;

View File

@ -3,6 +3,11 @@ library angular.alt_router.decorators;
import 'metadata.dart';
export 'metadata.dart';
/**
* Defines routes for a given component.
*
* It takes an array of {@link RouteMetadata}s.
*/
class Routes extends RoutesMetadata {
const Routes(List<RouteMetadata> routes): super(routes);
}

View File

@ -1,7 +1,19 @@
import {RoutesMetadata, RouteMetadata} from "./metadata";
import {makeDecorator} from '../core_private';
/**
* Defines routes for a given component.
*
* It takes an array of {@link RouteMetadata}s.
*/
export interface RoutesFactory {
(routes: RouteMetadata[]): any;
new (routes: RouteMetadata[]): RoutesMetadata;
}
/**
* Defines routes for a given component.
*
* It takes an array of {@link RouteMetadata}s.
*/
export var Routes: RoutesFactory = <RoutesFactory>makeDecorator(RoutesMetadata);

View File

@ -1,12 +1,34 @@
import {Type} from '@angular/core';
import {stringify} from "../facade/lang";
/**
* Information about a route.
*
* It has the following properties:
* - `path` is a string that uses the route matcher DSL.
* - `component` a component type.
*
* ### Example
* ```
* import {Routes} from '@angular/router';
*
* @Routes([
* {path: '/home', component: HomeCmp}
* ])
* class MyApp {}
* ```
*
* @ts2dart_const
*/
export abstract class RouteMetadata {
abstract get path(): string;
abstract get component(): Type;
}
/* @ts2dart_const */
/**
* See {@link RouteMetadata} for more information.
* @ts2dart_const
*/
export class Route implements RouteMetadata {
path: string;
component: Type;
@ -17,7 +39,12 @@ export class Route implements RouteMetadata {
toString(): string { return `@Route(${this.path}, ${stringify(this.component)})`; }
}
/* @ts2dart_const */
/**
* Defines routes for a given component.
*
* It takes an array of {@link RouteMetadata}s.
* @ts2dart_const
*/
export class RoutesMetadata {
constructor(public routes: RouteMetadata[]) {}
toString(): string { return `@Routes(${this.routes})`; }

View File

@ -25,18 +25,30 @@ import {
import {hasLifecycleHook} from './lifecycle_reflector';
import {DEFAULT_OUTLET_NAME} from './constants';
/**
* @internal
*/
export class RouterOutletMap {
/** @internal */
_outlets: {[name: string]: RouterOutlet} = {};
registerOutlet(name: string, outlet: RouterOutlet): void { this._outlets[name] = outlet; }
}
/**
* The `Router` is responsible for mapping URLs to components.
*
* You can see the state of the router by inspecting the read-only fields `router.urlTree`
* and `router.routeTree`.
*/
export class Router {
private _prevTree: RouteTree;
private _urlTree: UrlTree;
private _locationSubscription: any;
private _changes: EventEmitter<void> = new EventEmitter<void>();
/**
* @internal
*/
constructor(private _rootComponent: Object, private _rootComponentType: Type,
private _componentResolver: ComponentResolver,
private _urlSerializer: RouterUrlSerializer,
@ -46,18 +58,94 @@ export class Router {
this.navigateByUrl(this._location.path());
}
/**
* Returns the current url tree.
*/
get urlTree(): UrlTree { return this._urlTree; }
/**
* Returns the current route tree.
*/
get routeTree(): RouteTree { return this._prevTree; }
/**
* An observable or url changes from the router.
*/
get changes(): Observable<void> { return this._changes; }
/**
* Navigate based on the provided url. This navigation is always absolute.
*
* ### Usage
*
* ```
* router.navigateByUrl("/team/33/user/11");
* ```
*/
navigateByUrl(url: string): Promise<void> {
return this._navigate(this._urlSerializer.parse(url));
}
/**
* Navigate based on the provided array of commands and a starting point.
* If no segment is provided, the navigation is absolute.
*
* ### Usage
*
* ```
* router.navigate(['team', 33, 'team', '11], segment);
* ```
*/
navigate(commands: any[], segment?: RouteSegment): Promise<void> {
return this._navigate(this.createUrlTree(commands, segment));
}
/**
* @internal
*/
dispose(): void { ObservableWrapper.dispose(this._locationSubscription); }
/**
* Applies an array of commands to the current url tree and creates
* a new url tree.
*
* When given a segment, applies the given commands starting from the segment.
* When not given a segment, applies the given command starting from the root.
*
* ### Usage
*
* ```
* // create /team/33/user/11
* router.createUrlTree(['/team', 33, 'user', 11]);
*
* // create /team/33;expand=true/user/11
* router.createUrlTree(['/team', 33, {expand: true}, 'user', 11]);
*
* // you can collapse static fragments like this
* router.createUrlTree(['/team/33/user', userId]);
*
* // assuming the current url is `/team/33/user/11` and the segment points to `user/11`
*
* // navigate to /team/33/user/11/details
* router.createUrlTree(['details'], segment);
*
* // navigate to /team/33/user/22
* router.createUrlTree(['../22'], segment);
*
* // navigate to /team/44/user/22
* router.createUrlTree(['../../team/44/user/22'], segment);
* ```
*/
createUrlTree(commands: any[], segment?: RouteSegment): UrlTree {
let s = isPresent(segment) ? segment : this._prevTree.root;
return link(s, this._prevTree, this.urlTree, commands);
}
/**
* Serializes a {@link UrlTree} into a string.
*/
serializeUrl(url: UrlTree): string { return this._urlSerializer.serialize(url); }
private _createInitialTree(): RouteTree {
let root = new RouteSegment([new UrlSegment("", {}, null)], {}, DEFAULT_OUTLET_NAME,
this._rootComponentType, null);
@ -84,17 +172,6 @@ export class Router {
});
});
}
createUrlTree(commands: any[], segment?: RouteSegment): UrlTree {
let s = isPresent(segment) ? segment : this._prevTree.root;
return link(s, this._prevTree, this.urlTree, commands);
}
serializeUrl(url: UrlTree): string { return this._urlSerializer.serialize(url); }
get changes(): Observable<void> { return this._changes; }
get routeTree(): RouteTree { return this._prevTree; }
}

View File

@ -2,6 +2,28 @@ import {ROUTER_PROVIDERS_COMMON} from './router_providers_common';
import {BrowserPlatformLocation} from '@angular/platform-browser';
import {PlatformLocation} from '@angular/common';
/**
* A list of {@link Provider}s. To use the router, you must add this to your application.
*
* ```
* import {Component} from '@angular/core';
* import {
* ROUTER_DIRECTIVES,
* ROUTER_PROVIDERS,
* Routes
* } from '@angular/router';
*
* @Component({directives: [ROUTER_DIRECTIVES]})
* @Routes([
* {...},
* ])
* class AppCmp {
* // ...
* }
*
* bootstrap(AppCmp, [ROUTER_PROVIDERS]);
* ```
*/
export const ROUTER_PROVIDERS: any[] = /*@ts2dart_const*/[
ROUTER_PROVIDERS_COMMON,
/*@ts2dart_Provider*/ {provide: PlatformLocation, useClass: BrowserPlatformLocation},

View File

@ -5,6 +5,9 @@ import {RouterUrlSerializer, DefaultRouterUrlSerializer} from './router_url_seri
import {ApplicationRef} from '@angular/core';
import {BaseException} from '@angular/core';
/**
* The Platform agnostic ROUTER PROVIDERS
*/
export const ROUTER_PROVIDERS_COMMON: any[] = /*@ts2dart_const*/[
RouterOutletMap,
/*@ts2dart_Provider*/ {provide: RouterUrlSerializer, useClass: DefaultRouterUrlSerializer},

View File

@ -2,11 +2,24 @@ import {UrlSegment, Tree, TreeNode, rootNode, UrlTree} from './segments';
import {BaseException} from '@angular/core';
import {isBlank, isPresent, RegExpWrapper} from './facade/lang';
/**
* Defines a way to serialize/deserialize a url tree.
*/
export abstract class RouterUrlSerializer {
/**
* Parse a url into a {@Link UrlTree}
*/
abstract parse(url: string): UrlTree;
/**
* Converts a {@Link UrlTree} into a url
*/
abstract serialize(tree: UrlTree): string;
}
/**
* A default implementation of the serialization.
*/
export class DefaultRouterUrlSerializer extends RouterUrlSerializer {
parse(url: string): UrlTree {
let root = new _UrlParser().parse(url);