mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
refactor(router): Simplify the prioritizeGuardValue logic (#46745)
The existing logic does something similar but in a more roundabout way. It reads _the whole array_. If it encounters a pending value, it ignores the remaining ones. If it hasn't encountered a pending value by the time it hits false/UrlTree, it returns that result. The new logic is the same, but reverses what we're looking for. Instead of processing the whole array, we stop when we encounter an initial value. When we encounter one that isn't `true`, that gets returned. If we get to the end and everything was `true`, return `true`. PR Close #46745
This commit is contained in:
committed by
Jessica Janiuk
parent
e59c7047de
commit
cee207ea11
@@ -7,48 +7,43 @@
|
||||
*/
|
||||
|
||||
import {combineLatest, Observable, OperatorFunction} from 'rxjs';
|
||||
import {filter, map, scan, startWith, switchMap, take} from 'rxjs/operators';
|
||||
import {filter, map, startWith, switchMap, take} from 'rxjs/operators';
|
||||
|
||||
import {UrlTree} from '../url_tree';
|
||||
import {isUrlTree} from '../utils/type_guards';
|
||||
|
||||
const INITIAL_VALUE = Symbol('INITIAL_VALUE');
|
||||
const INITIAL_VALUE = Symbol();
|
||||
declare type INTERIM_VALUES = typeof INITIAL_VALUE | boolean | UrlTree;
|
||||
|
||||
/**
|
||||
* Takes an array of observables and returns the result of the first to emit something other than
|
||||
* `true`.
|
||||
*
|
||||
* If all Observables emit `true`, then this operator emits true.
|
||||
* Also note that only the first value of each observable is used.
|
||||
*/
|
||||
export function prioritizedGuardValue():
|
||||
OperatorFunction<Observable<boolean|UrlTree>[], boolean|UrlTree> {
|
||||
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;
|
||||
|
||||
// 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;
|
||||
|
||||
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>;
|
||||
.pipe(
|
||||
map((results: INTERIM_VALUES[]) => {
|
||||
for (const result of results) {
|
||||
if (result === true) {
|
||||
// If result is true, check the next one
|
||||
continue;
|
||||
} else if (result === INITIAL_VALUE) {
|
||||
// If guard has not finished, we need to stop processing.
|
||||
return INITIAL_VALUE;
|
||||
} else {
|
||||
// Result finished and was not true. Return the result.
|
||||
return result;
|
||||
}
|
||||
}
|
||||
// Everything resolved to true. Return true.
|
||||
return true;
|
||||
}),
|
||||
filter((item): item is boolean|UrlTree => item !== INITIAL_VALUE),
|
||||
take(1),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user