From b5c4bf1c59c56d48b8a536aa61c7bd72d94c99fc Mon Sep 17 00:00:00 2001 From: Victor Berchet Date: Fri, 9 Dec 2016 10:44:46 -0800 Subject: [PATCH] refactor(router): misc refactoring (#13330) --- .../core/src/linker/element_injector.ts | 2 - modules/@angular/core/src/linker/view.ts | 1 - .../@angular/router/src/apply_redirects.ts | 3 +- .../@angular/router/src/create_url_tree.ts | 169 ++++++++-------- modules/@angular/router/src/private_export.ts | 15 +- .../src/private_import_platform-browser.ts | 2 +- modules/@angular/router/src/recognize.ts | 4 +- .../router/src/route_reuse_strategy.ts | 28 +-- modules/@angular/router/src/router.ts | 79 +++----- modules/@angular/router/src/router_state.ts | 189 +++++------------- modules/@angular/router/src/url_tree.ts | 113 ++++------- .../@angular/router/src/utils/collection.ts | 19 +- tools/public_api_guard/router/index.d.ts | 4 +- 13 files changed, 217 insertions(+), 411 deletions(-) diff --git a/modules/@angular/core/src/linker/element_injector.ts b/modules/@angular/core/src/linker/element_injector.ts index bb7a7d53b6..10f1c248fa 100644 --- a/modules/@angular/core/src/linker/element_injector.ts +++ b/modules/@angular/core/src/linker/element_injector.ts @@ -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, private _nodeIndex: number) { super(); } diff --git a/modules/@angular/core/src/linker/view.ts b/modules/@angular/core/src/linker/view.ts index 5c76cba583..67b084b4d4 100644 --- a/modules/@angular/core/src/linker/view.ts +++ b/modules/@angular/core/src/linker/view.ts @@ -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'; diff --git a/modules/@angular/router/src/apply_redirects.ts b/modules/@angular/router/src/apply_redirects.ts index bce789e881..d4dabdb987 100644 --- a/modules/@angular/router/src/apply_redirects.ts +++ b/modules/@angular/router/src/apply_redirects.ts @@ -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 { `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 { diff --git a/modules/@angular/router/src/create_url_tree.ts b/modules/@angular/router/src/create_url_tree.ts index b3e2dc3aa3..3c7dda40b3 100644 --- a/modules/@angular/router/src/create_url_tree.ts +++ b/modules/@angular/router/src/create_url_tree.ts @@ -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) { diff --git a/modules/@angular/router/src/private_export.ts b/modules/@angular/router/src/private_export.ts index dfe3a0582e..e6f8bdfdbf 100644 --- a/modules/@angular/router/src/private_export.ts +++ b/modules/@angular/router/src/private_export.ts @@ -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, +}; diff --git a/modules/@angular/router/src/private_import_platform-browser.ts b/modules/@angular/router/src/private_import_platform-browser.ts index 1b82a8c95e..e1001c4b37 100644 --- a/modules/@angular/router/src/private_import_platform-browser.ts +++ b/modules/@angular/router/src/private_import_platform-browser.ts @@ -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; diff --git a/modules/@angular/router/src/recognize.ts b/modules/@angular/router/src/recognize.ts index 3bd8c16de9..56aa62cfe3 100644 --- a/modules/@angular/router/src/recognize.ts +++ b/modules/@angular/router/src/recognize.ts @@ -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'; diff --git a/modules/@angular/router/src/route_reuse_strategy.ts b/modules/@angular/router/src/route_reuse_strategy.ts index 150d6d8084..f733b06dc6 100644 --- a/modules/@angular/router/src/route_reuse_strategy.ts +++ b/modules/@angular/router/src/route_reuse_strategy.ts @@ -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, - route: TreeNode + route: TreeNode, }; - /** * @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; } \ No newline at end of file diff --git a/modules/@angular/router/src/router.ts b/modules/@angular/router/src/router.ts index b6f64aa117..48c7506c9c 100644 --- a/modules/@angular/router/src/router.ts +++ b/modules/@angular/router/src/router.ts @@ -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, - imperative: boolean + imperative: boolean, }; @@ -312,9 +303,8 @@ export class Router { private currentUrlTree: UrlTree; private rawUrlTree: UrlTree; - private navigations: BehaviorSubject = - new BehaviorSubject(null); - private routerEvents: Subject = new Subject(); + private navigations = new BehaviorSubject(null); + private routerEvents = new Subject(); 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 { 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 { @@ -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); diff --git a/modules/@angular/router/src/router_state.ts b/modules/@angular/router/src/router_state.ts index 929535d71d..3bcb7f7367 100644 --- a/modules/@angular/router/src/router_state.ts +++ b/modules/@angular/router/src/router_state.ts @@ -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 { - /** - * @internal - */ + /** @internal */ constructor( root: TreeNode, - /** - * The current snapshot of the router state. - */ + /** The current snapshot of the router state */ public snapshot: RouterStateSnapshot) { super(root); setRouterStateSnapshot(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 = route.params.map(p => p.id); - * const url: Observable = route.url.map(s => s.join('')); - * const user = route.data.map(d => d.user); //includes `data` and `resolve` + * const url: Observable = 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, - - /** - * 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, - - /** - * 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, - - /** - * 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, - - /** - * 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, - - /** - * 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|string, // TODO: vsavkin: remove |string - futureSnapshot: ActivatedRouteSnapshot) { + /** The component of the route. It's a constant */ + // TODO(vsavkin): remove |string + public component: Type|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|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 { - /** - * @internal - */ + /** @internal */ constructor( - /** The url from which this snapshot was created */ public url: string, root: TreeNode) { super(root); diff --git a/modules/@angular/router/src/url_tree.ts b/modules/@angular/router/src/url_tree.ts index 0207f147b0..1adae222a9 100644 --- a/modules/@angular/router/src/url_tree.ts +++ b/modules/@angular/router/src/url_tree.ts @@ -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( * @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 { constructor(public first: A, public second: B) {} } + function pairs(obj: {[key: string]: T}): Pair[] { const res: Pair[] = []; 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} { diff --git a/modules/@angular/router/src/utils/collection.ts b/modules/@angular/router/src/utils/collection.ts index 5ed30e710f..5046aefd71 100644 --- a/modules/@angular/router/src/utils/collection.ts +++ b/modules/@angular/router/src/utils/collection.ts @@ -60,7 +60,7 @@ export function last(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(m1: {[key: string]: V}, m2: {[key: string]: V}): {[key: string]: V} { @@ -81,8 +81,7 @@ export function merge(m1: {[key: string]: V}, m2: {[key: string]: V}): {[key: return m; } -export function forEach( - map: {[key: string]: V}, callback: /*(V, K) => void*/ Function): void { +export function forEach(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( 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 { @@ -130,9 +129,11 @@ export function andObservables(observables: Observable>): Observ export function wrapIntoObservable(value: T | Promise| Observable): Observable { 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); } \ No newline at end of file diff --git a/tools/public_api_guard/router/index.d.ts b/tools/public_api_guard/router/index.d.ts index d692a8df74..eb5a2ce1d7 100644 --- a/tools/public_api_guard/router/index.d.ts +++ b/tools/public_api_guard/router/index.d.ts @@ -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; }