refactor(router): misc refactoring (#13330)

This commit is contained in:
Victor Berchet 2016-12-09 10:44:46 -08:00 committed by GitHub
parent 56c361ff6a
commit b5c4bf1c59
13 changed files with 217 additions and 411 deletions

View File

@ -9,8 +9,6 @@
import {Injector, THROW_IF_NOT_FOUND} from '../di/injector';
import {AppView} from './view';
const _UNDEFINED = new Object();
export class ElementInjector extends Injector {
constructor(private _view: AppView<any>, private _nodeIndex: number) { super(); }

View File

@ -9,7 +9,6 @@
import {ApplicationRef} from '../application_ref';
import {ChangeDetectorRef, ChangeDetectorStatus} from '../change_detection/change_detection';
import {Injector, THROW_IF_NOT_FOUND} from '../di/injector';
import {ListWrapper} from '../facade/collection';
import {isPresent} from '../facade/lang';
import {WtfScopeFn, wtfCreateScope, wtfLeave} from '../profile/profile';
import {DirectRenderer, RenderComponentType, RenderDebugInfo, Renderer} from '../render/api';

View File

@ -18,7 +18,7 @@ import {map} from 'rxjs/operator/map';
import {mergeMap} from 'rxjs/operator/mergeMap';
import {EmptyError} from 'rxjs/util/EmptyError';
import {Route, Routes, UrlMatchResult} from './config';
import {Route, Routes} from './config';
import {LoadedRouterConfig, RouterConfigLoader} from './router_config_loader';
import {NavigationCancelingError, PRIMARY_OUTLET, Params, defaultUrlMatcher} from './shared';
import {UrlSegment, UrlSegmentGroup, UrlSerializer, UrlTree} from './url_tree';
@ -54,7 +54,6 @@ function canLoadFails(route: Route): Observable<LoadedRouterConfig> {
`Cannot load children because the guard of the route "path: '${route.path}'" returned false`)));
}
export function applyRedirects(
injector: Injector, configLoader: RouterConfigLoader, urlSerializer: UrlSerializer,
urlTree: UrlTree, config: Routes): Observable<UrlTree> {

View File

@ -9,7 +9,7 @@
import {ActivatedRoute} from './router_state';
import {PRIMARY_OUTLET, Params} from './shared';
import {UrlSegment, UrlSegmentGroup, UrlTree} from './url_tree';
import {forEach, shallowEqual} from './utils/collection';
import {forEach, shallowEqual, last} from './utils/collection';
export function createUrlTree(
route: ActivatedRoute, urlTree: UrlTree, commands: any[], queryParams: Params,
@ -18,36 +18,24 @@ export function createUrlTree(
return tree(urlTree.root, urlTree.root, urlTree, queryParams, fragment);
}
const normalizedCommands = normalizeCommands(commands);
validateCommands(normalizedCommands);
const nav = computeNavigation(commands);
if (navigateToRoot(normalizedCommands)) {
if (nav.toRoot()) {
return tree(urlTree.root, new UrlSegmentGroup([], {}), urlTree, queryParams, fragment);
}
const startingPosition = findStartingPosition(normalizedCommands, urlTree, route);
const startingPosition = findStartingPosition(nav, urlTree, route);
const segmentGroup = startingPosition.processChildren ?
updateSegmentGroupChildren(
startingPosition.segmentGroup, startingPosition.index, normalizedCommands.commands) :
startingPosition.segmentGroup, startingPosition.index, nav.commands) :
updateSegmentGroup(
startingPosition.segmentGroup, startingPosition.index, normalizedCommands.commands);
startingPosition.segmentGroup, startingPosition.index, nav.commands);
return tree(startingPosition.segmentGroup, segmentGroup, urlTree, queryParams, fragment);
}
function validateCommands(n: NormalizedNavigationCommands): void {
if (n.isAbsolute && n.commands.length > 0 && isMatrixParams(n.commands[0])) {
throw new Error('Root segment cannot have matrix parameters');
}
const c = n.commands.filter(c => typeof c === 'object' && c.outlets !== undefined);
if (c.length > 0 && c[0] !== n.commands[n.commands.length - 1]) {
throw new Error('{outlets:{}} has to be the last command');
}
}
function isMatrixParams(command: any): boolean {
return typeof command === 'object' && command.outlets === undefined &&
command.segmentPath === undefined;
return typeof command === 'object' && !command.outlets && !command.segmentPath;
}
function tree(
@ -55,11 +43,11 @@ function tree(
queryParams: Params, fragment: string): UrlTree {
if (urlTree.root === oldSegmentGroup) {
return new UrlTree(newSegmentGroup, stringify(queryParams), fragment);
} else {
return new UrlTree(
replaceSegment(urlTree.root, oldSegmentGroup, newSegmentGroup), stringify(queryParams),
fragment);
}
return new UrlTree(
replaceSegment(urlTree.root, oldSegmentGroup, newSegmentGroup), stringify(queryParams),
fragment);
}
function replaceSegment(
@ -76,72 +64,71 @@ function replaceSegment(
return new UrlSegmentGroup(current.segments, children);
}
function navigateToRoot(normalizedChange: NormalizedNavigationCommands): boolean {
return normalizedChange.isAbsolute && normalizedChange.commands.length === 1 &&
normalizedChange.commands[0] == '/';
class Navigation {
constructor(public isAbsolute: boolean, public numberOfDoubleDots: number, public commands: any[]) {
if (isAbsolute && commands.length > 0 && isMatrixParams(commands[0])) {
throw new Error('Root segment cannot have matrix parameters');
}
const cmdWithOutlet = commands.find(c => typeof c === 'object' && c.outlets);
if (cmdWithOutlet && cmdWithOutlet !== last(commands)) {
throw new Error('{outlets:{}} has to be the last command');
}
}
public toRoot(): boolean {
return this.isAbsolute && this.commands.length === 1 && this.commands[0] == '/';
}
}
class NormalizedNavigationCommands {
constructor(
public isAbsolute: boolean, public numberOfDoubleDots: number, public commands: any[]) {}
}
function normalizeCommands(commands: any[]): NormalizedNavigationCommands {
if ((typeof commands[0] === 'string') && commands.length === 1 && commands[0] == '/') {
return new NormalizedNavigationCommands(true, 0, commands);
/** Transforms commands to a normalized `Navigation` */
function computeNavigation(commands: any[]): Navigation {
if ((typeof commands[0] === 'string') && commands.length === 1 && commands[0] === '/') {
return new Navigation(true, 0, commands);
}
let numberOfDoubleDots = 0;
let isAbsolute = false;
const res: any[] = [];
for (let i = 0; i < commands.length; ++i) {
const c = commands[i];
const res: any[] = commands.reduce((res, cmd, cmdIdx) => {
if (typeof cmd === 'object') {
if (cmd.outlets) {
const outlets: {[k: string]: any} = {};
forEach(cmd.outlets, (commands: any, name: string) => {
outlets[name] = typeof commands === 'string' ? commands.split('/') : commands;
});
return [...res, {outlets}];
}
if (typeof c === 'object' && c.outlets !== undefined) {
const r: {[k: string]: any} = {};
forEach(c.outlets, (commands: any, name: string) => {
if (typeof commands === 'string') {
r[name] = commands.split('/');
} else {
r[name] = commands;
if (cmd.segmentPath) {
return [...res, cmd.segmentPath];
}
}
if (!(typeof cmd === 'string')) {
return [...res, cmd];
}
if (cmdIdx === 0) {
cmd.split('/').forEach((urlPart, partIndex) => {
if (partIndex == 0 && urlPart === '.') {
// skip './a'
} else if (partIndex == 0 && urlPart === '') { // '/a'
isAbsolute = true;
} else if (urlPart === '..') { // '../a'
numberOfDoubleDots++;
} else if (urlPart != '') {
res.push(urlPart);
}
});
res.push({outlets: r});
continue;
return res;
}
if (typeof c === 'object' && c.segmentPath !== undefined) {
res.push(c.segmentPath);
continue;
}
return [...res, cmd];
}, []);
if (!(typeof c === 'string')) {
res.push(c);
continue;
}
if (i === 0) {
const parts = c.split('/');
for (let j = 0; j < parts.length; ++j) {
const cc = parts[j];
if (j == 0 && cc == '.') { // './a'
// skip it
} else if (j == 0 && cc == '') { // '/a'
isAbsolute = true;
} else if (cc == '..') { // '../a'
numberOfDoubleDots++;
} else if (cc != '') {
res.push(cc);
}
}
} else {
res.push(c);
}
}
return new NormalizedNavigationCommands(isAbsolute, numberOfDoubleDots, res);
return new Navigation(isAbsolute, numberOfDoubleDots, res);
}
class Position {
@ -150,19 +137,19 @@ class Position {
}
}
function findStartingPosition(
normalizedChange: NormalizedNavigationCommands, urlTree: UrlTree,
route: ActivatedRoute): Position {
if (normalizedChange.isAbsolute) {
return new Position(urlTree.root, true, 0);
} else if (route.snapshot._lastPathIndex === -1) {
return new Position(route.snapshot._urlSegment, true, 0);
} else {
const modifier = isMatrixParams(normalizedChange.commands[0]) ? 0 : 1;
const index = route.snapshot._lastPathIndex + modifier;
return createPositionApplyingDoubleDots(
route.snapshot._urlSegment, index, normalizedChange.numberOfDoubleDots);
function findStartingPosition(nav: Navigation, tree: UrlTree, route: ActivatedRoute): Position {
if (nav.isAbsolute) {
return new Position(tree.root, true, 0);
}
if (route.snapshot._lastPathIndex === -1) {
return new Position(route.snapshot._urlSegment, true, 0);
}
const modifier = isMatrixParams(nav.commands[0]) ? 0 : 1;
const index = route.snapshot._lastPathIndex + modifier;
return createPositionApplyingDoubleDots(route.snapshot._urlSegment, index, nav.numberOfDoubleDots);
}
function createPositionApplyingDoubleDots(
@ -276,7 +263,7 @@ function createNewSegmentGroup(
let i = 0;
while (i < commands.length) {
if (typeof commands[i] === 'object' && commands[i].outlets !== undefined) {
const children = createNewSegmentChldren(commands[i].outlets);
const children = createNewSegmentChildren(commands[i].outlets);
return new UrlSegmentGroup(paths, children);
}
@ -301,7 +288,7 @@ function createNewSegmentGroup(
return new UrlSegmentGroup(paths, {});
}
function createNewSegmentChldren(outlets: {[name: string]: any}): any {
function createNewSegmentChildren(outlets: {[name: string]: any}): any {
const children: {[key: string]: UrlSegmentGroup} = {};
forEach(outlets, (commands: any, outlet: string) => {
if (commands !== null) {

View File

@ -12,9 +12,12 @@ import {ROUTES} from './router_config_loader';
import {ROUTER_PROVIDERS} from './router_module';
import {flatten} from './utils/collection';
export var __router_private__:
{ROUTER_PROVIDERS: typeof ROUTER_PROVIDERS; ROUTES: typeof ROUTES; flatten: typeof flatten;} = {
ROUTER_PROVIDERS: ROUTER_PROVIDERS,
ROUTES: ROUTES,
flatten: flatten
};
export const __router_private__: {
ROUTER_PROVIDERS: typeof ROUTER_PROVIDERS,
ROUTES: typeof ROUTES,
flatten: typeof flatten,
} = {
ROUTER_PROVIDERS: ROUTER_PROVIDERS,
ROUTES: ROUTES,
flatten: flatten,
};

View File

@ -7,4 +7,4 @@
*/
import {__platform_browser_private__ as r} from '@angular/platform-browser';
export var getDOM: typeof r.getDOM = r.getDOM;
export const getDOM: typeof r.getDOM = r.getDOM;

View File

@ -11,9 +11,9 @@ import {Observable} from 'rxjs/Observable';
import {Observer} from 'rxjs/Observer';
import {of } from 'rxjs/observable/of';
import {Data, ResolveData, Route, Routes, UrlMatchResult} from './config';
import {Data, ResolveData, Route, Routes} from './config';
import {ActivatedRouteSnapshot, RouterStateSnapshot, inheritedParamsDataResolve} from './router_state';
import {PRIMARY_OUTLET, Params, defaultUrlMatcher} from './shared';
import {PRIMARY_OUTLET, defaultUrlMatcher} from './shared';
import {UrlSegment, UrlSegmentGroup, UrlTree, mapChildrenIntoArray} from './url_tree';
import {forEach, last, merge} from './utils/collection';
import {TreeNode} from './utils/tree';

View File

@ -11,7 +11,6 @@ import {ComponentRef} from '@angular/core';
import {ActivatedRoute, ActivatedRouteSnapshot} from './router_state';
import {TreeNode} from './utils/tree';
/**
* @whatItDoes Represents the detached route tree.
*
@ -22,43 +21,30 @@ import {TreeNode} from './utils/tree';
*/
export type DetachedRouteHandle = {};
/**
* @internal
*/
/** @internal */
export type DetachedRouteHandleInternal = {
componentRef: ComponentRef<any>,
route: TreeNode<ActivatedRoute>
route: TreeNode<ActivatedRoute>,
};
/**
* @whatItDoes Provides a way to customize when activated routes get reused.
*
* @experimental
*/
export abstract class RouteReuseStrategy {
/**
* Determines if this route (and its subtree) should be detached to be reused later.
*/
/** Determines if this route (and its subtree) should be detached to be reused later */
abstract shouldDetach(route: ActivatedRouteSnapshot): boolean;
/**
* Stores the detached route.
*/
/** Stores the detached route */
abstract store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle): void;
/**
* Determines if this route (and its subtree) should be reattached.
*/
/** Determines if this route (and its subtree) should be reattached */
abstract shouldAttach(route: ActivatedRouteSnapshot): boolean;
/**
* Retrieves the previously stored route.
*/
/** Retrieves the previously stored route */
abstract retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle;
/**
* Determines if a route should be reused.
*/
/** Determines if a route should be reused */
abstract shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean;
}

View File

@ -13,7 +13,6 @@ import {Observable} from 'rxjs/Observable';
import {Subject} from 'rxjs/Subject';
import {Subscription} from 'rxjs/Subscription';
import {from} from 'rxjs/observable/from';
import {fromPromise} from 'rxjs/observable/fromPromise';
import {of } from 'rxjs/observable/of';
import {concatMap} from 'rxjs/operator/concatMap';
import {every} from 'rxjs/operator/every';
@ -148,7 +147,6 @@ export class NavigationStart {
constructor(
/** @docsNotRequired */
public id: number,
/** @docsNotRequired */
public url: string) {}
@ -166,10 +164,8 @@ export class NavigationEnd {
constructor(
/** @docsNotRequired */
public id: number,
/** @docsNotRequired */
public url: string,
/** @docsNotRequired */
public urlAfterRedirects: string) {}
@ -189,10 +185,8 @@ export class NavigationCancel {
constructor(
/** @docsNotRequired */
public id: number,
/** @docsNotRequired */
public url: string,
/** @docsNotRequired */
public reason: string) {}
@ -210,10 +204,8 @@ export class NavigationError {
constructor(
/** @docsNotRequired */
public id: number,
/** @docsNotRequired */
public url: string,
/** @docsNotRequired */
public error: any) {}
@ -233,7 +225,6 @@ export class RoutesRecognized {
constructor(
/** @docsNotRequired */
public id: number,
/** @docsNotRequired */
public url: string,
/** @docsNotRequired */
@ -282,7 +273,7 @@ type NavigationParams = {
resolve: any,
reject: any,
promise: Promise<boolean>,
imperative: boolean
imperative: boolean,
};
@ -312,9 +303,8 @@ export class Router {
private currentUrlTree: UrlTree;
private rawUrlTree: UrlTree;
private navigations: BehaviorSubject<NavigationParams> =
new BehaviorSubject<NavigationParams>(null);
private routerEvents: Subject<Event> = new Subject<Event>();
private navigations = new BehaviorSubject<NavigationParams>(null);
private routerEvents = new Subject<Event>();
private currentRouterState: RouterState;
private locationSubscription: Subscription;
@ -353,7 +343,6 @@ export class Router {
this.rawUrlTree = this.currentUrlTree;
this.configLoader = new RouterConfigLoader(loader, compiler);
this.currentRouterState = createEmptyState(this.currentUrlTree, this.rootComponentType);
this.processNavigations();
}
@ -403,19 +392,13 @@ export class Router {
}
}
/**
* Returns the current route state.
*/
/** The current route state */
get routerState(): RouterState { return this.currentRouterState; }
/**
* Returns the current url.
*/
/** The current url */
get url(): string { return this.serializeUrl(this.currentUrlTree); }
/**
* Returns an observable of route events
*/
/** An observable of router events */
get events(): Observable<Event> { return this.routerEvents; }
/**
@ -428,7 +411,7 @@ export class Router {
* { path: 'team/:id', component: TeamCmp, children: [
* { path: 'simple', component: SimpleCmp },
* { path: 'user/:name', component: UserCmp }
* ] }
* ]}
* ]);
* ```
*/
@ -437,14 +420,10 @@ export class Router {
this.config = config;
}
/**
* @docsNotRequired
*/
/** @docsNotRequired */
ngOnDestroy() { this.dispose(); }
/**
* Disposes of the router.
*/
/** Disposes of the router */
dispose(): void {
if (this.locationSubscription) {
this.locationSubscription.unsubscribe();
@ -496,7 +475,7 @@ export class Router {
createUrlTree(
commands: any[], {relativeTo, queryParams, fragment, preserveQueryParams,
preserveFragment}: NavigationExtras = {}): UrlTree {
const a = relativeTo ? relativeTo : this.routerState.root;
const a = relativeTo || this.routerState.root;
const q = preserveQueryParams ? this.currentUrlTree.queryParams : queryParams;
const f = preserveFragment ? this.currentUrlTree.fragment : fragment;
return createUrlTree(a, this.currentUrlTree, commands, q, f);
@ -506,9 +485,9 @@ export class Router {
* Navigate based on the provided url. This navigation is always absolute.
*
* Returns a promise that:
* - is resolved with 'true' when navigation succeeds
* - is resolved with 'false' when navigation fails
* - is rejected when an error happens
* - resolves to 'true' when navigation succeeds,
* - resolves to 'false' when navigation fails,
* - is rejected when an error happens.
*
* ### Usage
*
@ -527,11 +506,11 @@ export class Router {
if (url instanceof UrlTree) {
return this.scheduleNavigation(
this.urlHandlingStrategy.merge(url, this.rawUrlTree), true, extras);
} else {
const urlTree = this.urlSerializer.parse(url);
return this.scheduleNavigation(
this.urlHandlingStrategy.merge(urlTree, this.rawUrlTree), true, extras);
}
const urlTree = this.urlSerializer.parse(url);
return this.scheduleNavigation(
this.urlHandlingStrategy.merge(urlTree, this.rawUrlTree), true, extras);
}
/**
@ -539,9 +518,9 @@ export class Router {
* If no starting route is provided, the navigation is absolute.
*
* Returns a promise that:
* - is resolved with 'true' when navigation succeeds
* - is resolved with 'false' when navigation fails
* - is rejected when an error happens
* - resolves to 'true' when navigation succeeds,
* - resolves to 'false' when navigation fails,
* - is rejected when an error happens.
*
* ### Usage
*
@ -549,11 +528,11 @@ export class Router {
* router.navigate(['team', 33, 'user', 11], {relativeTo: route});
*
* // Navigate without updating the URL
* router.navigate(['team', 33, 'user', 11], {relativeTo: route, skipLocationChange: true });
* router.navigate(['team', 33, 'user', 11], {relativeTo: route, skipLocationChange: true});
* ```
*
* In opposite to `navigateByUrl`, `navigate` always takes a delta
* that is applied to the current URL.
* In opposite to `navigateByUrl`, `navigate` always takes a delta that is applied to the current
* URL.
*/
navigate(commands: any[], extras: NavigationExtras = {skipLocationChange: false}):
Promise<boolean> {
@ -563,19 +542,13 @@ export class Router {
return this.navigateByUrl(this.createUrlTree(commands, extras), extras);
}
/**
* Serializes a {@link UrlTree} into a string.
*/
/** Serializes a {@link UrlTree} into a string */
serializeUrl(url: UrlTree): string { return this.urlSerializer.serialize(url); }
/**
* Parses a string into a {@link UrlTree}.
*/
/** Parses a string into a {@link UrlTree} */
parseUrl(url: string): UrlTree { return this.urlSerializer.parse(url); }
/**
* Returns if the url is activated or not.
*/
/** Returns whether the url is activated */
isActive(url: string|UrlTree, exact: boolean): boolean {
if (url instanceof UrlTree) {
return containsTree(this.currentUrlTree, url, exact);

View File

@ -16,7 +16,6 @@ import {UrlSegment, UrlSegmentGroup, UrlTree, equalSegments} from './url_tree';
import {merge, shallowEqual, shallowEqualArrays} from './utils/collection';
import {Tree, TreeNode} from './utils/tree';
/**
* @whatItDoes Represents the state of the router.
*
@ -45,14 +44,10 @@ import {Tree, TreeNode} from './utils/tree';
* @stable
*/
export class RouterState extends Tree<ActivatedRoute> {
/**
* @internal
*/
/** @internal */
constructor(
root: TreeNode<ActivatedRoute>,
/**
* The current snapshot of the router state.
*/
/** The current snapshot of the router state */
public snapshot: RouterStateSnapshot) {
super(root);
setRouterStateSnapshot<RouterState, ActivatedRoute>(this, root);
@ -90,17 +85,18 @@ export function createEmptyStateSnapshot(
/**
* @whatItDoes Contains the information about a route associated with a component loaded in an
* outlet.
* ActivatedRoute can also be used to traverse the router state tree.
* An `ActivatedRoute` can also be used to traverse the router state tree.
*
* @howToUse
*
* ```
* @Component({templateUrl:'./my-component.html'})
* @Component({...})
* class MyComponent {
* constructor(route: ActivatedRoute) {
* const id: Observable<string> = route.params.map(p => p.id);
* const url: Observable<string> = route.url.map(s => s.join(''));
* const user = route.data.map(d => d.user); //includes `data` and `resolve`
* const url: Observable<string> = route.url.map(segments => segments.join(''));
* // route.data includes both `data` and `resolve`
* const user = route.data.map(d => d.user);
* }
* }
* ```
@ -108,112 +104,64 @@ export function createEmptyStateSnapshot(
* @stable
*/
export class ActivatedRoute {
/** The current snapshot of this route */
snapshot: ActivatedRouteSnapshot;
/** @internal */
_futureSnapshot: ActivatedRouteSnapshot;
/**
* The current snapshot of this route.
*/
snapshot: ActivatedRouteSnapshot;
/** @internal */
_routerState: RouterState;
/**
* @internal
*/
/** @internal */
constructor(
/**
* The URL segments matched by this route. The observable will emit a new value when
* the array of segments changes.
*/
/** An observable of the URL segments matched by this route */
public url: Observable<UrlSegment[]>,
/**
* The matrix parameters scoped to this route. The observable will emit a new value when
* the set of the parameters changes.
*/
/** An observable of the matrix parameters scoped to this route */
public params: Observable<Params>,
/**
* The query parameters shared by all the routes. The observable will emit a new value when
* the set of the parameters changes.
*/
/** An observable of the query parameters shared by all the routes */
public queryParams: Observable<Params>,
/**
* The URL fragment shared by all the routes. The observable will emit a new value when
* the URL fragment changes.
*/
/** An observable of the URL fragment shared by all the routes */
public fragment: Observable<string>,
/**
* The static and resolved data of this route. The observable will emit a new value when
* any of the resolvers returns a new object.
*/
/** An observable of the static and resolved data of this route. */
public data: Observable<Data>,
/**
* The outlet name of the route. It's a constant.
*/
/** The outlet name of the route. It's a constant */
public outlet: string,
/**
* The component of the route. It's a constant.
*/
public component: Type<any>|string, // TODO: vsavkin: remove |string
futureSnapshot: ActivatedRouteSnapshot) {
/** The component of the route. It's a constant */
// TODO(vsavkin): remove |string
public component: Type<any>|string, futureSnapshot: ActivatedRouteSnapshot) {
this._futureSnapshot = futureSnapshot;
}
/**
* The configuration used to match this route.
*/
/** The configuration used to match this route */
get routeConfig(): Route { return this._futureSnapshot.routeConfig; }
/**
* The root of the router state.
*/
/** The root of the router state */
get root(): ActivatedRoute { return this._routerState.root; }
/**
* The parent of this route in the router state tree.
*/
/** The parent of this route in the router state tree */
get parent(): ActivatedRoute { return this._routerState.parent(this); }
/**
* The first child of this route in the router state tree.
*/
/** The first child of this route in the router state tree */
get firstChild(): ActivatedRoute { return this._routerState.firstChild(this); }
/**
* The children of this route in the router state tree.
*/
/** The children of this route in the router state tree */
get children(): ActivatedRoute[] { return this._routerState.children(this); }
/**
* The path from the root of the router state tree to this route.
*/
/** The path from the root of the router state tree to this route */
get pathFromRoot(): ActivatedRoute[] { return this._routerState.pathFromRoot(this); }
/**
* @docsNotRequired
*/
toString(): string {
return this.snapshot ? this.snapshot.toString() : `Future(${this._futureSnapshot})`;
}
}
/**
* @internal
*/
/** @internal */
export type Inherited = {
params: Params; data: Data; resolve: Data;
params: Params,
data: Data,
resolve: Data,
}
/**
* @internal
*/
/** @internal */
export function
inheritedParamsDataResolve(route: ActivatedRouteSnapshot):
Inherited {
@ -269,59 +217,32 @@ inheritedParamsDataResolve(route: ActivatedRouteSnapshot):
export class ActivatedRouteSnapshot {
/** @internal **/
_routeConfig: Route;
/** @internal **/
_urlSegment: UrlSegmentGroup;
/** @internal */
_lastPathIndex: number;
/** @internal */
_resolve: ResolveData;
/** @internal */
_resolvedData: Data;
/** @internal */
_routerState: RouterStateSnapshot;
/**
* @internal
*/
/** @internal */
constructor(
/**
* The URL segments matched by this route.
*/
/** The URL segments matched by this route */
public url: UrlSegment[],
/**
* The matrix parameters scoped to this route.
*/
/** The matrix parameters scoped to this route */
public params: Params,
/**
* The query parameters shared by all the routes.
*/
/** The query parameters shared by all the routes */
public queryParams: Params,
/**
* The URL fragment shared by all the routes.
*/
/** The URL fragment shared by all the routes */
public fragment: string,
/**
* The static and resolved data of this route.
*/
/** The static and resolved data of this route */
public data: Data,
/**
* The outlet name of the route.
*/
/** The outlet name of the route */
public outlet: string,
/**
* The component of the route.
*/
/** The component of the route */
public component: Type<any>|string, routeConfig: Route, urlSegment: UrlSegmentGroup,
lastPathIndex: number, resolve: ResolveData) {
this._routeConfig = routeConfig;
@ -330,41 +251,26 @@ export class ActivatedRouteSnapshot {
this._resolve = resolve;
}
/**
* The configuration used to match this route.
*/
/** The configuration used to match this route */
get routeConfig(): Route { return this._routeConfig; }
/**
* The root of the router state.
*/
/** The root of the router state */
get root(): ActivatedRouteSnapshot { return this._routerState.root; }
/**
* The parent of this route in the router state tree.
*/
/** The parent of this route in the router state tree */
get parent(): ActivatedRouteSnapshot { return this._routerState.parent(this); }
/**
* The first child of this route in the router state tree.
*/
/** The first child of this route in the router state tree */
get firstChild(): ActivatedRouteSnapshot { return this._routerState.firstChild(this); }
/**
* The children of this route in the router state tree.
*/
/** The children of this route in the router state tree */
get children(): ActivatedRouteSnapshot[] { return this._routerState.children(this); }
/**
* The path from the root of the router state tree to this route.
*/
/** The path from the root of the router state tree to this route */
get pathFromRoot(): ActivatedRouteSnapshot[] { return this._routerState.pathFromRoot(this); }
/**
* @docsNotRequired
*/
toString(): string {
const url = this.url.map(s => s.toString()).join('/');
const url = this.url.map(segment => segment.toString()).join('/');
const matched = this._routeConfig ? this._routeConfig.path : '';
return `Route(url:'${url}', path:'${matched}')`;
}
@ -396,11 +302,8 @@ export class ActivatedRouteSnapshot {
* @stable
*/
export class RouterStateSnapshot extends Tree<ActivatedRouteSnapshot> {
/**
* @internal
*/
/** @internal */
constructor(
/** The url from which this snapshot was created */
public url: string, root: TreeNode<ActivatedRouteSnapshot>) {
super(root);

View File

@ -17,10 +17,10 @@ export function containsTree(container: UrlTree, containee: UrlTree, exact: bool
if (exact) {
return equalQueryParams(container.queryParams, containee.queryParams) &&
equalSegmentGroups(container.root, containee.root);
} else {
return containsQueryParams(container.queryParams, containee.queryParams) &&
containsSegmentGroup(container.root, containee.root);
}
return containsQueryParams(container.queryParams, containee.queryParams) &&
containsSegmentGroup(container.root, containee.root);
}
function equalQueryParams(
@ -83,7 +83,7 @@ function containsSegmentGroupHelper(
* class MyComponent {
* constructor(router: Router) {
* const tree: UrlTree =
* router.parseUrl('/team/33/(user/victor//support:help)?debug=true#fragment');
* router.parseUrl('/team/33/(user/victor//support:help)?debug=true#fragment');
* const f = tree.fragment; // return 'fragment'
* const q = tree.queryParams; // returns {debug: 'true'}
* const g: UrlSegmentGroup = tree.root.children[PRIMARY_OUTLET];
@ -103,79 +103,49 @@ function containsSegmentGroupHelper(
* @stable
*/
export class UrlTree {
/**
* @internal
*/
/** @internal */
constructor(
/**
* The root segment group of the URL tree.
*/
/** The root segment group of the URL tree */
public root: UrlSegmentGroup,
/**
* The query params of the URL.
*/
/** The query params of the URL */
public queryParams: {[key: string]: string},
/**
* The fragment of the URL.
*/
/** The fragment of the URL */
public fragment: string) {}
/**
* @docsNotRequired
*/
/** @docsNotRequired */
toString(): string { return new DefaultUrlSerializer().serialize(this); }
}
/**
* @whatItDoes Represents the parsed URL segment.
* @whatItDoes Represents the parsed URL segment group.
*
* See {@link UrlTree} for more information.
*
* @stable
*/
export class UrlSegmentGroup {
/**
* @internal
*/
/** @internal */
_sourceSegment: UrlSegmentGroup;
/**
* @internal
*/
/** @internal */
_segmentIndexShift: number;
/**
* The parent node in the url tree.
*/
public parent: UrlSegmentGroup = null;
/** The parent node in the url tree */
parent: UrlSegmentGroup = null;
constructor(
/**
* The URL segments of this group. See {@link UrlSegment} for more information.
*/
/** The URL segments of this group. See {@link UrlSegment} for more information */
public segments: UrlSegment[],
/**
* The list of children of this group.
*/
public children: {[key: string]: UrlSegmentGroup}
) {
/** The list of children of this group */
public children: {[key: string]: UrlSegmentGroup}) {
forEach(children, (v: any, k: any) => v.parent = this);
}
/**
* Return true if the segment has child segments
*/
/** Wether the segment has child segments */
hasChildren(): boolean { return this.numberOfChildren > 0; }
/**
* Returns the number of child sements.
*/
/** Number of child segments */
get numberOfChildren(): number { return Object.keys(this.children).length; }
/**
* @docsNotRequired
*/
/** @docsNotRequired */
toString(): string { return serializePaths(this); }
}
@ -200,26 +170,20 @@ export class UrlSegmentGroup {
*
* @description
*
* A UrlSegment is a part of a URL between the two slashes. It contains a path and
* the matrix parameters associated with the segment.
* A UrlSegment is a part of a URL between the two slashes. It contains a path and the matrix
* parameters associated with the segment.
*
* @stable
*/
export class UrlSegment {
constructor(
/**
* The path part of a URL segment.
*/
/** The path part of a URL segment */
public path: string,
/**
* The matrix parameters associated with a segment.
*/
public parameters: {[key: string]: string}) {}
/** The matrix parameters associated with a segment */
public parameters: {[name: string]: string}) {}
/**
* @docsNotRequired
*/
/** @docsNotRequired */
toString(): string { return serializePath(this); }
}
@ -268,14 +232,10 @@ export function mapChildrenIntoArray<T>(
* @stable
*/
export abstract class UrlSerializer {
/**
* Parse a url into a {@link UrlTree}.
*/
/** Parse a url into a {@link UrlTree} */
abstract parse(url: string): UrlTree;
/**
* Converts a {@link UrlTree} into a url.
*/
/** Converts a {@link UrlTree} into a url */
abstract serialize(tree: UrlTree): string;
}
@ -298,17 +258,13 @@ export abstract class UrlSerializer {
* @stable
*/
export class DefaultUrlSerializer implements UrlSerializer {
/**
* Parse a url into a {@link UrlTree}.
*/
/** Parses a url into a {@link UrlTree} */
parse(url: string): UrlTree {
const p = new UrlParser(url);
return new UrlTree(p.parseRootSegment(), p.parseQueryParams(), p.parseFragment());
}
/**
* Converts a {@link UrlTree} into a url.
*/
/** Converts a {@link UrlTree} into a url */
serialize(tree: UrlTree): string {
const segment = `/${serializeSegment(tree.root, true)}`;
const query = serializeQueryParams(tree.queryParams);
@ -383,6 +339,7 @@ function serializeQueryParams(params: {[key: string]: any}): string {
class Pair<A, B> {
constructor(public first: A, public second: B) {}
}
function pairs<T>(obj: {[key: string]: T}): Pair<string, T>[] {
const res: Pair<string, T>[] = [];
for (const prop in obj) {
@ -436,9 +393,9 @@ class UrlParser {
if (this.remaining === '' || this.remaining.startsWith('?') || this.remaining.startsWith('#')) {
return new UrlSegmentGroup([], {});
} else {
return new UrlSegmentGroup([], this.parseChildren());
}
return new UrlSegmentGroup([], this.parseChildren());
}
parseChildren(): {[key: string]: UrlSegmentGroup} {
@ -508,9 +465,9 @@ class UrlParser {
parseFragment(): string {
if (this.peekStartsWith('#')) {
return decodeURI(this.remaining.substring(1));
} else {
return null;
}
return null;
}
parseMatrixParams(): {[key: string]: any} {

View File

@ -60,7 +60,7 @@ export function last<T>(a: T[]): T {
}
export function and(bools: boolean[]): boolean {
return bools.reduce((a, b) => a && b, true);
return !bools.some(v => !v);
}
export function merge<V>(m1: {[key: string]: V}, m2: {[key: string]: V}): {[key: string]: V} {
@ -81,8 +81,7 @@ export function merge<V>(m1: {[key: string]: V}, m2: {[key: string]: V}): {[key:
return m;
}
export function forEach<K, V>(
map: {[key: string]: V}, callback: /*(V, K) => void*/ Function): void {
export function forEach<K, V>(map: {[key: string]: V}, callback: (v: V, k: string) => void): void {
for (const prop in map) {
if (map.hasOwnProperty(prop)) {
callback(map[prop], prop);
@ -117,9 +116,9 @@ export function waitForMap<A, B>(
const concatted$ = concatAll.call(of (...waitFor));
const last$ = l.last.call(concatted$);
return map.call(last$, () => res);
} else {
return of (res);
}
return of (res);
}
export function andObservables(observables: Observable<Observable<any>>): Observable<boolean> {
@ -130,9 +129,11 @@ export function andObservables(observables: Observable<Observable<any>>): Observ
export function wrapIntoObservable<T>(value: T | Promise<T>| Observable<T>): Observable<T> {
if (value instanceof Observable) {
return value;
} else if (value instanceof Promise) {
return fromPromise(value);
} else {
return of (value);
}
if (value instanceof Promise) {
return fromPromise(value);
}
return of (value);
}

View File

@ -366,13 +366,13 @@ export declare abstract class UrlHandlingStrategy {
/** @stable */
export declare class UrlSegment {
parameters: {
[key: string]: string;
[name: string]: string;
};
path: string;
constructor(
path: string,
parameters: {
[key: string]: string;
[name: string]: string;
});
toString(): string;
}