@ -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;
|
||||
});
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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>;
|
||||
});
|
||||
}
|
||||
|
@ -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(
|
||||
|
@ -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) => {
|
||||
|
@ -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';
|
||||
|
||||
/**
|
||||
|
Reference in New Issue
Block a user