build: reformat repo to new clang@1.4.0 (#36613)

PR Close #36613
This commit is contained in:
Joey Perrott
2020-04-13 16:40:21 -07:00
committed by atscott
parent 5e80e7e216
commit 698b0288be
1160 changed files with 31667 additions and 24000 deletions

View File

@ -7,13 +7,13 @@
*/
import {Injector, NgModuleRef} from '@angular/core';
import {EmptyError, Observable, Observer, from, of } from 'rxjs';
import {EmptyError, from, Observable, Observer, of} from 'rxjs';
import {catchError, concatAll, every, first, map, mergeMap} from 'rxjs/operators';
import {LoadedRouterConfig, Route, Routes} from './config';
import {CanLoadFn} from './interfaces';
import {RouterConfigLoader} from './router_config_loader';
import {PRIMARY_OUTLET, Params, defaultUrlMatcher, navigationCancelingError} from './shared';
import {defaultUrlMatcher, navigationCancelingError, Params, PRIMARY_OUTLET} from './shared';
import {UrlSegment, UrlSegmentGroup, UrlSerializer, UrlTree} from './url_tree';
import {forEach, waitForMap, wrapIntoObservable} from './utils/collection';
import {isCanLoad, isFunction} from './utils/type_guards';
@ -21,7 +21,9 @@ import {isCanLoad, isFunction} from './utils/type_guards';
class NoMatch {
public segmentGroup: UrlSegmentGroup|null;
constructor(segmentGroup?: UrlSegmentGroup) { this.segmentGroup = segmentGroup || null; }
constructor(segmentGroup?: UrlSegmentGroup) {
this.segmentGroup = segmentGroup || null;
}
}
class AbsoluteRedirect {
@ -46,8 +48,9 @@ function namedOutletsRedirect(redirectTo: string): Observable<any> {
function canLoadFails(route: Route): Observable<LoadedRouterConfig> {
return new Observable<LoadedRouterConfig>(
(obs: Observer<LoadedRouterConfig>) => obs.error(navigationCancelingError(
`Cannot load children because the guard of the route "path: '${route.path}'" returned false`)));
(obs: Observer<LoadedRouterConfig>) => obs.error(
navigationCancelingError(`Cannot load children because the guard of the route "path: '${
route.path}'" returned false`)));
}
/**
@ -76,7 +79,7 @@ class ApplyRedirects {
this.expandSegmentGroup(this.ngModule, this.config, this.urlTree.root, PRIMARY_OUTLET);
const urlTrees$ = expanded$.pipe(
map((rootSegmentGroup: UrlSegmentGroup) => this.createUrlTree(
rootSegmentGroup, this.urlTree.queryParams, this.urlTree.fragment !)));
rootSegmentGroup, this.urlTree.queryParams, this.urlTree.fragment!)));
return urlTrees$.pipe(catchError((e: any) => {
if (e instanceof AbsoluteRedirect) {
// after an absolute redirect we do not apply any more redirects!
@ -98,7 +101,7 @@ class ApplyRedirects {
this.expandSegmentGroup(this.ngModule, this.config, tree.root, PRIMARY_OUTLET);
const mapped$ = expanded$.pipe(
map((rootSegmentGroup: UrlSegmentGroup) =>
this.createUrlTree(rootSegmentGroup, tree.queryParams, tree.fragment !)));
this.createUrlTree(rootSegmentGroup, tree.queryParams, tree.fragment!)));
return mapped$.pipe(catchError((e: any): Observable<UrlTree> => {
if (e instanceof NoMatch) {
throw this.noMatchError(e);
@ -144,7 +147,7 @@ class ApplyRedirects {
ngModule: NgModuleRef<any>, segmentGroup: UrlSegmentGroup, routes: Route[],
segments: UrlSegment[], outlet: string,
allowRedirects: boolean): Observable<UrlSegmentGroup> {
return of (...routes).pipe(
return of(...routes).pipe(
map((r: any) => {
const expanded$ = this.expandSegmentAgainstRoute(
ngModule, segmentGroup, routes, r, segments, outlet, allowRedirects);
@ -152,7 +155,7 @@ class ApplyRedirects {
if (e instanceof NoMatch) {
// TODO(i): this return type doesn't match the declared Observable<UrlSegmentGroup> -
// talk to Jason
return of (null) as any;
return of(null) as any;
}
throw e;
}));
@ -160,7 +163,7 @@ class ApplyRedirects {
concatAll(), first((s: any) => !!s), catchError((e: any, _: any) => {
if (e instanceof EmptyError || e.name === 'EmptyError') {
if (this.noLeftoversInUrl(segmentGroup, segments, outlet)) {
return of (new UrlSegmentGroup([], {}));
return of(new UrlSegmentGroup([], {}));
}
throw new NoMatch(segmentGroup);
}
@ -207,8 +210,8 @@ class ApplyRedirects {
private expandWildCardWithParamsAgainstRouteUsingRedirect(
ngModule: NgModuleRef<any>, routes: Route[], route: Route,
outlet: string): Observable<UrlSegmentGroup> {
const newTree = this.applyRedirectCommands([], route.redirectTo !, {});
if (route.redirectTo !.startsWith('/')) {
const newTree = this.applyRedirectCommands([], route.redirectTo!, {});
if (route.redirectTo!.startsWith('/')) {
return absoluteRedirect(newTree);
}
@ -226,8 +229,8 @@ class ApplyRedirects {
if (!matched) return noMatch(segmentGroup);
const newTree = this.applyRedirectCommands(
consumedSegments, route.redirectTo !, <any>positionalParamSegments);
if (route.redirectTo !.startsWith('/')) {
consumedSegments, route.redirectTo!, <any>positionalParamSegments);
if (route.redirectTo!.startsWith('/')) {
return absoluteRedirect(newTree);
}
@ -250,7 +253,7 @@ class ApplyRedirects {
}));
}
return of (new UrlSegmentGroup(segments, {}));
return of(new UrlSegmentGroup(segments, {}));
}
const {matched, consumedSegments, lastChild} = match(rawSegmentGroup, route, segments);
@ -273,7 +276,7 @@ class ApplyRedirects {
}
if (childConfig.length === 0 && slicedSegments.length === 0) {
return of (new UrlSegmentGroup(consumedSegments, {}));
return of(new UrlSegmentGroup(consumedSegments, {}));
}
const expanded$ = this.expandSegment(
@ -288,13 +291,13 @@ class ApplyRedirects {
Observable<LoadedRouterConfig> {
if (route.children) {
// The children belong to the same module
return of (new LoadedRouterConfig(route.children, ngModule));
return of(new LoadedRouterConfig(route.children, ngModule));
}
if (route.loadChildren) {
// lazy children belong to the loaded module
if (route._loadedConfig !== undefined) {
return of (route._loadedConfig);
return of(route._loadedConfig);
}
return runCanLoadGuard(ngModule.injector, route, segments)
@ -310,7 +313,7 @@ class ApplyRedirects {
}));
}
return of (new LoadedRouterConfig([], ngModule));
return of(new LoadedRouterConfig([], ngModule));
}
private lineralizeSegments(route: Route, urlTree: UrlTree): Observable<UrlSegment[]> {
@ -319,11 +322,11 @@ class ApplyRedirects {
while (true) {
res = res.concat(c.segments);
if (c.numberOfChildren === 0) {
return of (res);
return of(res);
}
if (c.numberOfChildren > 1 || !c.children[PRIMARY_OUTLET]) {
return namedOutletsRedirect(route.redirectTo !);
return namedOutletsRedirect(route.redirectTo!);
}
c = c.children[PRIMARY_OUTLET];
@ -406,7 +409,7 @@ class ApplyRedirects {
function runCanLoadGuard(
moduleInjector: Injector, route: Route, segments: UrlSegment[]): Observable<boolean> {
const canLoad = route.canLoad;
if (!canLoad || canLoad.length === 0) return of (true);
if (!canLoad || canLoad.length === 0) return of(true);
const obs = from(canLoad).pipe(map((injectionToken: any) => {
const guard = moduleInjector.get(injectionToken);
@ -452,9 +455,9 @@ function match(segmentGroup: UrlSegmentGroup, route: Route, segments: UrlSegment
return {
matched: true,
consumedSegments: res.consumed !,
lastChild: res.consumed.length !,
positionalParamSegments: res.posParams !,
consumedSegments: res.consumed!,
lastChild: res.consumed.length!,
positionalParamSegments: res.posParams!,
};
}
@ -464,16 +467,18 @@ function split(
if (slicedSegments.length > 0 &&
containsEmptyPathRedirectsWithNamedOutlets(segmentGroup, slicedSegments, config)) {
const s = new UrlSegmentGroup(
consumedSegments, createChildrenForEmptySegments(
config, new UrlSegmentGroup(slicedSegments, segmentGroup.children)));
consumedSegments,
createChildrenForEmptySegments(
config, new UrlSegmentGroup(slicedSegments, segmentGroup.children)));
return {segmentGroup: mergeTrivialChildren(s), slicedSegments: []};
}
if (slicedSegments.length === 0 &&
containsEmptyPathRedirects(segmentGroup, slicedSegments, config)) {
const s = new UrlSegmentGroup(
segmentGroup.segments, addEmptySegmentsToChildrenIfNeeded(
segmentGroup, slicedSegments, config, segmentGroup.children));
segmentGroup.segments,
addEmptySegmentsToChildrenIfNeeded(
segmentGroup, slicedSegments, config, segmentGroup.children));
return {segmentGroup: mergeTrivialChildren(s), slicedSegments};
}

View File

@ -7,7 +7,7 @@
*/
import {ActivatedRoute} from './router_state';
import {PRIMARY_OUTLET, Params} from './shared';
import {Params, PRIMARY_OUTLET} from './shared';
import {UrlSegment, UrlSegmentGroup, UrlTree} from './url_tree';
import {forEach, last, shallowEqual} from './utils/collection';
@ -164,7 +164,7 @@ function createPositionApplyingDoubleDots(
let dd = numberOfDoubleDots;
while (dd > ci) {
dd -= ci;
g = g.parent !;
g = g.parent!;
if (!g) {
throw new Error('Invalid number of \'../\'');
}

View File

@ -76,14 +76,12 @@ import {RouterLink, RouterLinkWithHref} from './router_link';
selector: '[routerLinkActive]',
exportAs: 'routerLinkActive',
})
export class RouterLinkActive implements OnChanges,
OnDestroy, AfterContentInit {
export class RouterLinkActive implements OnChanges, OnDestroy, AfterContentInit {
// TODO(issue/24571): remove '!'.
@ContentChildren(RouterLink, {descendants: true})
links !: QueryList<RouterLink>;
@ContentChildren(RouterLink, {descendants: true}) links!: QueryList<RouterLink>;
// TODO(issue/24571): remove '!'.
@ContentChildren(RouterLinkWithHref, {descendants: true})
linksWithHrefs !: QueryList<RouterLinkWithHref>;
linksWithHrefs!: QueryList<RouterLinkWithHref>;
private classes: string[] = [];
private subscription: Subscription;
@ -115,8 +113,12 @@ export class RouterLinkActive implements OnChanges,
this.classes = classes.filter(c => !!c);
}
ngOnChanges(changes: SimpleChanges): void { this.update(); }
ngOnDestroy(): void { this.subscription.unsubscribe(); }
ngOnChanges(changes: SimpleChanges): void {
this.update();
}
ngOnDestroy(): void {
this.subscription.unsubscribe();
}
private update(): void {
if (!this.links || !this.linksWithHrefs || !this.router.navigated) return;
@ -136,7 +138,7 @@ export class RouterLinkActive implements OnChanges,
}
private isLinkActive(router: Router): (link: (RouterLink|RouterLinkWithHref)) => boolean {
return (link: RouterLink | RouterLinkWithHref) =>
return (link: RouterLink|RouterLinkWithHref) =>
router.isActive(link.urlTree, this.routerLinkActiveOptions.exact);
}

View File

@ -56,7 +56,9 @@ export class RouterOutlet implements OnDestroy, OnInit {
parentContexts.onChildOutletCreated(this.name, this);
}
ngOnDestroy(): void { this.parentContexts.onChildOutletDestroyed(this.name); }
ngOnDestroy(): void {
this.parentContexts.onChildOutletDestroyed(this.name);
}
ngOnInit(): void {
if (!this.activated) {
@ -75,7 +77,9 @@ export class RouterOutlet implements OnDestroy, OnInit {
}
}
get isActivated(): boolean { return !!this.activated; }
get isActivated(): boolean {
return !!this.activated;
}
get component(): Object {
if (!this.activated) throw new Error('Outlet is not activated');
@ -131,7 +135,7 @@ export class RouterOutlet implements OnDestroy, OnInit {
}
this._activatedRoute = activatedRoute;
const snapshot = activatedRoute._futureSnapshot;
const component = <any>snapshot.routeConfig !.component;
const component = <any>snapshot.routeConfig!.component;
resolver = resolver || this.resolver;
const factory = resolver.resolveComponentFactory(component);
const childContexts = this.parentContexts.getOrCreateContext(this.name).children;

View File

@ -18,7 +18,7 @@ import {ActivatedRouteSnapshot, RouterStateSnapshot} from './router_state';
*
* @publicApi
*/
export type NavigationTrigger = 'imperative' | 'popstate' | 'hashchange';
export type NavigationTrigger = 'imperative'|'popstate'|'hashchange';
/**
* Base for events the router goes through, as opposed to events tied to a specific
@ -96,7 +96,9 @@ export class NavigationStart extends RouterEvent {
}
/** @docsNotRequired */
toString(): string { return `NavigationStart(id: ${this.id}, url: '${this.url}')`; }
toString(): string {
return `NavigationStart(id: ${this.id}, url: '${this.url}')`;
}
}
/**
@ -117,7 +119,8 @@ export class NavigationEnd extends RouterEvent {
/** @docsNotRequired */
toString(): string {
return `NavigationEnd(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}')`;
return `NavigationEnd(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${
this.urlAfterRedirects}')`;
}
}
@ -141,7 +144,9 @@ export class NavigationCancel extends RouterEvent {
}
/** @docsNotRequired */
toString(): string { return `NavigationCancel(id: ${this.id}, url: '${this.url}')`; }
toString(): string {
return `NavigationCancel(id: ${this.id}, url: '${this.url}')`;
}
}
/**
@ -186,7 +191,8 @@ export class RoutesRecognized extends RouterEvent {
/** @docsNotRequired */
toString(): string {
return `RoutesRecognized(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state})`;
return `RoutesRecognized(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${
this.urlAfterRedirects}', state: ${this.state})`;
}
}
@ -209,7 +215,8 @@ export class GuardsCheckStart extends RouterEvent {
}
toString(): string {
return `GuardsCheckStart(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state})`;
return `GuardsCheckStart(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${
this.urlAfterRedirects}', state: ${this.state})`;
}
}
@ -234,7 +241,8 @@ export class GuardsCheckEnd extends RouterEvent {
}
toString(): string {
return `GuardsCheckEnd(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state}, shouldActivate: ${this.shouldActivate})`;
return `GuardsCheckEnd(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${
this.urlAfterRedirects}', state: ${this.state}, shouldActivate: ${this.shouldActivate})`;
}
}
@ -260,7 +268,8 @@ export class ResolveStart extends RouterEvent {
}
toString(): string {
return `ResolveStart(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state})`;
return `ResolveStart(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${
this.urlAfterRedirects}', state: ${this.state})`;
}
}
@ -284,7 +293,8 @@ export class ResolveEnd extends RouterEvent {
}
toString(): string {
return `ResolveEnd(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state})`;
return `ResolveEnd(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${
this.urlAfterRedirects}', state: ${this.state})`;
}
}
@ -297,7 +307,9 @@ export class RouteConfigLoadStart {
constructor(
/** @docsNotRequired */
public route: Route) {}
toString(): string { return `RouteConfigLoadStart(path: ${this.route.path})`; }
toString(): string {
return `RouteConfigLoadStart(path: ${this.route.path})`;
}
}
/**
@ -309,7 +321,9 @@ export class RouteConfigLoadEnd {
constructor(
/** @docsNotRequired */
public route: Route) {}
toString(): string { return `RouteConfigLoadEnd(path: ${this.route.path})`; }
toString(): string {
return `RouteConfigLoadEnd(path: ${this.route.path})`;
}
}
/**
@ -429,5 +443,5 @@ export class Scroll {
*
* @publicApi
*/
export type Event = RouterEvent | RouteConfigLoadStart | RouteConfigLoadEnd | ChildActivationStart |
ChildActivationEnd | ActivationStart | ActivationEnd | Scroll;
export type Event = RouterEvent|RouteConfigLoadStart|RouteConfigLoadEnd|ChildActivationStart|
ChildActivationEnd|ActivationStart|ActivationEnd|Scroll;

