fix(guards): Cancel in-flight guards if one returns false

This commit is contained in:
Mike Ryan 2016-06-08 11:23:23 -07:00 committed by vsavkin
parent 6988a550ea
commit 97cf0e40d5
2 changed files with 41 additions and 33 deletions

View File

@ -1,6 +1,6 @@
import {Attribute, ComponentFactory, ComponentRef, Directive, ReflectiveInjector, ResolvedReflectiveProvider, ViewContainerRef} from "@angular/core"; import {Attribute, ComponentFactory, ComponentRef, Directive, ReflectiveInjector, ResolvedReflectiveProvider, ViewContainerRef} from '@angular/core';
import {RouterOutletMap} from "../router_outlet_map"; import {RouterOutletMap} from '../router_outlet_map';
import {PRIMARY_OUTLET} from "../shared"; import {PRIMARY_OUTLET} from '../shared';
@Directive({selector: 'router-outlet'}) @Directive({selector: 'router-outlet'})
export class RouterOutlet { export class RouterOutlet {

View File

@ -3,13 +3,15 @@ import 'rxjs/add/operator/scan';
import 'rxjs/add/operator/mergeMap'; import 'rxjs/add/operator/mergeMap';
import 'rxjs/add/operator/concat'; import 'rxjs/add/operator/concat';
import 'rxjs/add/operator/concatMap'; import 'rxjs/add/operator/concatMap';
import 'rxjs/add/operator/every';
import 'rxjs/add/operator/mergeAll';
import 'rxjs/add/observable/from';
import {Location} from '@angular/common'; import {Location} from '@angular/common';
import {ComponentResolver, Injector, ReflectiveInjector, Type} from '@angular/core'; import {ComponentResolver, Injector, ReflectiveInjector, Type} from '@angular/core';
import {Observable} from 'rxjs/Observable'; import {Observable} from 'rxjs/Observable';
import {Subject} from 'rxjs/Subject'; import {Subject} from 'rxjs/Subject';
import {Subscription} from 'rxjs/Subscription'; import {Subscription} from 'rxjs/Subscription';
import {forkJoin} from 'rxjs/observable/forkJoin';
import {of } from 'rxjs/observable/of'; import {of } from 'rxjs/observable/of';
import {RouterConfig} from './config'; import {RouterConfig} from './config';
@ -23,7 +25,7 @@ import {ActivatedRoute, ActivatedRouteSnapshot, RouterState, RouterStateSnapshot
import {PRIMARY_OUTLET, Params} from './shared'; import {PRIMARY_OUTLET, Params} from './shared';
import {UrlSerializer} from './url_serializer'; import {UrlSerializer} from './url_serializer';
import {UrlTree, createEmptyUrlTree} from './url_tree'; import {UrlTree, createEmptyUrlTree} from './url_tree';
import {and, forEach, shallowEqual} from './utils/collection'; import {forEach, shallowEqual} from './utils/collection';
import {TreeNode} from './utils/tree'; import {TreeNode} from './utils/tree';
export interface NavigationExtras { export interface NavigationExtras {
@ -298,16 +300,18 @@ class GuardChecks {
const currRoot = this.curr ? this.curr._root : null; const currRoot = this.curr ? this.curr._root : null;
this.traverseChildRoutes(futureRoot, currRoot, parentOutletMap); this.traverseChildRoutes(futureRoot, currRoot, parentOutletMap);
if (this.checks.length === 0) return of (true); if (this.checks.length === 0) return of (true);
return forkJoin(this.checks.map(s => { return Observable.from(this.checks)
if (s instanceof CanActivate) { .map(s => {
return this.runCanActivate(s.route); if (s instanceof CanActivate) {
} else if (s instanceof CanDeactivate) { return this.runCanActivate(s.route);
return this.runCanDeactivate(s.component, s.route); } else if (s instanceof CanDeactivate) {
} else { return this.runCanDeactivate(s.component, s.route);
throw new Error('Cannot be reached'); } else {
} throw new Error('Cannot be reached');
})) }
.map(and); })
.mergeAll()
.every(result => result === true);
} }
private traverseChildRoutes( private traverseChildRoutes(
@ -352,29 +356,33 @@ class GuardChecks {
private runCanActivate(future: ActivatedRouteSnapshot): Observable<boolean> { private runCanActivate(future: ActivatedRouteSnapshot): Observable<boolean> {
const canActivate = future._routeConfig ? future._routeConfig.canActivate : null; const canActivate = future._routeConfig ? future._routeConfig.canActivate : null;
if (!canActivate || canActivate.length === 0) return of (true); if (!canActivate || canActivate.length === 0) return of (true);
return forkJoin(canActivate.map(c => { return Observable.from(canActivate)
const guard = this.injector.get(c); .map(c => {
if (guard.canActivate) { const guard = this.injector.get(c);
return wrapIntoObservable(guard.canActivate(future, this.future)); if (guard.canActivate) {
} else { return wrapIntoObservable(guard.canActivate(future, this.future));
return wrapIntoObservable(guard(future, this.future)); } else {
} return wrapIntoObservable(guard(future, this.future));
})) }
.map(and); })
.mergeAll()
.every(result => result === true);
} }
private runCanDeactivate(component: Object, curr: ActivatedRouteSnapshot): Observable<boolean> { private runCanDeactivate(component: Object, curr: ActivatedRouteSnapshot): Observable<boolean> {
const canDeactivate = curr._routeConfig ? curr._routeConfig.canDeactivate : null; const canDeactivate = curr._routeConfig ? curr._routeConfig.canDeactivate : null;
if (!canDeactivate || canDeactivate.length === 0) return of (true); if (!canDeactivate || canDeactivate.length === 0) return of (true);
return forkJoin(canDeactivate.map(c => { return Observable.from(canDeactivate)
const guard = this.injector.get(c); .map(c => {
if (guard.canDeactivate) { const guard = this.injector.get(c);
return wrapIntoObservable(guard.canDeactivate(component, curr, this.curr)); if (guard.canDeactivate) {
} else { return wrapIntoObservable(guard.canDeactivate(component, curr, this.curr));
return wrapIntoObservable(guard(component, curr, this.curr)); } else {
} return wrapIntoObservable(guard(component, curr, this.curr));
})) }
.map(and); })
.mergeAll()
.every(result => result === true);
} }
} }