refactor(router): get guards only one time and simplify guard operator signature (#26239)

PR Close #26239
This commit is contained in:
Jason Aden 2018-10-03 11:47:46 -07:00
parent f859d83298
commit 532e53678d
4 changed files with 58 additions and 57 deletions

View File

@ -12,28 +12,27 @@ import {concatMap, every, first, map, mergeMap} from 'rxjs/operators';
import {ActivationStart, ChildActivationStart, Event} from '../events'; import {ActivationStart, ChildActivationStart, Event} from '../events';
import {NavigationTransition} from '../router'; import {NavigationTransition} from '../router';
import {ChildrenOutletContexts} from '../router_outlet_context';
import {ActivatedRouteSnapshot, RouterStateSnapshot} from '../router_state'; import {ActivatedRouteSnapshot, RouterStateSnapshot} from '../router_state';
import {andObservables, wrapIntoObservable} from '../utils/collection'; import {andObservables, wrapIntoObservable} from '../utils/collection';
import {CanActivate, CanDeactivate, Checks, getAllRouteGuards, getCanActivateChild, getToken} from '../utils/preactivation'; import {CanActivate, CanDeactivate, getCanActivateChild, getToken} from '../utils/preactivation';
export function checkGuards( export function checkGuards(moduleInjector: Injector, forwardEvent?: (evt: Event) => void):
rootContexts: ChildrenOutletContexts, moduleInjector: Injector, MonoTypeOperatorFunction<NavigationTransition> {
forwardEvent?: (evt: Event) => void): MonoTypeOperatorFunction<NavigationTransition> {
return function(source: Observable<NavigationTransition>) { return function(source: Observable<NavigationTransition>) {
return source.pipe(mergeMap(t => { return source.pipe(mergeMap(t => {
const {targetSnapshot, currentSnapshot} = t; const {targetSnapshot, currentSnapshot, guards: {canActivateChecks, canDeactivateChecks}} = t;
const checks = getAllRouteGuards(targetSnapshot !, currentSnapshot, rootContexts); if (canDeactivateChecks.length === 0 && canActivateChecks.length === 0) {
if (checks.canDeactivateChecks.length === 0 && checks.canActivateChecks.length === 0) {
return of ({...t, guardsResult: true}); return of ({...t, guardsResult: true});
} }
return runCanDeactivateChecks(checks, targetSnapshot !, currentSnapshot, moduleInjector) return runCanDeactivateChecks(
canDeactivateChecks, targetSnapshot !, currentSnapshot, moduleInjector)
.pipe( .pipe(
mergeMap((canDeactivate: boolean) => { mergeMap((canDeactivate: boolean) => {
return canDeactivate ? return canDeactivate ?
runCanActivateChecks(targetSnapshot !, checks, moduleInjector, forwardEvent) : runCanActivateChecks(
targetSnapshot !, canActivateChecks, moduleInjector, forwardEvent) :
of (false); of (false);
}), }),
map(guardsResult => ({...t, guardsResult}))); map(guardsResult => ({...t, guardsResult})));
@ -42,21 +41,19 @@ export function checkGuards(
} }
function runCanDeactivateChecks( function runCanDeactivateChecks(
checks: Checks, futureRSS: RouterStateSnapshot, currRSS: RouterStateSnapshot, checks: CanDeactivate[], futureRSS: RouterStateSnapshot, currRSS: RouterStateSnapshot,
moduleInjector: Injector): Observable<boolean> { moduleInjector: Injector): Observable<boolean> {
return from(checks.canDeactivateChecks) return from(checks).pipe(
.pipe(
mergeMap( mergeMap(
(check: CanDeactivate) => runCanDeactivate( (check: CanDeactivate) =>
check.component, check.route, currRSS, futureRSS, moduleInjector)), runCanDeactivate(check.component, check.route, currRSS, futureRSS, moduleInjector)),
every((result: boolean) => result === true)); every((result: boolean) => result === true));
} }
function runCanActivateChecks( function runCanActivateChecks(
futureSnapshot: RouterStateSnapshot, checks: Checks, moduleInjector: Injector, futureSnapshot: RouterStateSnapshot, checks: CanActivate[], moduleInjector: Injector,
forwardEvent?: (evt: Event) => void): Observable<boolean> { forwardEvent?: (evt: Event) => void): Observable<boolean> {
return from(checks.canActivateChecks) return from(checks).pipe(
.pipe(
concatMap((check: CanActivate) => andObservables(from([ concatMap((check: CanActivate) => andObservables(from([
fireChildActivationStart(check.route.parent, forwardEvent), fireChildActivationStart(check.route.parent, forwardEvent),
fireActivationStart(check.route, forwardEvent), fireActivationStart(check.route, forwardEvent),

View File

@ -12,25 +12,23 @@ import {concatMap, last, map, mergeMap, reduce} from 'rxjs/operators';
import {ResolveData} from '../config'; import {ResolveData} from '../config';
import {NavigationTransition} from '../router'; import {NavigationTransition} from '../router';
import {ChildrenOutletContexts} from '../router_outlet_context';
import {ActivatedRouteSnapshot, RouterStateSnapshot, inheritedParamsDataResolve} from '../router_state'; import {ActivatedRouteSnapshot, RouterStateSnapshot, inheritedParamsDataResolve} from '../router_state';
import {wrapIntoObservable} from '../utils/collection'; import {wrapIntoObservable} from '../utils/collection';
import {getAllRouteGuards, getToken} from '../utils/preactivation'; import {getToken} from '../utils/preactivation';
export function resolveData( export function resolveData(
rootContexts: ChildrenOutletContexts, paramsInheritanceStrategy: 'emptyOnly' | 'always', paramsInheritanceStrategy: 'emptyOnly' | 'always',
moduleInjector: Injector): MonoTypeOperatorFunction<NavigationTransition> { moduleInjector: Injector): MonoTypeOperatorFunction<NavigationTransition> {
return function(source: Observable<NavigationTransition>) { return function(source: Observable<NavigationTransition>) {
return source.pipe(mergeMap(t => { return source.pipe(mergeMap(t => {
const {targetSnapshot, currentSnapshot} = t; const {targetSnapshot, guards: {canActivateChecks}} = t;
const checks = getAllRouteGuards(targetSnapshot !, currentSnapshot, rootContexts);
if (!checks.canActivateChecks.length) { if (!canActivateChecks.length) {
return of (t); return of (t);
} }
return from(checks.canActivateChecks) return from(canActivateChecks)
.pipe( .pipe(
concatMap( concatMap(
check => runResolve( check => runResolve(

View File

@ -28,7 +28,7 @@ import {ActivatedRoute, RouterState, RouterStateSnapshot, createEmptyState} from
import {Params, isNavigationCancelingError} from './shared'; import {Params, isNavigationCancelingError} from './shared';
import {DefaultUrlHandlingStrategy, UrlHandlingStrategy} from './url_handling_strategy'; import {DefaultUrlHandlingStrategy, UrlHandlingStrategy} from './url_handling_strategy';
import {UrlSerializer, UrlTree, containsTree, createEmptyUrlTree} from './url_tree'; import {UrlSerializer, UrlTree, containsTree, createEmptyUrlTree} from './url_tree';
import {getAllRouteGuards} from './utils/preactivation'; import {Checks, getAllRouteGuards} from './utils/preactivation';
@ -185,6 +185,7 @@ export type NavigationTransition = {
targetSnapshot: RouterStateSnapshot | null, targetSnapshot: RouterStateSnapshot | null,
currentRouterState: RouterState, currentRouterState: RouterState,
targetRouterState: RouterState | null, targetRouterState: RouterState | null,
guards: Checks,
guardsResult: boolean | null, guardsResult: boolean | null,
}; };
@ -354,6 +355,7 @@ export class Router {
targetSnapshot: null, targetSnapshot: null,
currentRouterState: this.routerState, currentRouterState: this.routerState,
targetRouterState: null, targetRouterState: null,
guards: {canActivateChecks: [], canDeactivateChecks: []},
guardsResult: null, guardsResult: null,
}); });
this.navigations = this.setupNavigations(this.transitions); this.navigations = this.setupNavigations(this.transitions);
@ -478,9 +480,13 @@ export class Router {
this.triggerEvent(guardsStart); this.triggerEvent(guardsStart);
}), }),
checkGuards( map(t => ({
this.rootContexts, this.ngModule.injector, ...t,
(evt: Event) => this.triggerEvent(evt)), guards:
getAllRouteGuards(t.targetSnapshot !, t.currentSnapshot, this.rootContexts)
})),
checkGuards(this.ngModule.injector, (evt: Event) => this.triggerEvent(evt)),
tap(t => { tap(t => {
const guardsEnd = new GuardsCheckEnd( const guardsEnd = new GuardsCheckEnd(
@ -503,8 +509,7 @@ export class Router {
// --- RESOLVE --- // --- RESOLVE ---
switchTap(t => { switchTap(t => {
if (getAllRouteGuards(t.targetSnapshot !, t.currentSnapshot, this.rootContexts) if (t.guards.canActivateChecks.length) {
.canActivateChecks.length) {
return of (t).pipe( return of (t).pipe(
tap(t => { tap(t => {
const resolveStart = new ResolveStart( const resolveStart = new ResolveStart(
@ -513,7 +518,7 @@ export class Router {
this.triggerEvent(resolveStart); this.triggerEvent(resolveStart);
}), }),
resolveData( resolveData(
this.rootContexts, this.paramsInheritanceStrategy, this.paramsInheritanceStrategy,
this.ngModule.injector), // this.ngModule.injector), //
tap(t => { tap(t => {
const resolveEnd = new ResolveEnd( const resolveEnd = new ResolveEnd(

View File

@ -18,6 +18,7 @@ import {NavigationTransition, Router} from '../src/router';
import {ChildrenOutletContexts} from '../src/router_outlet_context'; import {ChildrenOutletContexts} from '../src/router_outlet_context';
import {RouterStateSnapshot, createEmptyStateSnapshot} from '../src/router_state'; import {RouterStateSnapshot, createEmptyStateSnapshot} from '../src/router_state';
import {DefaultUrlSerializer} from '../src/url_tree'; import {DefaultUrlSerializer} from '../src/url_tree';
import {getAllRouteGuards} from '../src/utils/preactivation';
import {TreeNode} from '../src/utils/tree'; import {TreeNode} from '../src/utils/tree';
import {RouterTestingModule} from '../testing/src/router_testing_module'; import {RouterTestingModule} from '../testing/src/router_testing_module';
@ -143,9 +144,8 @@ describe('Router', () => {
const futureState = new (RouterStateSnapshot as any)( const futureState = new (RouterStateSnapshot as any)(
'url', new TreeNode(empty.root, [new TreeNode(childSnapshot, [])])); 'url', new TreeNode(empty.root, [new TreeNode(childSnapshot, [])]));
of ({targetSnapshot: futureState, currentSnapshot: empty}) of ({guards: getAllRouteGuards(futureState, empty, new ChildrenOutletContexts())})
.pipe(checkGuardsOperator( .pipe(checkGuardsOperator(TestBed, (evt) => { events.push(evt); }))
new ChildrenOutletContexts(), TestBed, (evt) => { events.push(evt); }))
.subscribe((x) => result = !!x.guardsResult, (e) => { throw e; }); .subscribe((x) => result = !!x.guardsResult, (e) => { throw e; });
expect(result).toBe(true); expect(result).toBe(true);
@ -178,9 +178,8 @@ describe('Router', () => {
new TreeNode(grandchildSnapshot, [new TreeNode(greatGrandchildSnapshot, [])]) new TreeNode(grandchildSnapshot, [new TreeNode(greatGrandchildSnapshot, [])])
])])); ])]));
of ({targetSnapshot: futureState, currentSnapshot: empty}) of ({guards: getAllRouteGuards(futureState, empty, new ChildrenOutletContexts())})
.pipe(checkGuardsOperator( .pipe(checkGuardsOperator(TestBed, (evt) => { events.push(evt); }))
new ChildrenOutletContexts(), TestBed, (evt) => { events.push(evt); }))
.subscribe((x) => result = !!x.guardsResult, (e) => { throw e; }); .subscribe((x) => result = !!x.guardsResult, (e) => { throw e; });
expect(result).toBe(true); expect(result).toBe(true);
@ -211,9 +210,8 @@ describe('Router', () => {
new TreeNode( new TreeNode(
empty.root, [new TreeNode(childSnapshot, [new TreeNode(grandchildSnapshot, [])])])); empty.root, [new TreeNode(childSnapshot, [new TreeNode(grandchildSnapshot, [])])]));
of ({targetSnapshot: futureState, currentSnapshot: currentState}) of ({guards: getAllRouteGuards(futureState, currentState, new ChildrenOutletContexts())})
.pipe(checkGuardsOperator( .pipe(checkGuardsOperator(TestBed, (evt) => { events.push(evt); }))
new ChildrenOutletContexts(), TestBed, (evt) => { events.push(evt); }))
.subscribe((x) => result = !!x.guardsResult, (e) => { throw e; }); .subscribe((x) => result = !!x.guardsResult, (e) => { throw e; });
expect(result).toBe(true); expect(result).toBe(true);
@ -257,9 +255,8 @@ describe('Router', () => {
greatGrandchildSnapshot, [new TreeNode(greatGreatGrandchildSnapshot, [])]) greatGrandchildSnapshot, [new TreeNode(greatGreatGrandchildSnapshot, [])])
])])])); ])])]));
of ({targetSnapshot: futureState, currentSnapshot: currentState}) of ({guards: getAllRouteGuards(futureState, currentState, new ChildrenOutletContexts())})
.pipe(checkGuardsOperator( .pipe(checkGuardsOperator(TestBed, (evt) => { events.push(evt); }))
new ChildrenOutletContexts(), TestBed, (evt) => { events.push(evt); }))
.subscribe((x) => result = !!x.guardsResult, (e) => { throw e; }); .subscribe((x) => result = !!x.guardsResult, (e) => { throw e; });
expect(result).toBe(true); expect(result).toBe(true);
@ -538,15 +535,19 @@ describe('Router', () => {
function checkResolveData( function checkResolveData(
future: RouterStateSnapshot, curr: RouterStateSnapshot, injector: any, check: any): void { future: RouterStateSnapshot, curr: RouterStateSnapshot, injector: any, check: any): void {
of ({ targetSnapshot: future, currentSnapshot: curr } as Partial<NavigationTransition>) of ({
.pipe(resolveDataOperator(new ChildrenOutletContexts(), 'emptyOnly', injector)) guards: getAllRouteGuards(future, curr, new ChildrenOutletContexts())
} as Partial<NavigationTransition>)
.pipe(resolveDataOperator('emptyOnly', injector))
.subscribe(check, (e) => { throw e; }); .subscribe(check, (e) => { throw e; });
} }
function checkGuards( function checkGuards(
future: RouterStateSnapshot, curr: RouterStateSnapshot, injector: any, future: RouterStateSnapshot, curr: RouterStateSnapshot, injector: any,
check: (result: boolean) => void): void { check: (result: boolean) => void): void {
of ({ targetSnapshot: future, currentSnapshot: curr } as Partial<NavigationTransition>) of ({
.pipe(checkGuardsOperator(new ChildrenOutletContexts(), injector)) guards: getAllRouteGuards(future, curr, new ChildrenOutletContexts())
} as Partial<NavigationTransition>)
.pipe(checkGuardsOperator(injector))
.subscribe(t => check(!!t.guardsResult), (e) => { throw e; }); .subscribe(t => check(!!t.guardsResult), (e) => { throw e; });
} }