View File

@ -7,7 +7,7 @@
*/
export {Data, DeprecatedLoadChildren, LoadChildren, LoadChildrenCallback, QueryParamsHandling, ResolveData, Route, Routes, RunGuardsAndResolvers, UrlMatchResult, UrlMatcher} from './config';
export {Data, DeprecatedLoadChildren, LoadChildren, LoadChildrenCallback, QueryParamsHandling, ResolveData, Route, Routes, RunGuardsAndResolvers, UrlMatcher, UrlMatchResult} from './config';
export {RouterLink, RouterLinkWithHref} from './directives/router_link';
export {RouterLinkActive} from './directives/router_link_active';
export {RouterOutlet} from './directives/router_outlet';
@ -16,11 +16,11 @@ export {CanActivate, CanActivateChild, CanDeactivate, CanLoad, Resolve} from './
export {DetachedRouteHandle, RouteReuseStrategy} from './route_reuse_strategy';
export {Navigation, NavigationExtras, Router} from './router';
export {ROUTES} from './router_config_loader';
export {ExtraOptions, InitialNavigation, ROUTER_CONFIGURATION, ROUTER_INITIALIZER, RouterModule, provideRoutes} from './router_module';
export {ExtraOptions, InitialNavigation, provideRoutes, ROUTER_CONFIGURATION, ROUTER_INITIALIZER, RouterModule} from './router_module';
export {ChildrenOutletContexts, OutletContext} from './router_outlet_context';
export {NoPreloading, PreloadAllModules, PreloadingStrategy, RouterPreloader} from './router_preloader';
export {ActivatedRoute, ActivatedRouteSnapshot, RouterState, RouterStateSnapshot} from './router_state';
export {PRIMARY_OUTLET, ParamMap, Params, convertToParamMap} from './shared';
export {convertToParamMap, ParamMap, Params, PRIMARY_OUTLET} from './shared';
export {UrlHandlingStrategy} from './url_handling_strategy';
export {DefaultUrlSerializer, UrlSegment, UrlSegmentGroup, UrlSerializer, UrlTree} from './url_tree';
export {VERSION} from './version';

View File

@ -88,7 +88,7 @@ export interface CanActivate {
}
export type CanActivateFn = (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) =>
Observable<boolean|UrlTree>| Promise<boolean|UrlTree>| boolean | UrlTree;
Observable<boolean|UrlTree>|Promise<boolean|UrlTree>|boolean|UrlTree;
/**
* @description
@ -175,7 +175,7 @@ export interface CanActivateChild {
}
export type CanActivateChildFn = (childRoute: ActivatedRouteSnapshot, state: RouterStateSnapshot) =>
Observable<boolean|UrlTree>| Promise<boolean|UrlTree>| boolean | UrlTree;
Observable<boolean|UrlTree>|Promise<boolean|UrlTree>|boolean|UrlTree;
/**
* @description
@ -259,7 +259,7 @@ export interface CanDeactivate<T> {
export type CanDeactivateFn<T> =
(component: T, currentRoute: ActivatedRouteSnapshot, currentState: RouterStateSnapshot,
nextState?: RouterStateSnapshot) =>
Observable<boolean|UrlTree>| Promise<boolean|UrlTree>| boolean | UrlTree;
Observable<boolean|UrlTree>|Promise<boolean|UrlTree>|boolean|UrlTree;
/**
* @description
@ -404,4 +404,4 @@ export interface CanLoad {
}
export type CanLoadFn = (route: Route, segments: UrlSegment[]) =>
Observable<boolean>| Promise<boolean>| boolean;
Observable<boolean>|Promise<boolean>|boolean;

View File

@ -14,16 +14,16 @@ import {ActivationEnd, ChildActivationEnd, Event} from '../events';
import {DetachedRouteHandleInternal, RouteReuseStrategy} from '../route_reuse_strategy';
import {NavigationTransition} from '../router';
import {ChildrenOutletContexts} from '../router_outlet_context';
import {ActivatedRoute, ActivatedRouteSnapshot, RouterState, advanceActivatedRoute} from '../router_state';
import {ActivatedRoute, ActivatedRouteSnapshot, advanceActivatedRoute, RouterState} from '../router_state';
import {forEach} from '../utils/collection';
import {TreeNode, nodeChildrenAsMap} from '../utils/tree';
import {nodeChildrenAsMap, TreeNode} from '../utils/tree';
export const activateRoutes =
(rootContexts: ChildrenOutletContexts, routeReuseStrategy: RouteReuseStrategy,
forwardEvent: (evt: Event) => void): MonoTypeOperatorFunction<NavigationTransition> =>
map(t => {
new ActivateRoutes(
routeReuseStrategy, t.targetRouterState !, t.currentRouterState, forwardEvent)
routeReuseStrategy, t.targetRouterState!, t.currentRouterState, forwardEvent)
.activate(rootContexts);
return t;
});

View File

@ -7,7 +7,7 @@
*/
import {Injector} from '@angular/core';
import {MonoTypeOperatorFunction, Observable, defer, from, of } from 'rxjs';
import {defer, from, MonoTypeOperatorFunction, Observable, of} from 'rxjs';
import {concatAll, concatMap, first, map, mergeMap} from 'rxjs/operators';
import {ActivationStart, ChildActivationStart, Event} from '../events';
@ -24,21 +24,20 @@ import {prioritizedGuardValue} from './prioritized_guard_value';
export function checkGuards(moduleInjector: Injector, forwardEvent?: (evt: Event) => void):
MonoTypeOperatorFunction<NavigationTransition> {
return function(source: Observable<NavigationTransition>) {
return source.pipe(mergeMap(t => {
const {targetSnapshot, currentSnapshot, guards: {canActivateChecks, canDeactivateChecks}} = t;
if (canDeactivateChecks.length === 0 && canActivateChecks.length === 0) {
return of ({...t, guardsResult: true});
return of({...t, guardsResult: true});
}
return runCanDeactivateChecks(
canDeactivateChecks, targetSnapshot !, currentSnapshot, moduleInjector)
canDeactivateChecks, targetSnapshot!, currentSnapshot, moduleInjector)
.pipe(
mergeMap(canDeactivate => {
return canDeactivate && isBoolean(canDeactivate) ?
runCanActivateChecks(
targetSnapshot !, canActivateChecks, moduleInjector, forwardEvent) :
of (canDeactivate);
targetSnapshot!, canActivateChecks, moduleInjector, forwardEvent) :
of(canDeactivate);
}),
map(guardsResult => ({...t, guardsResult})));
}));
@ -52,7 +51,9 @@ function runCanDeactivateChecks(
mergeMap(
check =>
runCanDeactivate(check.component, check.route, currRSS, futureRSS, moduleInjector)),
first(result => { return result !== true; }, true as boolean | UrlTree));
first(result => {
return result !== true;
}, true as boolean | UrlTree));
}
function runCanActivateChecks(
@ -70,48 +71,50 @@ function runCanActivateChecks(
return result !== true;
}, true as boolean | UrlTree));
}),
first(result => { return result !== true; }, true as boolean | UrlTree));
first(result => {
return result !== true;
}, true as boolean | UrlTree));
}
/**
* This should fire off `ActivationStart` events for each route being activated at this
* level.
* In other words, if you're activating `a` and `b` below, `path` will contain the
* `ActivatedRouteSnapshot`s for both and we will fire `ActivationStart` for both. Always
* return
* `true` so checks continue to run.
*/
* This should fire off `ActivationStart` events for each route being activated at this
* level.
* In other words, if you're activating `a` and `b` below, `path` will contain the
* `ActivatedRouteSnapshot`s for both and we will fire `ActivationStart` for both. Always
* return
* `true` so checks continue to run.
*/
function fireActivationStart(
snapshot: ActivatedRouteSnapshot | null,
snapshot: ActivatedRouteSnapshot|null,
forwardEvent?: (evt: Event) => void): Observable<boolean> {
if (snapshot !== null && forwardEvent) {
forwardEvent(new ActivationStart(snapshot));
}
return of (true);
return of(true);
}
/**
* This should fire off `ChildActivationStart` events for each route being activated at this
* level.
* In other words, if you're activating `a` and `b` below, `path` will contain the
* `ActivatedRouteSnapshot`s for both and we will fire `ChildActivationStart` for both. Always
* return
* `true` so checks continue to run.
*/
* This should fire off `ChildActivationStart` events for each route being activated at this
* level.
* In other words, if you're activating `a` and `b` below, `path` will contain the
* `ActivatedRouteSnapshot`s for both and we will fire `ChildActivationStart` for both. Always
* return
* `true` so checks continue to run.
*/
function fireChildActivationStart(
snapshot: ActivatedRouteSnapshot | null,
snapshot: ActivatedRouteSnapshot|null,
forwardEvent?: (evt: Event) => void): Observable<boolean> {
if (snapshot !== null && forwardEvent) {
forwardEvent(new ChildActivationStart(snapshot));
}
return of (true);
return of(true);
}
function runCanActivate(
futureRSS: RouterStateSnapshot, futureARS: ActivatedRouteSnapshot,
moduleInjector: Injector): Observable<boolean|UrlTree> {
const canActivate = futureARS.routeConfig ? futureARS.routeConfig.canActivate : null;
if (!canActivate || canActivate.length === 0) return of (true);
if (!canActivate || canActivate.length === 0) return of(true);
const canActivateObservables = canActivate.map((c: any) => {
return defer(() => {
@ -127,7 +130,7 @@ function runCanActivate(
return observable.pipe(first());
});
});
return of (canActivateObservables).pipe(prioritizedGuardValue());
return of(canActivateObservables).pipe(prioritizedGuardValue());
}
function runCanActivateChild(
@ -154,23 +157,22 @@ function runCanActivateChild(
}
return observable.pipe(first());
});
return of (guardsMapped).pipe(prioritizedGuardValue());
return of(guardsMapped).pipe(prioritizedGuardValue());
});
});
return of (canActivateChildGuardsMapped).pipe(prioritizedGuardValue());
return of(canActivateChildGuardsMapped).pipe(prioritizedGuardValue());
}
function runCanDeactivate(
component: Object | null, currARS: ActivatedRouteSnapshot, currRSS: RouterStateSnapshot,
component: Object|null, currARS: ActivatedRouteSnapshot, currRSS: RouterStateSnapshot,
futureRSS: RouterStateSnapshot, moduleInjector: Injector): Observable<boolean|UrlTree> {
const canDeactivate = currARS && currARS.routeConfig ? currARS.routeConfig.canDeactivate : null;
if (!canDeactivate || canDeactivate.length === 0) return of (true);
if (!canDeactivate || canDeactivate.length === 0) return of(true);
const canDeactivateObservables = canDeactivate.map((c: any) => {
const guard = getToken(c, currARS, moduleInjector);
let observable;
if (isCanDeactivate(guard)) {
observable =
wrapIntoObservable(guard.canDeactivate(component !, currARS, currRSS, futureRSS));
observable = wrapIntoObservable(guard.canDeactivate(component!, currARS, currRSS, futureRSS));
} else if (isFunction<CanDeactivateFn<any>>(guard)) {
observable = wrapIntoObservable(guard(component, currARS, currRSS, futureRSS));
} else {
@ -178,5 +180,5 @@ function runCanDeactivate(
}
return observable.pipe(first());
});
return of (canDeactivateObservables).pipe(prioritizedGuardValue());
return of(canDeactivateObservables).pipe(prioritizedGuardValue());
}

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {Observable, OperatorFunction, combineLatest} from 'rxjs';
import {combineLatest, Observable, OperatorFunction} from 'rxjs';
import {filter, map, scan, startWith, switchMap, take} from 'rxjs/operators';
import {UrlTree} from '../url_tree';
@ -20,36 +20,36 @@ export function prioritizedGuardValue():
return switchMap(obs => {
return combineLatest(
...obs.map(o => o.pipe(take(1), startWith(INITIAL_VALUE as INTERIM_VALUES))))
.pipe(
scan(
(acc: INTERIM_VALUES, list: INTERIM_VALUES[]) => {
let isPending = false;
return list.reduce((innerAcc, val, i: number) => {
if (innerAcc !== INITIAL_VALUE) return innerAcc;
.pipe(
scan(
(acc: INTERIM_VALUES, list: INTERIM_VALUES[]) => {
let isPending = false;
return list.reduce((innerAcc, val, i: number) => {
if (innerAcc !== INITIAL_VALUE) return innerAcc;
// Toggle pending flag if any values haven't been set yet
if (val === INITIAL_VALUE) isPending = true;
// Toggle pending flag if any values haven't been set yet
if (val === INITIAL_VALUE) isPending = true;
// Any other return values are only valid if we haven't yet hit a pending call.
// This guarantees that in the case of a guard at the bottom of the tree that
// returns a redirect, we will wait for the higher priority guard at the top to
// finish before performing the redirect.
if (!isPending) {
// Early return when we hit a `false` value as that should always cancel
// navigation
if (val === false) return val;
// Any other return values are only valid if we haven't yet hit a pending
// call. This guarantees that in the case of a guard at the bottom of the
// tree that returns a redirect, we will wait for the higher priority
// guard at the top to finish before performing the redirect.
if (!isPending) {
// Early return when we hit a `false` value as that should always
// cancel navigation
if (val === false) return val;
if (i === list.length - 1 || isUrlTree(val)) {
return val;
}
}
if (i === list.length - 1 || isUrlTree(val)) {
return val;
}
}
return innerAcc;
}, acc);
},
INITIAL_VALUE),
filter(item => item !== INITIAL_VALUE),
map(item => isUrlTree(item) ? item : item === true), //
take(1)) as Observable<boolean|UrlTree>;
return innerAcc;
}, acc);
},
INITIAL_VALUE),
filter(item => item !== INITIAL_VALUE),
map(item => isUrlTree(item) ? item : item === true), //
take(1)) as Observable<boolean|UrlTree>;
});
}

View File

@ -16,9 +16,9 @@ import {NavigationTransition} from '../router';
import {UrlTree} from '../url_tree';
export function recognize(
rootComponentType: Type<any>| null, config: Route[], serializer: (url: UrlTree) => string,
paramsInheritanceStrategy: 'emptyOnly' | 'always', relativeLinkResolution: 'legacy' |
'corrected'): MonoTypeOperatorFunction<NavigationTransition> {
rootComponentType: Type<any>|null, config: Route[], serializer: (url: UrlTree) => string,
paramsInheritanceStrategy: 'emptyOnly'|'always',
relativeLinkResolution: 'legacy'|'corrected'): MonoTypeOperatorFunction<NavigationTransition> {
return function(source: Observable<NavigationTransition>) {
return source.pipe(mergeMap(
t => recognizeFn(

View File

@ -7,32 +7,31 @@
*/
import {Injector} from '@angular/core';
import {MonoTypeOperatorFunction, Observable, from, of } from 'rxjs';
import {from, MonoTypeOperatorFunction, Observable, of} from 'rxjs';
import {concatMap, last, map, mergeMap, reduce} from 'rxjs/operators';
import {ResolveData} from '../config';
import {NavigationTransition} from '../router';
import {ActivatedRouteSnapshot, RouterStateSnapshot, inheritedParamsDataResolve} from '../router_state';
import {ActivatedRouteSnapshot, inheritedParamsDataResolve, RouterStateSnapshot} from '../router_state';
import {wrapIntoObservable} from '../utils/collection';
import {getToken} from '../utils/preactivation';
export function resolveData(
paramsInheritanceStrategy: 'emptyOnly' | 'always',
paramsInheritanceStrategy: 'emptyOnly'|'always',
moduleInjector: Injector): MonoTypeOperatorFunction<NavigationTransition> {
return function(source: Observable<NavigationTransition>) {
return source.pipe(mergeMap(t => {
const {targetSnapshot, guards: {canActivateChecks}} = t;
if (!canActivateChecks.length) {
return of (t);
return of(t);
}
return from(canActivateChecks)
.pipe(
concatMap(
check => runResolve(
check.route, targetSnapshot !, paramsInheritanceStrategy, moduleInjector)),
check.route, targetSnapshot!, paramsInheritanceStrategy, moduleInjector)),
reduce((_: any, __: any) => _), map(_ => t));
}));
};
@ -40,14 +39,15 @@ export function resolveData(
function runResolve(
futureARS: ActivatedRouteSnapshot, futureRSS: RouterStateSnapshot,
paramsInheritanceStrategy: 'emptyOnly' | 'always', moduleInjector: Injector) {
paramsInheritanceStrategy: 'emptyOnly'|'always', moduleInjector: Injector) {
const resolve = futureARS._resolve;
return resolveNode(resolve, futureARS, futureRSS, moduleInjector)
.pipe(map((resolvedData: any) => {
futureARS._resolvedData = resolvedData;
futureARS.data = {
...futureARS.data,
...inheritedParamsDataResolve(futureARS, paramsInheritanceStrategy).resolve};
...futureARS.data,
...inheritedParamsDataResolve(futureARS, paramsInheritanceStrategy).resolve
};
return null;
}));
}
@ -57,12 +57,14 @@ function resolveNode(
moduleInjector: Injector): Observable<any> {
const keys = Object.keys(resolve);
if (keys.length === 0) {
return of ({});
return of({});
}
if (keys.length === 1) {
const key = keys[0];
return getResolver(resolve[key], futureARS, futureRSS, moduleInjector)
.pipe(map((value: any) => { return {[key]: value}; }));
.pipe(map((value: any) => {
return {[key]: value};
}));
}
const data: {[k: string]: any} = {};
const runningResolvers$ = from(keys).pipe(mergeMap((key: string) => {

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {MonoTypeOperatorFunction, ObservableInput, from} from 'rxjs';
import {from, MonoTypeOperatorFunction, ObservableInput} from 'rxjs';
import {map, switchMap} from 'rxjs/operators';
/**

View File

@ -7,21 +7,21 @@
*/
import {Type} from '@angular/core';
import {Observable, Observer, of } from 'rxjs';
import {Observable, Observer, of} from 'rxjs';
import {Data, ResolveData, Route, Routes} from './config';
import {ActivatedRouteSnapshot, ParamsInheritanceStrategy, RouterStateSnapshot, inheritedParamsDataResolve} from './router_state';
import {PRIMARY_OUTLET, defaultUrlMatcher} from './shared';
import {UrlSegment, UrlSegmentGroup, UrlTree, mapChildrenIntoArray} from './url_tree';
import {ActivatedRouteSnapshot, inheritedParamsDataResolve, ParamsInheritanceStrategy, RouterStateSnapshot} from './router_state';
import {defaultUrlMatcher, PRIMARY_OUTLET} from './shared';
import {mapChildrenIntoArray, UrlSegment, UrlSegmentGroup, UrlTree} from './url_tree';
import {forEach, last} from './utils/collection';
import {TreeNode} from './utils/tree';
class NoMatch {}
export function recognize(
rootComponentType: Type<any>| null, config: Routes, urlTree: UrlTree, url: string,
rootComponentType: Type<any>|null, config: Routes, urlTree: UrlTree, url: string,
paramsInheritanceStrategy: ParamsInheritanceStrategy = 'emptyOnly',
relativeLinkResolution: 'legacy' | 'corrected' = 'legacy'): Observable<RouterStateSnapshot> {
relativeLinkResolution: 'legacy'|'corrected' = 'legacy'): Observable<RouterStateSnapshot> {
return new Recognizer(
rootComponentType, config, urlTree, url, paramsInheritanceStrategy,
relativeLinkResolution)
@ -43,13 +43,13 @@ class Recognizer {
const root = new ActivatedRouteSnapshot(
[], Object.freeze({}), Object.freeze({...this.urlTree.queryParams}),
this.urlTree.fragment !, {}, PRIMARY_OUTLET, this.rootComponentType, null,
this.urlTree.fragment!, {}, PRIMARY_OUTLET, this.rootComponentType, null,
this.urlTree.root, -1, {});
const rootNode = new TreeNode<ActivatedRouteSnapshot>(root, children);
const routeState = new RouterStateSnapshot(this.url, rootNode);
this.inheritParamsAndData(routeState._root);
return of (routeState);
return of(routeState);
} catch (e) {
return new Observable<RouterStateSnapshot>(
@ -119,10 +119,10 @@ class Recognizer {
let rawSlicedSegments: UrlSegment[] = [];
if (route.path === '**') {
const params = segments.length > 0 ? last(segments) !.parameters : {};
const params = segments.length > 0 ? last(segments)!.parameters : {};
snapshot = new ActivatedRouteSnapshot(
segments, params, Object.freeze({...this.urlTree.queryParams}), this.urlTree.fragment !,
getData(route), outlet, route.component !, route, getSourceSegmentGroup(rawSegment),
segments, params, Object.freeze({...this.urlTree.queryParams}), this.urlTree.fragment!,
getData(route), outlet, route.component!, route, getSourceSegmentGroup(rawSegment),
getPathIndexShift(rawSegment) + segments.length, getResolve(route));
} else {
const result: MatchResult = match(rawSegment, route, segments);
@ -131,7 +131,7 @@ class Recognizer {
snapshot = new ActivatedRouteSnapshot(
consumedSegments, result.parameters, Object.freeze({...this.urlTree.queryParams}),
this.urlTree.fragment !, getData(route), outlet, route.component !, route,
this.urlTree.fragment!, getData(route), outlet, route.component!, route,
getSourceSegmentGroup(rawSegment),
getPathIndexShift(rawSegment) + consumedSegments.length, getResolve(route));
}
@ -169,7 +169,7 @@ function getChildConfig(route: Route): Route[] {
}
if (route.loadChildren) {
return route._loadedConfig !.routes;
return route._loadedConfig!.routes;
}
return [];
@ -195,7 +195,9 @@ function match(segmentGroup: UrlSegmentGroup, route: Route, segments: UrlSegment
if (!res) throw new NoMatch();
const posParams: {[n: string]: string} = {};
forEach(res.posParams !, (v: UrlSegment, k: string) => { posParams[k] = v.path; });
forEach(res.posParams!, (v: UrlSegment, k: string) => {
posParams[k] = v.path;
});
const parameters = res.consumed.length > 0 ?
{...posParams, ...res.consumed[res.consumed.length - 1].parameters} :
posParams;
@ -236,13 +238,14 @@ function getPathIndexShift(segmentGroup: UrlSegmentGroup): number {
function split(
segmentGroup: UrlSegmentGroup, consumedSegments: UrlSegment[], slicedSegments: UrlSegment[],
config: Route[], relativeLinkResolution: 'legacy' | 'corrected') {
config: Route[], relativeLinkResolution: 'legacy'|'corrected') {
if (slicedSegments.length > 0 &&
containsEmptyPathMatchesWithNamedOutlets(segmentGroup, slicedSegments, config)) {
const s = new UrlSegmentGroup(
consumedSegments, createChildrenForEmptyPaths(
segmentGroup, consumedSegments, config,
new UrlSegmentGroup(slicedSegments, segmentGroup.children)));
consumedSegments,
createChildrenForEmptyPaths(
segmentGroup, consumedSegments, config,
new UrlSegmentGroup(slicedSegments, segmentGroup.children)));
s._sourceSegment = segmentGroup;
s._segmentIndexShift = consumedSegments.length;
return {segmentGroup: s, slicedSegments: []};
@ -251,9 +254,10 @@ function split(
if (slicedSegments.length === 0 &&
containsEmptyPathMatches(segmentGroup, slicedSegments, config)) {
const s = new UrlSegmentGroup(
segmentGroup.segments, addEmptyPathsToChildrenIfNeeded(
segmentGroup, consumedSegments, slicedSegments, config,
segmentGroup.children, relativeLinkResolution));
segmentGroup.segments,
addEmptyPathsToChildrenIfNeeded(
segmentGroup, consumedSegments, slicedSegments, config, segmentGroup.children,
relativeLinkResolution));
s._sourceSegment = segmentGroup;
s._segmentIndexShift = consumedSegments.length;
return {segmentGroup: s, slicedSegments};
@ -268,7 +272,7 @@ function split(
function addEmptyPathsToChildrenIfNeeded(
segmentGroup: UrlSegmentGroup, consumedSegments: UrlSegment[], slicedSegments: UrlSegment[],
routes: Route[], children: {[name: string]: UrlSegmentGroup},
relativeLinkResolution: 'legacy' | 'corrected'): {[name: string]: UrlSegmentGroup} {
relativeLinkResolution: 'legacy'|'corrected'): {[name: string]: UrlSegmentGroup} {
const res: {[name: string]: UrlSegmentGroup} = {};
for (const r of routes) {
if (emptyPathMatch(segmentGroup, slicedSegments, r) && !children[getOutlet(r)]) {

View File

@ -63,10 +63,16 @@ export abstract class RouteReuseStrategy {
* Does not detach any subtrees. Reuses routes as long as their route config is the same.
*/
export class DefaultRouteReuseStrategy implements RouteReuseStrategy {
shouldDetach(route: ActivatedRouteSnapshot): boolean { return false; }
shouldDetach(route: ActivatedRouteSnapshot): boolean {
return false;
}
store(route: ActivatedRouteSnapshot, detachedTree: DetachedRouteHandle): void {}
shouldAttach(route: ActivatedRouteSnapshot): boolean { return false; }
retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle|null { return null; }
shouldAttach(route: ActivatedRouteSnapshot): boolean {
return false;
}
retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle|null {
return null;
}
shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
return future.routeConfig === curr.routeConfig;
}

View File

@ -7,8 +7,8 @@
*/
import {Location} from '@angular/common';
import {Compiler, Injector, NgModuleFactoryLoader, NgModuleRef, NgZone, Type, isDevMode, ɵConsole as Console} from '@angular/core';
import {BehaviorSubject, EMPTY, Observable, Subject, Subscription, defer, of } from 'rxjs';
import {Compiler, Injector, isDevMode, NgModuleFactoryLoader, NgModuleRef, NgZone, Type, ɵConsole as Console} from '@angular/core';
import {BehaviorSubject, defer, EMPTY, Observable, of, Subject, Subscription} from 'rxjs';
import {catchError, filter, finalize, map, switchMap, tap} from 'rxjs/operators';
import {QueryParamsHandling, Route, Routes, standardizeConfig, validateConfig} from './config';
@ -24,10 +24,10 @@ import {switchTap} from './operators/switch_tap';
import {DefaultRouteReuseStrategy, RouteReuseStrategy} from './route_reuse_strategy';
import {RouterConfigLoader} from './router_config_loader';
import {ChildrenOutletContexts} from './router_outlet_context';
import {ActivatedRoute, RouterState, RouterStateSnapshot, createEmptyState} from './router_state';
import {Params, isNavigationCancelingError, navigationCancelingError} from './shared';
import {ActivatedRoute, createEmptyState, RouterState, RouterStateSnapshot} from './router_state';
import {isNavigationCancelingError, navigationCancelingError, Params} from './shared';
import {DefaultUrlHandlingStrategy, UrlHandlingStrategy} from './url_handling_strategy';
import {UrlSerializer, UrlTree, containsTree, createEmptyUrlTree} from './url_tree';
import {containsTree, createEmptyUrlTree, UrlSerializer, UrlTree} from './url_tree';
import {Checks, getAllRouteGuards} from './utils/preactivation';
import {isUrlTree} from './utils/type_guards';
@ -49,16 +49,16 @@ export interface NavigationExtras {
*
* ```
* [{
* path: 'parent',
* component: ParentComponent,
* children: [{
* path: 'list',
* component: ListComponent
* },{
* path: 'child',
* component: ChildComponent
* }]
* }]
* path: 'parent',
* component: ParentComponent,
* children: [{
* path: 'list',
* component: ListComponent
* },{
* path: 'child',
* component: ChildComponent
* }]
* }]
* ```
*
* The following `go()` function navigates to the `list` route by
@ -67,12 +67,12 @@ export interface NavigationExtras {
* ```
* @Component({...})
* class ChildComponent {
* constructor(private router: Router, private route: ActivatedRoute) {}
*
* go() {
* this.router.navigate(['../list'], { relativeTo: this.route });
* }
* }
* constructor(private router: Router, private route: ActivatedRoute) {}
*
* go() {
* this.router.navigate(['../list'], { relativeTo: this.route });
* }
* }
* ```
*/
relativeTo?: ActivatedRoute|null;
@ -243,13 +243,13 @@ export type NavigationTransition = {
reject: any,
promise: Promise<boolean>,
source: NavigationTrigger,
restoredState: RestoredState | null,
restoredState: RestoredState|null,
currentSnapshot: RouterStateSnapshot,
targetSnapshot: RouterStateSnapshot | null,
targetSnapshot: RouterStateSnapshot|null,
currentRouterState: RouterState,
targetRouterState: RouterState | null,
targetRouterState: RouterState|null,
guards: Checks,
guardsResult: boolean | UrlTree | null,
guardsResult: boolean|UrlTree|null,
};
/**
@ -273,7 +273,7 @@ function defaultRouterHook(snapshot: RouterStateSnapshot, runExtras: {
replaceUrl: boolean,
navigationId: number
}): Observable<void> {
return of (null) as any;
return of(null) as any;
}
/**
@ -298,7 +298,7 @@ export class Router {
private currentNavigation: Navigation|null = null;
// TODO(issue/24571): remove '!'.
private locationSubscription !: Subscription;
private locationSubscription!: Subscription;
private navigationId: number = 0;
private configLoader: RouterConfigLoader;
private ngModule: NgModuleRef<any>;
@ -343,10 +343,10 @@ export class Router {
*
* @internal
*/
hooks: {beforePreactivation: RouterHook, afterPreactivation: RouterHook} = {
beforePreactivation: defaultRouterHook,
afterPreactivation: defaultRouterHook
};
hooks: {
beforePreactivation: RouterHook,
afterPreactivation: RouterHook
} = {beforePreactivation: defaultRouterHook, afterPreactivation: defaultRouterHook};
/**
* A strategy for extracting and merging URLs.
@ -445,339 +445,358 @@ export class Router {
Observable<NavigationTransition> {
const eventsSubject = (this.events as Subject<Event>);
return transitions.pipe(
filter(t => t.id !== 0),
filter(t => t.id !== 0),
// Extract URL
map(t => ({
...t, extractedUrl: this.urlHandlingStrategy.extract(t.rawUrl)
} as NavigationTransition)),
// Extract URL
map(t =>
({...t, extractedUrl: this.urlHandlingStrategy.extract(t.rawUrl)} as
NavigationTransition)),
// Using switchMap so we cancel executing navigations when a new one comes in
switchMap(t => {
let completed = false;
let errored = false;
return of (t).pipe(
// Store the Navigation object
tap(t => {
this.currentNavigation = {
id: t.id,
initialUrl: t.currentRawUrl,
extractedUrl: t.extractedUrl,
trigger: t.source,
extras: t.extras,
previousNavigation: this.lastSuccessfulNavigation ?
{...this.lastSuccessfulNavigation, previousNavigation: null} :
null
};
}),
switchMap(t => {
const urlTransition =
!this.navigated || t.extractedUrl.toString() !== this.browserUrlTree.toString();
const processCurrentUrl =
(this.onSameUrlNavigation === 'reload' ? true : urlTransition) &&
this.urlHandlingStrategy.shouldProcessUrl(t.rawUrl);
// Using switchMap so we cancel executing navigations when a new one comes in
switchMap(t => {
let completed = false;
let errored = false;
return of(t).pipe(
// Store the Navigation object
tap(t => {
this.currentNavigation = {
id: t.id,
initialUrl: t.currentRawUrl,
extractedUrl: t.extractedUrl,
trigger: t.source,
extras: t.extras,
previousNavigation: this.lastSuccessfulNavigation ?
{...this.lastSuccessfulNavigation, previousNavigation: null} :
null
};
}),
switchMap(t => {
const urlTransition = !this.navigated ||
t.extractedUrl.toString() !== this.browserUrlTree.toString();
const processCurrentUrl =
(this.onSameUrlNavigation === 'reload' ? true : urlTransition) &&
this.urlHandlingStrategy.shouldProcessUrl(t.rawUrl);
if (processCurrentUrl) {
return of (t).pipe(
// Fire NavigationStart event
switchMap(t => {
const transition = this.transitions.getValue();
eventsSubject.next(new NavigationStart(
t.id, this.serializeUrl(t.extractedUrl), t.source, t.restoredState));
if (transition !== this.transitions.getValue()) {
return EMPTY;
}
return [t];
}),
if (processCurrentUrl) {
return of(t).pipe(
// Fire NavigationStart event
switchMap(t => {
const transition = this.transitions.getValue();
eventsSubject.next(new NavigationStart(
t.id, this.serializeUrl(t.extractedUrl), t.source,
t.restoredState));
if (transition !== this.transitions.getValue()) {
return EMPTY;
}
return [t];
}),
// This delay is required to match old behavior that forced navigation to
// always be async
switchMap(t => Promise.resolve(t)),
// This delay is required to match old behavior that forced navigation
// to always be async
switchMap(t => Promise.resolve(t)),
// ApplyRedirects
applyRedirects(
this.ngModule.injector, this.configLoader, this.urlSerializer,
this.config),
// ApplyRedirects
applyRedirects(
this.ngModule.injector, this.configLoader, this.urlSerializer,
this.config),
// Update the currentNavigation
tap(t => {
this.currentNavigation = {
...this.currentNavigation !,
finalUrl: t.urlAfterRedirects
};
}),
// Update the currentNavigation
tap(t => {
this.currentNavigation = {
...this.currentNavigation!,
finalUrl: t.urlAfterRedirects
};
}),
// Recognize
recognize(
this.rootComponentType, this.config, (url) => this.serializeUrl(url),
this.paramsInheritanceStrategy, this.relativeLinkResolution),
// Recognize
recognize(
this.rootComponentType, this.config,
(url) => this.serializeUrl(url), this.paramsInheritanceStrategy,
this.relativeLinkResolution),
// Update URL if in `eager` update mode
tap(t => {
if (this.urlUpdateStrategy === 'eager') {
if (!t.extras.skipLocationChange) {
this.setBrowserUrl(
t.urlAfterRedirects, !!t.extras.replaceUrl, t.id, t.extras.state);
}
this.browserUrlTree = t.urlAfterRedirects;
}
}),
// Update URL if in `eager` update mode
tap(t => {
if (this.urlUpdateStrategy === 'eager') {
if (!t.extras.skipLocationChange) {
this.setBrowserUrl(
t.urlAfterRedirects, !!t.extras.replaceUrl, t.id,
t.extras.state);
}
this.browserUrlTree = t.urlAfterRedirects;
}
}),
// Fire RoutesRecognized
tap(t => {
const routesRecognized = new RoutesRecognized(
t.id, this.serializeUrl(t.extractedUrl),
this.serializeUrl(t.urlAfterRedirects), t.targetSnapshot !);
eventsSubject.next(routesRecognized);
}));
} else {
const processPreviousUrl = urlTransition && this.rawUrlTree &&
this.urlHandlingStrategy.shouldProcessUrl(this.rawUrlTree);
/* When the current URL shouldn't be processed, but the previous one was, we
* handle this "error condition" by navigating to the previously successful URL,
* but leaving the URL intact.*/
if (processPreviousUrl) {
const {id, extractedUrl, source, restoredState, extras} = t;
const navStart = new NavigationStart(
id, this.serializeUrl(extractedUrl), source, restoredState);
eventsSubject.next(navStart);
const targetSnapshot =
createEmptyState(extractedUrl, this.rootComponentType).snapshot;
// Fire RoutesRecognized
tap(t => {
const routesRecognized = new RoutesRecognized(
t.id, this.serializeUrl(t.extractedUrl),
this.serializeUrl(t.urlAfterRedirects), t.targetSnapshot!);
eventsSubject.next(routesRecognized);
}));
} else {
const processPreviousUrl = urlTransition && this.rawUrlTree &&
this.urlHandlingStrategy.shouldProcessUrl(this.rawUrlTree);
/* When the current URL shouldn't be processed, but the previous one was,
* we handle this "error condition" by navigating to the previously
* successful URL, but leaving the URL intact.*/
if (processPreviousUrl) {
const {id, extractedUrl, source, restoredState, extras} = t;
const navStart = new NavigationStart(
id, this.serializeUrl(extractedUrl), source, restoredState);
eventsSubject.next(navStart);
const targetSnapshot =
createEmptyState(extractedUrl, this.rootComponentType).snapshot;
return of ({
...t,
targetSnapshot,
urlAfterRedirects: extractedUrl,
extras: {...extras, skipLocationChange: false, replaceUrl: false},
});
} else {
/* When neither the current or previous URL can be processed, do nothing other
* than update router's internal reference to the current "settled" URL. This
* way the next navigation will be coming from the current URL in the browser.
*/
this.rawUrlTree = t.rawUrl;
this.browserUrlTree = t.urlAfterRedirects;
t.resolve(null);
return EMPTY;
}
}
}),
return of({
...t,
targetSnapshot,
urlAfterRedirects: extractedUrl,
extras: {...extras, skipLocationChange: false, replaceUrl: false},
});
} else {
/* When neither the current or previous URL can be processed, do nothing
* other than update router's internal reference to the current "settled"
* URL. This way the next navigation will be coming from the current URL
* in the browser.
*/
this.rawUrlTree = t.rawUrl;
this.browserUrlTree = t.urlAfterRedirects;
t.resolve(null);
return EMPTY;
}
}
}),
// Before Preactivation
switchTap(t => {
const {
targetSnapshot,
id: navigationId,
extractedUrl: appliedUrlTree,
rawUrl: rawUrlTree,
extras: {skipLocationChange, replaceUrl}
} = t;
return this.hooks.beforePreactivation(targetSnapshot !, {
navigationId,
appliedUrlTree,
rawUrlTree,
skipLocationChange: !!skipLocationChange,
replaceUrl: !!replaceUrl,
});
}),
// Before Preactivation
switchTap(t => {
const {
targetSnapshot,
id: navigationId,
extractedUrl: appliedUrlTree,
rawUrl: rawUrlTree,
extras: {skipLocationChange, replaceUrl}
} = t;
return this.hooks.beforePreactivation(targetSnapshot!, {
navigationId,
appliedUrlTree,
rawUrlTree,
skipLocationChange: !!skipLocationChange,
replaceUrl: !!replaceUrl,
});
}),
// --- GUARDS ---
tap(t => {
const guardsStart = new GuardsCheckStart(
t.id, this.serializeUrl(t.extractedUrl), this.serializeUrl(t.urlAfterRedirects),
t.targetSnapshot !);
this.triggerEvent(guardsStart);
}),
// --- GUARDS ---
tap(t => {
const guardsStart = new GuardsCheckStart(
t.id, this.serializeUrl(t.extractedUrl),
this.serializeUrl(t.urlAfterRedirects), t.targetSnapshot!);
this.triggerEvent(guardsStart);
}),
map(t => ({
...t,
guards:
getAllRouteGuards(t.targetSnapshot !, t.currentSnapshot, this.rootContexts)
})),
map(t => ({
...t,
guards: getAllRouteGuards(
t.targetSnapshot!, t.currentSnapshot, this.rootContexts)
})),
checkGuards(this.ngModule.injector, (evt: Event) => this.triggerEvent(evt)),
tap(t => {
if (isUrlTree(t.guardsResult)) {
const error: Error&{url?: UrlTree} = navigationCancelingError(
`Redirecting to "${this.serializeUrl(t.guardsResult)}"`);
error.url = t.guardsResult;
throw error;
}
}),
checkGuards(this.ngModule.injector, (evt: Event) => this.triggerEvent(evt)),
tap(t => {
if (isUrlTree(t.guardsResult)) {
const error: Error&{url?: UrlTree} = navigationCancelingError(
`Redirecting to "${this.serializeUrl(t.guardsResult)}"`);
error.url = t.guardsResult;
throw error;
}
}),
tap(t => {
const guardsEnd = new GuardsCheckEnd(
t.id, this.serializeUrl(t.extractedUrl), this.serializeUrl(t.urlAfterRedirects),
t.targetSnapshot !, !!t.guardsResult);
this.triggerEvent(guardsEnd);
}),
tap(t => {
const guardsEnd = new GuardsCheckEnd(
t.id, this.serializeUrl(t.extractedUrl),
this.serializeUrl(t.urlAfterRedirects), t.targetSnapshot!,
!!t.guardsResult);
this.triggerEvent(guardsEnd);
}),
filter(t => {
if (!t.guardsResult) {
this.resetUrlToCurrentUrlTree();
const navCancel =
new NavigationCancel(t.id, this.serializeUrl(t.extractedUrl), '');
eventsSubject.next(navCancel);
t.resolve(false);
return false;
}
return true;
}),
filter(t => {
if (!t.guardsResult) {
this.resetUrlToCurrentUrlTree();
const navCancel =
new NavigationCancel(t.id, this.serializeUrl(t.extractedUrl), '');
eventsSubject.next(navCancel);
t.resolve(false);
return false;
}
return true;
}),
// --- RESOLVE ---
switchTap(t => {
if (t.guards.canActivateChecks.length) {
return of (t).pipe(
tap(t => {
const resolveStart = new ResolveStart(
t.id, this.serializeUrl(t.extractedUrl),
this.serializeUrl(t.urlAfterRedirects), t.targetSnapshot !);
this.triggerEvent(resolveStart);
}),
resolveData(
this.paramsInheritanceStrategy,
this.ngModule.injector), //
tap(t => {
const resolveEnd = new ResolveEnd(
t.id, this.serializeUrl(t.extractedUrl),
this.serializeUrl(t.urlAfterRedirects), t.targetSnapshot !);
this.triggerEvent(resolveEnd);
}));
}
return undefined;
}),
// --- RESOLVE ---
switchTap(t => {
if (t.guards.canActivateChecks.length) {
return of(t).pipe(
tap(t => {
const resolveStart = new ResolveStart(
t.id, this.serializeUrl(t.extractedUrl),
this.serializeUrl(t.urlAfterRedirects), t.targetSnapshot!);
this.triggerEvent(resolveStart);
}),
resolveData(
this.paramsInheritanceStrategy,
this.ngModule.injector), //
tap(t => {
const resolveEnd = new ResolveEnd(
t.id, this.serializeUrl(t.extractedUrl),
this.serializeUrl(t.urlAfterRedirects), t.targetSnapshot!);
this.triggerEvent(resolveEnd);
}));
}
return undefined;
}),
// --- AFTER PREACTIVATION ---
switchTap((t: NavigationTransition) => {
const {
targetSnapshot,
id: navigationId,
extractedUrl: appliedUrlTree,
rawUrl: rawUrlTree,
extras: {skipLocationChange, replaceUrl}
} = t;
return this.hooks.afterPreactivation(targetSnapshot !, {
navigationId,
appliedUrlTree,
rawUrlTree,
skipLocationChange: !!skipLocationChange,
replaceUrl: !!replaceUrl,
});
}),
// --- AFTER PREACTIVATION ---
switchTap((t: NavigationTransition) => {
const {
targetSnapshot,
id: navigationId,
extractedUrl: appliedUrlTree,
rawUrl: rawUrlTree,
extras: {skipLocationChange, replaceUrl}
} = t;
return this.hooks.afterPreactivation(targetSnapshot!, {
navigationId,
appliedUrlTree,
rawUrlTree,
skipLocationChange: !!skipLocationChange,
replaceUrl: !!replaceUrl,
});
}),
map((t: NavigationTransition) => {
const targetRouterState = createRouterState(
this.routeReuseStrategy, t.targetSnapshot !, t.currentRouterState);
return ({...t, targetRouterState});
}),
map((t: NavigationTransition) => {
const targetRouterState = createRouterState(
this.routeReuseStrategy, t.targetSnapshot!, t.currentRouterState);
return ({...t, targetRouterState});
}),
/* Once here, we are about to activate syncronously. The assumption is this will
succeed, and user code may read from the Router service. Therefore before
activation, we need to update router properties storing the current URL and the
RouterState, as well as updated the browser URL. All this should happen *before*
activating. */
tap((t: NavigationTransition) => {
this.currentUrlTree = t.urlAfterRedirects;
this.rawUrlTree = this.urlHandlingStrategy.merge(this.currentUrlTree, t.rawUrl);
/* Once here, we are about to activate syncronously. The assumption is this
will succeed, and user code may read from the Router service. Therefore
before activation, we need to update router properties storing the current
URL and the RouterState, as well as updated the browser URL. All this should
happen *before* activating. */
tap((t: NavigationTransition) => {
this.currentUrlTree = t.urlAfterRedirects;
this.rawUrlTree =
this.urlHandlingStrategy.merge(this.currentUrlTree, t.rawUrl);
(this as{routerState: RouterState}).routerState = t.targetRouterState !;
(this as {routerState: RouterState}).routerState = t.targetRouterState!;
if (this.urlUpdateStrategy === 'deferred') {
if (!t.extras.skipLocationChange) {
this.setBrowserUrl(
this.rawUrlTree, !!t.extras.replaceUrl, t.id, t.extras.state);
}
this.browserUrlTree = t.urlAfterRedirects;
}
}),
if (this.urlUpdateStrategy === 'deferred') {
if (!t.extras.skipLocationChange) {
this.setBrowserUrl(
this.rawUrlTree, !!t.extras.replaceUrl, t.id, t.extras.state);
}
this.browserUrlTree = t.urlAfterRedirects;
}
}),
activateRoutes(
this.rootContexts, this.routeReuseStrategy,
(evt: Event) => this.triggerEvent(evt)),
activateRoutes(
this.rootContexts, this.routeReuseStrategy,
(evt: Event) => this.triggerEvent(evt)),
tap({next() { completed = true; }, complete() { completed = true; }}),
finalize(() => {
/* When the navigation stream finishes either through error or success, we set the
* `completed` or `errored` flag. However, there are some situations where we could
* get here without either of those being set. For instance, a redirect during
* NavigationStart. Therefore, this is a catch-all to make sure the NavigationCancel
* event is fired when a navigation gets cancelled but not caught by other means. */
if (!completed && !errored) {
// Must reset to current URL tree here to ensure history.state is set. On a fresh
// page load, if a new navigation comes in before a successful navigation
// completes, there will be nothing in history.state.navigationId. This can cause
// sync problems with AngularJS sync code which looks for a value here in order
// to determine whether or not to handle a given popstate event or to leave it
// to the Angualr router.
this.resetUrlToCurrentUrlTree();
const navCancel = new NavigationCancel(
t.id, this.serializeUrl(t.extractedUrl),
`Navigation ID ${t.id} is not equal to the current navigation id ${this.navigationId}`);
eventsSubject.next(navCancel);
t.resolve(false);
}
// currentNavigation should always be reset to null here. If navigation was
// successful, lastSuccessfulTransition will have already been set. Therefore we
// can safely set currentNavigation to null here.
this.currentNavigation = null;
}),
catchError((e) => {
errored = true;
/* This error type is issued during Redirect, and is handled as a cancellation
* rather than an error. */
if (isNavigationCancelingError(e)) {
const redirecting = isUrlTree(e.url);
if (!redirecting) {
// Set property only if we're not redirecting. If we landed on a page and
// redirect to `/` route, the new navigation is going to see the `/` isn't
// a change from the default currentUrlTree and won't navigate. This is
// only applicable with initial navigation, so setting `navigated` only when
// not redirecting resolves this scenario.
this.navigated = true;
this.resetStateAndUrl(t.currentRouterState, t.currentUrlTree, t.rawUrl);
}
const navCancel =
new NavigationCancel(t.id, this.serializeUrl(t.extractedUrl), e.message);
eventsSubject.next(navCancel);
tap({
next() {
completed = true;
},
complete() {
completed = true;
}
}),
finalize(() => {
/* When the navigation stream finishes either through error or success, we
* set the `completed` or `errored` flag. However, there are some situations
* where we could get here without either of those being set. For instance, a
* redirect during NavigationStart. Therefore, this is a catch-all to make
* sure the NavigationCancel
* event is fired when a navigation gets cancelled but not caught by other
* means. */
if (!completed && !errored) {
// Must reset to current URL tree here to ensure history.state is set. On a
// fresh page load, if a new navigation comes in before a successful
// navigation completes, there will be nothing in
// history.state.navigationId. This can cause sync problems with AngularJS
// sync code which looks for a value here in order to determine whether or
// not to handle a given popstate event or to leave it to the Angualr
// router.
this.resetUrlToCurrentUrlTree();
const navCancel = new NavigationCancel(
t.id, this.serializeUrl(t.extractedUrl),
`Navigation ID ${t.id} is not equal to the current navigation id ${
this.navigationId}`);
eventsSubject.next(navCancel);
t.resolve(false);
}
// currentNavigation should always be reset to null here. If navigation was
// successful, lastSuccessfulTransition will have already been set. Therefore
// we can safely set currentNavigation to null here.
this.currentNavigation = null;
}),
catchError((e) => {
errored = true;
/* This error type is issued during Redirect, and is handled as a
* cancellation rather than an error. */
if (isNavigationCancelingError(e)) {
const redirecting = isUrlTree(e.url);
if (!redirecting) {
// Set property only if we're not redirecting. If we landed on a page and
// redirect to `/` route, the new navigation is going to see the `/`
// isn't a change from the default currentUrlTree and won't navigate.
// This is only applicable with initial navigation, so setting
// `navigated` only when not redirecting resolves this scenario.
this.navigated = true;
this.resetStateAndUrl(t.currentRouterState, t.currentUrlTree, t.rawUrl);
}
const navCancel = new NavigationCancel(
t.id, this.serializeUrl(t.extractedUrl), e.message);
eventsSubject.next(navCancel);
// When redirecting, we need to delay resolving the navigation
// promise and push it to the redirect navigation
if (!redirecting) {
t.resolve(false);
} else {
// setTimeout is required so this navigation finishes with
// the return EMPTY below. If it isn't allowed to finish
// processing, there can be multiple navigations to the same
// URL.
setTimeout(() => {
const mergedTree = this.urlHandlingStrategy.merge(e.url, this.rawUrlTree);
const extras = {
skipLocationChange: t.extras.skipLocationChange,
replaceUrl: this.urlUpdateStrategy === 'eager'
};
// When redirecting, we need to delay resolving the navigation
// promise and push it to the redirect navigation
if (!redirecting) {
t.resolve(false);
} else {
// setTimeout is required so this navigation finishes with
// the return EMPTY below. If it isn't allowed to finish
// processing, there can be multiple navigations to the same
// URL.
setTimeout(() => {
const mergedTree =
this.urlHandlingStrategy.merge(e.url, this.rawUrlTree);
const extras = {
skipLocationChange: t.extras.skipLocationChange,
replaceUrl: this.urlUpdateStrategy === 'eager'
};
return this.scheduleNavigation(
mergedTree, 'imperative', null, extras,
{resolve: t.resolve, reject: t.reject, promise: t.promise});
}, 0);
}
return this.scheduleNavigation(
mergedTree, 'imperative', null, extras,
{resolve: t.resolve, reject: t.reject, promise: t.promise});
}, 0);
}
/* All other errors should reset to the router's internal URL reference to the
* pre-error state. */
} else {
this.resetStateAndUrl(t.currentRouterState, t.currentUrlTree, t.rawUrl);
const navError = new NavigationError(t.id, this.serializeUrl(t.extractedUrl), e);
eventsSubject.next(navError);
try {
t.resolve(this.errorHandler(e));
} catch (ee) {
t.reject(ee);
}
}
return EMPTY;
}));
// TODO(jasonaden): remove cast once g3 is on updated TypeScript
})) as any as Observable<NavigationTransition>;
/* All other errors should reset to the router's internal URL reference to
* the pre-error state. */
} else {
this.resetStateAndUrl(t.currentRouterState, t.currentUrlTree, t.rawUrl);
const navError =
new NavigationError(t.id, this.serializeUrl(t.extractedUrl), e);
eventsSubject.next(navError);
try {
t.resolve(this.errorHandler(e));
} catch (ee) {
t.reject(ee);
}
}
return EMPTY;
}));
// TODO(jasonaden): remove cast once g3 is on updated TypeScript
})) as any as Observable<NavigationTransition>;
}
/**
@ -828,20 +847,27 @@ export class Router {
// Navigations coming from Angular router have a navigationId state property. When this
// exists, restore the state.
const state = change.state && change.state.navigationId ? change.state : null;
setTimeout(
() => { this.scheduleNavigation(rawUrlTree, source, state, {replaceUrl: true}); }, 0);
setTimeout(() => {
this.scheduleNavigation(rawUrlTree, source, state, {replaceUrl: true});
}, 0);
});
}
}
/** The current URL. */
get url(): string { return this.serializeUrl(this.currentUrlTree); }
get url(): string {
return this.serializeUrl(this.currentUrlTree);
}
/** The current Navigation object if one exists */
getCurrentNavigation(): Navigation|null { return this.currentNavigation; }
getCurrentNavigation(): Navigation|null {
return this.currentNavigation;
}
/** @internal */
triggerEvent(event: Event): void { (this.events as Subject<Event>).next(event); }
triggerEvent(event: Event): void {
(this.events as Subject<Event>).next(event);
}
/**
* Resets the configuration used for navigation and generating links.
@ -867,13 +893,15 @@ export class Router {
}
/** @docsNotRequired */
ngOnDestroy(): void { this.dispose(); }
ngOnDestroy(): void {
this.dispose();
}
/** Disposes of the router. */
dispose(): void {
if (this.locationSubscription) {
this.locationSubscription.unsubscribe();
this.locationSubscription = null !;
this.locationSubscription = null!;
}
}
@ -923,8 +951,14 @@ export class Router {
* ```
*/
createUrlTree(commands: any[], navigationExtras: NavigationExtras = {}): UrlTree {
const {relativeTo, queryParams, fragment,
preserveQueryParams, queryParamsHandling, preserveFragment} = navigationExtras;
const {
relativeTo,
queryParams,
fragment,
preserveQueryParams,
queryParamsHandling,
preserveFragment
} = navigationExtras;
if (isDevMode() && preserveQueryParams && <any>console && <any>console.warn) {
console.warn('preserveQueryParams is deprecated, use queryParamsHandling instead.');
}
@ -948,7 +982,7 @@ export class Router {
if (q !== null) {
q = this.removeEmptyProps(q);
}
return createUrlTree(a, this.currentUrlTree, commands, q !, f !);
return createUrlTree(a, this.currentUrlTree, commands, q!, f!);
}
/**
@ -1019,7 +1053,9 @@ export class Router {
}
/** Serializes a `UrlTree` into a string */
serializeUrl(url: UrlTree): string { return this.urlSerializer.serialize(url); }
serializeUrl(url: UrlTree): string {
return this.urlSerializer.serialize(url);
}
/** Parses a string into a `UrlTree` */
parseUrl(url: string): UrlTree {
@ -1064,7 +1100,9 @@ export class Router {
this.currentNavigation = null;
t.resolve(true);
},
e => { this.console.warn(`Unhandled Navigation Error: `); });
e => {
this.console.warn(`Unhandled Navigation Error: `);
});
}
private scheduleNavigation(
@ -1116,14 +1154,21 @@ export class Router {
source,
restoredState,
currentUrlTree: this.currentUrlTree,
currentRawUrl: this.rawUrlTree, rawUrl, extras, resolve, reject, promise,
currentRawUrl: this.rawUrlTree,
rawUrl,
extras,
resolve,
reject,
promise,
currentSnapshot: this.routerState.snapshot,
currentRouterState: this.routerState
});
// Make sure that the error is propagated even though `processNavigations` catch
// handler does not rethrow
return promise.catch((e: any) => { return Promise.reject(e); });
return promise.catch((e: any) => {
return Promise.reject(e);
});
}
private setBrowserUrl(
@ -1139,7 +1184,7 @@ export class Router {
}
private resetStateAndUrl(storedState: RouterState, storedUrl: UrlTree, rawUrl: UrlTree): void {
(this as{routerState: RouterState}).routerState = storedState;
(this as {routerState: RouterState}).routerState = storedState;
this.currentUrlTree = storedUrl;
this.rawUrlTree = this.urlHandlingStrategy.merge(this.currentUrlTree, rawUrl);
this.resetUrlToCurrentUrlTree();

View File

@ -7,8 +7,9 @@
*/
import {Compiler, InjectionToken, Injector, NgModuleFactory, NgModuleFactoryLoader} from '@angular/core';
import {Observable, from, of } from 'rxjs';
import {from, Observable, of} from 'rxjs';
import {map, mergeMap} from 'rxjs/operators';
import {LoadChildren, LoadedRouterConfig, Route, standardizeConfig} from './config';
import {flatten, wrapIntoObservable} from './utils/collection';
@ -30,7 +31,7 @@ export class RouterConfigLoader {
this.onLoadStartListener(route);
}
const moduleFactory$ = this.loadModuleFactory(route.loadChildren !);
const moduleFactory$ = this.loadModuleFactory(route.loadChildren!);
return moduleFactory$.pipe(map((factory: NgModuleFactory<any>) => {
if (this.onLoadEndListener) {
@ -50,7 +51,7 @@ export class RouterConfigLoader {
} else {
return wrapIntoObservable(loadChildren()).pipe(mergeMap((t: any) => {
if (t instanceof NgModuleFactory) {
return of (t);
return of(t);
} else {
return from(this.compiler.compileModuleAsync(t));
}

View File

@ -6,9 +6,10 @@
* found in the LICENSE file at https://angular.io/license
*/
import {APP_BASE_HREF, HashLocationStrategy, LOCATION_INITIALIZED, Location, LocationStrategy, PathLocationStrategy, PlatformLocation, ViewportScroller, ɵgetDOM as getDOM} from '@angular/common';
import {APP_BASE_HREF, HashLocationStrategy, Location, LOCATION_INITIALIZED, LocationStrategy, PathLocationStrategy, PlatformLocation, ViewportScroller, ɵgetDOM as getDOM} from '@angular/common';
import {ANALYZE_FOR_ENTRY_COMPONENTS, APP_BOOTSTRAP_LISTENER, APP_INITIALIZER, ApplicationRef, Compiler, ComponentRef, Inject, Injectable, InjectionToken, Injector, ModuleWithProviders, NgModule, NgModuleFactoryLoader, NgProbeToken, Optional, Provider, SkipSelf, SystemJsNgModuleLoader} from '@angular/core';
import {Subject, of } from 'rxjs';
import {of, Subject} from 'rxjs';
import {EmptyOutletComponent} from './components/empty_outlet';
import {Route, Routes} from './config';
import {RouterLink, RouterLinkWithHref} from './directives/router_link';
@ -136,7 +137,7 @@ export class RouterModule {
* @param routes An array of `Route` objects that define the navigation paths for the application.
* @param config An `ExtraOptions` configuration object that controls how navigation is performed.
* @return The new router module.
*/
*/
static forRoot(routes: Routes, config?: ExtraOptions): ModuleWithProviders<RouterModule> {
return {
ngModule: RouterModule,
@ -152,9 +153,8 @@ export class RouterModule {
{
provide: LocationStrategy,
useFactory: provideLocationStrategy,
deps: [
PlatformLocation, [new Inject(APP_BASE_HREF), new Optional()], ROUTER_CONFIGURATION
]
deps:
[PlatformLocation, [new Inject(APP_BASE_HREF), new Optional()], ROUTER_CONFIGURATION]
},
{
provide: RouterScroller,
@ -236,8 +236,9 @@ export function provideRoutes(routes: Routes): any {
* the root component gets created. Use if there is a reason to have
* more control over when the router starts its initial navigation due to some complex
* initialization logic.
* * 'legacy_enabled'- (Default, for compatibility.) The initial navigation starts after the root component has been created.
* The bootstrap is not blocked until the initial navigation is complete. @deprecated
* * 'legacy_enabled'- (Default, for compatibility.) The initial navigation starts after the root
* component has been created. The bootstrap is not blocked until the initial navigation is
* complete. @deprecated
* * 'legacy_disabled'- The initial navigation is not performed. The location listener is set up
* after the root component gets created. @deprecated since v4
* * `true` - same as 'legacy_enabled'. @deprecated since v4
@ -249,8 +250,7 @@ export function provideRoutes(routes: Routes): any {
*
* @publicApi
*/
export type InitialNavigation =
true | false | 'enabled' | 'disabled' | 'legacy_enabled' | 'legacy_disabled';
export type InitialNavigation = true|false|'enabled'|'disabled'|'legacy_enabled'|'legacy_disabled';
/**
* A set of configuration options for a router module, provided in the
@ -506,7 +506,7 @@ export class RouterInitializer {
appInitializer(): Promise<any> {
const p: Promise<any> = this.injector.get(LOCATION_INITIALIZED, Promise.resolve(null));
return p.then(() => {
let resolve: Function = null !;
let resolve: Function = null!;
const res = new Promise(r => resolve = r);
const router = this.injector.get(Router);
const opts = this.injector.get(ROUTER_CONFIGURATION);
@ -528,7 +528,7 @@ export class RouterInitializer {
// subsequent navigations should not be delayed
} else {
return of (null) as any;
return of(null) as any;
}
};
router.initialNavigation();
@ -561,7 +561,7 @@ export class RouterInitializer {
preloader.setUpPreloading();
routerScroller.init();
router.resetRootComponentType(ref.componentTypes[0]);
this.resultOfPreactivationDone.next(null !);
this.resultOfPreactivationDone.next(null!);
this.resultOfPreactivationDone.complete();
}

View File

@ -63,7 +63,9 @@ export class ChildrenOutletContexts {
return contexts;
}
onOutletReAttached(contexts: Map<string, OutletContext>) { this.contexts = contexts; }
onOutletReAttached(contexts: Map<string, OutletContext>) {
this.contexts = contexts;
}
getOrCreateContext(childName: string): OutletContext {
let context = this.getContext(childName);
@ -76,5 +78,7 @@ export class ChildrenOutletContexts {
return context;
}
getContext(childName: string): OutletContext|null { return this.contexts.get(childName) || null; }
getContext(childName: string): OutletContext|null {
return this.contexts.get(childName) || null;
}
}

View File

@ -1,13 +1,13 @@
/**
*@license
*Copyright Google Inc. All Rights Reserved.
*
*Use of this source code is governed by an MIT-style license that can be
*found in the LICENSE file at https://angular.io/license
*/
*@license
*Copyright Google Inc. All Rights Reserved.
*
*Use of this source code is governed by an MIT-style license that can be
*found in the LICENSE file at https://angular.io/license
*/
import {Compiler, Injectable, Injector, NgModuleFactoryLoader, NgModuleRef, OnDestroy} from '@angular/core';
import {Observable, Subscription, from, of } from 'rxjs';
import {from, Observable, of, Subscription} from 'rxjs';
import {catchError, concatMap, filter, map, mergeAll, mergeMap} from 'rxjs/operators';
import {LoadedRouterConfig, Route, Routes} from './config';
@ -40,7 +40,7 @@ export abstract class PreloadingStrategy {
*/
export class PreloadAllModules implements PreloadingStrategy {
preload(route: Route, fn: () => Observable<any>): Observable<any> {
return fn().pipe(catchError(() => of (null)));
return fn().pipe(catchError(() => of(null)));
}
}
@ -54,7 +54,9 @@ export class PreloadAllModules implements PreloadingStrategy {
* @publicApi
*/
export class NoPreloading implements PreloadingStrategy {
preload(route: Route, fn: () => Observable<any>): Observable<any> { return of (null); }
preload(route: Route, fn: () => Observable<any>): Observable<any> {
return of(null);
}
}
/**
@ -73,7 +75,7 @@ export class NoPreloading implements PreloadingStrategy {
export class RouterPreloader implements OnDestroy {
private loader: RouterConfigLoader;
// TODO(issue/24571): remove '!'.
private subscription !: Subscription;
private subscription!: Subscription;
constructor(
private router: Router, moduleLoader: NgModuleFactoryLoader, compiler: Compiler,
@ -99,7 +101,9 @@ export class RouterPreloader implements OnDestroy {
// TODO(jasonaden): This class relies on code external to the class to call setUpPreloading. If
// this hasn't been done, ngOnDestroy will fail as this.subscription will be undefined. This
// should be refactored.
ngOnDestroy(): void { this.subscription.unsubscribe(); }
ngOnDestroy(): void {
this.subscription.unsubscribe();
}
private processRoutes(ngModule: NgModuleRef<any>, routes: Routes): Observable<void> {
const res: Observable<any>[] = [];

View File

@ -15,9 +15,9 @@ import {Router} from './router';
export class RouterScroller implements OnDestroy {
// TODO(issue/24571): remove '!'.
private routerEventsSubscription !: Unsubscribable;
private routerEventsSubscription!: Unsubscribable;
// TODO(issue/24571): remove '!'.
private scrollEventsSubscription !: Unsubscribable;
private scrollEventsSubscription!: Unsubscribable;
private lastId = 0;
private lastSource: 'imperative'|'popstate'|'hashchange'|undefined = 'imperative';
@ -27,7 +27,7 @@ export class RouterScroller implements OnDestroy {
constructor(
private router: Router,
/** @docsNotRequired */ public readonly viewportScroller: ViewportScroller, private options: {
scrollPositionRestoration?: 'disabled' | 'enabled' | 'top',
scrollPositionRestoration?: 'disabled'|'enabled'|'top',
anchorScrolling?: 'disabled'|'enabled'
} = {}) {
// Default both options to 'disabled'

View File

@ -11,8 +11,8 @@ import {BehaviorSubject, Observable} from 'rxjs';
import {map} from 'rxjs/operators';
import {Data, ResolveData, Route} from './config';
import {PRIMARY_OUTLET, ParamMap, Params, convertToParamMap} from './shared';
import {UrlSegment, UrlSegmentGroup, UrlTree, equalSegments} from './url_tree';
import {convertToParamMap, ParamMap, Params, PRIMARY_OUTLET} from './shared';
import {equalSegments, UrlSegment, UrlSegmentGroup, UrlTree} from './url_tree';
import {shallowEqual, shallowEqualArrays} from './utils/collection';
import {Tree, TreeNode} from './utils/tree';
@ -57,10 +57,12 @@ export class RouterState extends Tree<ActivatedRoute> {
setRouterState(<RouterState>this, root);
}
toString(): string { return this.snapshot.toString(); }
toString(): string {
return this.snapshot.toString();
}
}
export function createEmptyState(urlTree: UrlTree, rootComponent: Type<any>| null): RouterState {
export function createEmptyState(urlTree: UrlTree, rootComponent: Type<any>|null): RouterState {
const snapshot = createEmptyStateSnapshot(urlTree, rootComponent);
const emptyUrl = new BehaviorSubject([new UrlSegment('', {})]);
const emptyParams = new BehaviorSubject({});
@ -75,7 +77,7 @@ export function createEmptyState(urlTree: UrlTree, rootComponent: Type<any>| nul
}
export function createEmptyStateSnapshot(
urlTree: UrlTree, rootComponent: Type<any>| null): RouterStateSnapshot {
urlTree: UrlTree, rootComponent: Type<any>|null): RouterStateSnapshot {
const emptyParams = {};
const emptyData = {};
const emptyQueryParams = {};
@ -98,15 +100,15 @@ export function createEmptyStateSnapshot(
*/
export class ActivatedRoute {
/** The current snapshot of this route */
snapshot !: ActivatedRouteSnapshot;
snapshot!: ActivatedRouteSnapshot;
/** @internal */
_futureSnapshot: ActivatedRouteSnapshot;
/** @internal */
_routerState !: RouterState;
_routerState!: RouterState;
/** @internal */
_paramMap !: Observable<ParamMap>;
_paramMap!: Observable<ParamMap>;
/** @internal */
_queryParamMap !: Observable<ParamMap>;
_queryParamMap!: Observable<ParamMap>;
/** @internal */
constructor(
@ -129,26 +131,40 @@ export class ActivatedRoute {
}
/** The configuration used to match this route. */
get routeConfig(): Route|null { return this._futureSnapshot.routeConfig; }
get routeConfig(): Route|null {
return this._futureSnapshot.routeConfig;
}
/** The root of the router state. */
get root(): ActivatedRoute { return this._routerState.root; }
get root(): ActivatedRoute {
return this._routerState.root;
}
/** The parent of this route in the router state tree. */
get parent(): ActivatedRoute|null { return this._routerState.parent(this); }
get parent(): ActivatedRoute|null {
return this._routerState.parent(this);
}
/** The first child of this route in the router state tree. */
get firstChild(): ActivatedRoute|null { return this._routerState.firstChild(this); }
get firstChild(): ActivatedRoute|null {
return this._routerState.firstChild(this);
}
/** The children of this route in the router state tree. */
get children(): ActivatedRoute[] { return this._routerState.children(this); }
get children(): ActivatedRoute[] {
return this._routerState.children(this);
}
/** The path from the root of the router state tree to this route. */
get pathFromRoot(): ActivatedRoute[] { return this._routerState.pathFromRoot(this); }
get pathFromRoot(): ActivatedRoute[] {
return this._routerState.pathFromRoot(this);
}
/** An Observable that contains a map of the required and optional parameters
/**
* An Observable that contains a map of the required and optional parameters
* specific to the route.
* The map supports retrieving single and multiple values from the same parameter. */
* The map supports retrieving single and multiple values from the same parameter.
*/
get paramMap(): Observable<ParamMap> {
if (!this._paramMap) {
this._paramMap = this.params.pipe(map((p: Params): ParamMap => convertToParamMap(p)));
@ -173,7 +189,7 @@ export class ActivatedRoute {
}
}
export type ParamsInheritanceStrategy = 'emptyOnly' | 'always';
export type ParamsInheritanceStrategy = 'emptyOnly'|'always';
/** @internal */
export type Inherited = {
@ -257,16 +273,16 @@ export class ActivatedRouteSnapshot {
_resolve: ResolveData;
/** @internal */
// TODO(issue/24571): remove '!'.
_resolvedData !: Data;
_resolvedData!: Data;
/** @internal */
// TODO(issue/24571): remove '!'.
_routerState !: RouterStateSnapshot;
_routerState!: RouterStateSnapshot;
/** @internal */
// TODO(issue/24571): remove '!'.
_paramMap !: ParamMap;
_paramMap!: ParamMap;
/** @internal */
// TODO(issue/24571): remove '!'.
_queryParamMap !: ParamMap;
_queryParamMap!: ParamMap;
/** @internal */
constructor(
@ -292,19 +308,29 @@ export class ActivatedRouteSnapshot {
}
/** The root of the router state */
get root(): ActivatedRouteSnapshot { return this._routerState.root; }
get root(): ActivatedRouteSnapshot {
return this._routerState.root;
}
/** The parent of this route in the router state tree */
get parent(): ActivatedRouteSnapshot|null { return this._routerState.parent(this); }
get parent(): ActivatedRouteSnapshot|null {
return this._routerState.parent(this);
}
/** The first child of this route in the router state tree */
get firstChild(): ActivatedRouteSnapshot|null { return this._routerState.firstChild(this); }
get firstChild(): ActivatedRouteSnapshot|null {
return this._routerState.firstChild(this);
}
/** The children of this route in the router state tree */
get children(): ActivatedRouteSnapshot[] { return this._routerState.children(this); }
get children(): ActivatedRouteSnapshot[] {
return this._routerState.children(this);
}
/** The path from the root of the router state tree to this route */
get pathFromRoot(): ActivatedRouteSnapshot[] { return this._routerState.pathFromRoot(this); }
get pathFromRoot(): ActivatedRouteSnapshot[] {
return this._routerState.pathFromRoot(this);
}
get paramMap(): ParamMap {
if (!this._paramMap) {
@ -363,10 +389,12 @@ export class RouterStateSnapshot extends Tree<ActivatedRouteSnapshot> {
setRouterState(<RouterStateSnapshot>this, root);
}
toString(): string { return serializeNode(this._root); }
toString(): string {
return serializeNode(this._root);
}
}
function setRouterState<U, T extends{_routerState: U}>(state: U, node: TreeNode<T>): void {
function setRouterState<U, T extends {_routerState: U}>(state: U, node: TreeNode<T>): void {
node.value._routerState = state;
node.children.forEach(c => setRouterState(state, c));
}
@ -416,5 +444,5 @@ export function equalParamsAndUrlSegments(
const parentsMismatch = !a.parent !== !b.parent;
return equalUrlParams && !parentsMismatch &&
(!a.parent || equalParamsAndUrlSegments(a.parent, b.parent !));
(!a.parent || equalParamsAndUrlSegments(a.parent, b.parent!));
}

View File

@ -69,9 +69,13 @@ export interface ParamMap {
class ParamsAsMap implements ParamMap {
private params: Params;
constructor(params: Params) { this.params = params || {}; }
constructor(params: Params) {
this.params = params || {};
}
has(name: string): boolean { return this.params.hasOwnProperty(name); }
has(name: string): boolean {
return this.params.hasOwnProperty(name);
}
get(name: string): string|null {
if (this.has(name)) {
@ -91,7 +95,9 @@ class ParamsAsMap implements ParamMap {
return [];
}
get keys(): string[] { return Object.keys(this.params); }
get keys(): string[] {
return Object.keys(this.params);
}
}
/**
@ -120,7 +126,7 @@ export function isNavigationCancelingError(error: Error) {
// Matches the route configuration (`route`) against the actual URL (`segments`).
export function defaultUrlMatcher(
segments: UrlSegment[], segmentGroup: UrlSegmentGroup, route: Route): UrlMatchResult|null {
const parts = route.path !.split('/');
const parts = route.path!.split('/');
if (parts.length > segments.length) {
// The actual URL is shorter than the config, no match

View File

@ -42,7 +42,13 @@ export abstract class UrlHandlingStrategy {
* @publicApi
*/
export class DefaultUrlHandlingStrategy implements UrlHandlingStrategy {
shouldProcessUrl(url: UrlTree): boolean { return true; }
extract(url: UrlTree): UrlTree { return url; }
merge(newUrlPart: UrlTree, wholeUrl: UrlTree): UrlTree { return newUrlPart; }
shouldProcessUrl(url: UrlTree): boolean {
return true;
}
extract(url: UrlTree): UrlTree {
return url;
}
merge(newUrlPart: UrlTree, wholeUrl: UrlTree): UrlTree {
return newUrlPart;
}
}

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {PRIMARY_OUTLET, ParamMap, Params, convertToParamMap} from './shared';
import {convertToParamMap, ParamMap, Params, PRIMARY_OUTLET} from './shared';
import {equalArraysOrString, forEach, shallowEqual} from './utils/collection';
export function createEmptyUrlTree() {
@ -106,7 +106,7 @@ function containsSegmentGroupHelper(
export class UrlTree {
/** @internal */
// TODO(issue/24571): remove '!'.
_queryParamMap !: ParamMap;
_queryParamMap!: ParamMap;
/** @internal */
constructor(
@ -125,7 +125,9 @@ export class UrlTree {
}
/** @docsNotRequired */
toString(): string { return DEFAULT_SERIALIZER.serialize(this); }
toString(): string {
return DEFAULT_SERIALIZER.serialize(this);
}
}
/**
@ -140,10 +142,10 @@ export class UrlTree {
export class UrlSegmentGroup {
/** @internal */
// TODO(issue/24571): remove '!'.
_sourceSegment !: UrlSegmentGroup;
_sourceSegment!: UrlSegmentGroup;
/** @internal */
// TODO(issue/24571): remove '!'.
_segmentIndexShift !: number;
_segmentIndexShift!: number;
/** The parent node in the url tree */
parent: UrlSegmentGroup|null = null;
@ -156,13 +158,19 @@ export class UrlSegmentGroup {
}
/** Whether the segment has child segments */
hasChildren(): boolean { return this.numberOfChildren > 0; }
hasChildren(): boolean {
return this.numberOfChildren > 0;
}
/** Number of child segments */
get numberOfChildren(): number { return Object.keys(this.children).length; }
get numberOfChildren(): number {
return Object.keys(this.children).length;
}
/** @docsNotRequired */
toString(): string { return serializePaths(this); }
toString(): string {
return serializePaths(this);
}
}
@ -195,7 +203,7 @@ export class UrlSegmentGroup {
export class UrlSegment {
/** @internal */
// TODO(issue/24571): remove '!'.
_parameterMap !: ParamMap;
_parameterMap!: ParamMap;
constructor(
/** The path part of a URL segment */
@ -212,7 +220,9 @@ export class UrlSegment {
}
/** @docsNotRequired */
toString(): string { return serializePath(this); }
toString(): string {
return serializePath(this);
}
}
export function equalSegments(as: UrlSegment[], bs: UrlSegment[]): boolean {
@ -291,7 +301,7 @@ export class DefaultUrlSerializer implements UrlSerializer {
const segment = `/${serializeSegment(tree.root, true)}`;
const query = serializeQueryParams(tree.queryParams);
const fragment =
typeof tree.fragment === `string` ? `#${encodeUriFragment(tree.fragment !)}` : '';
typeof tree.fragment === `string` ? `#${encodeUriFragment(tree.fragment!)}` : '';
return `${segment}${query}${fragment}`;
}
@ -329,7 +339,6 @@ function serializeSegment(segment: UrlSegmentGroup, root: boolean): string {
}
return [`${k}:${serializeSegment(v, false)}`];
});
return `${serializePaths(segment)}/(${children.join('//')})`;
@ -409,7 +418,7 @@ function serializeQueryParams(params: {[key: string]: any}): string {
`${encodeUriQuery(name)}=${encodeUriQuery(value)}`;
});
return strParams.length ? `?${strParams.join("&")}` : '';
return strParams.length ? `?${strParams.join('&')}` : '';
}
const SEGMENT_RE = /^[^\/()?;=#]+/;
@ -435,7 +444,9 @@ function matchUrlQueryParamValue(str: string): string {
class UrlParser {
private remaining: string;
constructor(private url: string) { this.remaining = url; }
constructor(private url: string) {
this.remaining = url;
}
parseRootSegment(): UrlSegmentGroup {
this.consumeOptional('/');
@ -584,7 +595,7 @@ class UrlParser {
throw new Error(`Cannot parse url '${this.url}'`);
}
let outletName: string = undefined !;
let outletName: string = undefined!;
if (path.indexOf(':') > -1) {
outletName = path.substr(0, path.indexOf(':'));
this.capture(outletName);
@ -602,7 +613,9 @@ class UrlParser {
return segments;
}
private peekStartsWith(str: string): boolean { return this.remaining.startsWith(str); }
private peekStartsWith(str: string): boolean {
return this.remaining.startsWith(str);
}
// Consumes the prefix when it is present and returns whether it has been consumed
private consumeOptional(str: string): boolean {

View File

@ -7,10 +7,10 @@
*/
import {NgModuleFactory, ɵisObservable as isObservable, ɵisPromise as isPromise} from '@angular/core';
import {Observable, from, of } from 'rxjs';
import {from, Observable, of} from 'rxjs';
import {concatAll, last as lastValue, map} from 'rxjs/operators';
import {PRIMARY_OUTLET, Params} from '../shared';
import {Params, PRIMARY_OUTLET} from '../shared';
export function shallowEqualArrays(a: any[], b: any[]): boolean {
if (a.length !== b.length) return false;
@ -43,7 +43,7 @@ export function shallowEqual(a: Params, b: Params): boolean {
/**
* Test equality for arrays of strings or a string.
*/
export function equalArraysOrString(a: string | string[], b: string | string[]) {
export function equalArraysOrString(a: string|string[], b: string|string[]) {
if (Array.isArray(a) && Array.isArray(b)) {
if (a.length != b.length) return false;
return a.every(aItem => b.indexOf(aItem) > -1);
@ -84,7 +84,7 @@ export function forEach<K, V>(map: {[key: string]: V}, callback: (v: V, k: strin
export function waitForMap<A, B>(
obj: {[k: string]: A}, fn: (k: string, a: A) => Observable<B>): Observable<{[k: string]: B}> {
if (Object.keys(obj).length === 0) {
return of ({});
return of({});
}
const waitHead: Observable<B>[] = [];
@ -103,11 +103,11 @@ export function waitForMap<A, B>(
// Closure compiler has problem with using spread operator here. So we use "Array.concat".
// Note that we also need to cast the new promise because TypeScript cannot infer the type
// when calling the "of" function through "Function.apply"
return (of .apply(null, waitHead.concat(waitTail)) as Observable<Observable<B>>)
return (of.apply(null, waitHead.concat(waitTail)) as Observable<Observable<B>>)
.pipe(concatAll(), lastValue(), map(() => res));
}
export function wrapIntoObservable<T>(value: T | Promise<T>| Observable<T>): Observable<T> {
export function wrapIntoObservable<T>(value: T|Promise<T>|Observable<T>): Observable<T> {
if (isObservable(value)) {
return value;
}
@ -119,5 +119,5 @@ export function wrapIntoObservable<T>(value: T | Promise<T>| Observable<T>): Obs
return from(Promise.resolve(value));
}
return of (value);
return of(value);
}

View File

@ -10,9 +10,13 @@ export class Tree<T> {
/** @internal */
_root: TreeNode<T>;
constructor(root: TreeNode<T>) { this._root = root; }
constructor(root: TreeNode<T>) {
this._root = root;
}
get root(): T { return this._root.value; }
get root(): T {
return this._root.value;
}
/**
* @internal
@ -52,7 +56,9 @@ export class Tree<T> {
/**
* @internal
*/
pathFromRoot(t: T): T[] { return findPath(t, this._root).map(s => s.value); }
pathFromRoot(t: T): T[] {
return findPath(t, this._root).map(s => s.value);
}
}
@ -86,11 +92,13 @@ function findPath<T>(value: T, node: TreeNode<T>): TreeNode<T>[] {
export class TreeNode<T> {
constructor(public value: T, public children: TreeNode<T>[]) {}
toString(): string { return `TreeNode(${this.value})`; }
toString(): string {
return `TreeNode(${this.value})`;
}
}
// Return the list of T indexed by outlet name
export function nodeChildrenAsMap<T extends{outlet: string}>(node: TreeNode<T>| null) {
export function nodeChildrenAsMap<T extends {outlet: string}>(node: TreeNode<T>|null) {
const map: {[outlet: string]: TreeNode<T>} = {};
if (node) {