feat(router): add support for using classes as guard
This commit is contained in:
parent
f04b6978fb
commit
f34af4f249
@ -6,6 +6,7 @@ export { RouterOutletMap } from './router_outlet_map';
|
|||||||
export { RouterConfig, Route } from './config';
|
export { RouterConfig, Route } from './config';
|
||||||
export { Params, PRIMARY_OUTLET } from './shared';
|
export { Params, PRIMARY_OUTLET } from './shared';
|
||||||
export { provideRouter } from './router_providers';
|
export { provideRouter } from './router_providers';
|
||||||
|
export { CanActivate, CanDeactivate } from './interfaces';
|
||||||
|
|
||||||
import { RouterOutlet } from './directives/router_outlet';
|
import { RouterOutlet } from './directives/router_outlet';
|
||||||
import { RouterLink } from './directives/router_link';
|
import { RouterLink } from './directives/router_link';
|
||||||
|
16
modules/@angular/router/src/interfaces.ts
Normal file
16
modules/@angular/router/src/interfaces.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import {Observable} from 'rxjs/Observable';
|
||||||
|
import {ActivatedRouteSnapshot, RouterStateSnapshot} from './router_state';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An interface a class can implement to be a guard deciding if a route can be activated.
|
||||||
|
*/
|
||||||
|
export interface CanActivate {
|
||||||
|
canActivate(route:ActivatedRouteSnapshot, state:RouterStateSnapshot):Observable<boolean> | boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An interface a class can implement to be a guard deciding if a route can be deactivated.
|
||||||
|
*/
|
||||||
|
export interface CanDeactivate<T> {
|
||||||
|
canDeactivate(component:T, route:ActivatedRouteSnapshot, state:RouterStateSnapshot):Observable<boolean> | boolean;
|
||||||
|
}
|
@ -254,7 +254,11 @@ class GuardChecks {
|
|||||||
if (!canActivate || canActivate.length === 0) return of(true);
|
if (!canActivate || canActivate.length === 0) return of(true);
|
||||||
return forkJoin(canActivate.map(c => {
|
return forkJoin(canActivate.map(c => {
|
||||||
const guard = this.injector.get(c);
|
const guard = this.injector.get(c);
|
||||||
return of(guard(future, this.future));
|
if (guard.canActivate) {
|
||||||
|
return of(guard.canActivate(future, this.future));
|
||||||
|
} else {
|
||||||
|
return of(guard(future, this.future));
|
||||||
|
}
|
||||||
})).map(and);
|
})).map(and);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -263,7 +267,11 @@ class GuardChecks {
|
|||||||
if (!canDeactivate || canDeactivate.length === 0) return of(true);
|
if (!canDeactivate || canDeactivate.length === 0) return of(true);
|
||||||
return forkJoin(canDeactivate.map(c => {
|
return forkJoin(canDeactivate.map(c => {
|
||||||
const guard = this.injector.get(c);
|
const guard = this.injector.get(c);
|
||||||
return of(guard(component, curr, this.curr));
|
if (guard.canDeactivate) {
|
||||||
|
return of(guard.canDeactivate(component, curr, this.curr));
|
||||||
|
} else {
|
||||||
|
return of(guard(component, curr, this.curr));
|
||||||
|
}
|
||||||
})).map(and);
|
})).map(and);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ import {TestComponentBuilder, ComponentFixture} from '@angular/compiler/testing'
|
|||||||
import { ComponentResolver } from '@angular/core';
|
import { ComponentResolver } from '@angular/core';
|
||||||
import { SpyLocation } from '@angular/common/testing';
|
import { SpyLocation } from '@angular/common/testing';
|
||||||
import { UrlSerializer, DefaultUrlSerializer, RouterOutletMap, Router, ActivatedRoute, ROUTER_DIRECTIVES, Params,
|
import { UrlSerializer, DefaultUrlSerializer, RouterOutletMap, Router, ActivatedRoute, ROUTER_DIRECTIVES, Params,
|
||||||
RouterStateSnapshot, ActivatedRouteSnapshot } from '../src/index';
|
RouterStateSnapshot, ActivatedRouteSnapshot, CanActivate, CanDeactivate } from '../src/index';
|
||||||
import { Observable } from 'rxjs/Observable';
|
import { Observable } from 'rxjs/Observable';
|
||||||
import 'rxjs/add/operator/map';
|
import 'rxjs/add/operator/map';
|
||||||
|
|
||||||
@ -330,13 +330,38 @@ describe("Integration", () => {
|
|||||||
|
|
||||||
describe("should activate a route when CanActivate returns true", () => {
|
describe("should activate a route when CanActivate returns true", () => {
|
||||||
beforeEachProviders(() => [
|
beforeEachProviders(() => [
|
||||||
{provide: 'alwaysFalse', useValue: (a:ActivatedRouteSnapshot, s:RouterStateSnapshot) => true}
|
{provide: 'alwaysTrue', useValue: (a:ActivatedRouteSnapshot, s:RouterStateSnapshot) => true}
|
||||||
]);
|
]);
|
||||||
|
|
||||||
it('works',
|
it('works',
|
||||||
fakeAsync(inject([Router, TestComponentBuilder, Location], (router, tcb, location) => {
|
fakeAsync(inject([Router, TestComponentBuilder, Location], (router, tcb, location) => {
|
||||||
router.resetConfig([
|
router.resetConfig([
|
||||||
{ path: 'team/:id', component: TeamCmp, canActivate: ["alwaysFalse"] }
|
{ path: 'team/:id', component: TeamCmp, canActivate: ["alwaysTrue"] }
|
||||||
|
]);
|
||||||
|
|
||||||
|
const fixture = tcb.createFakeAsync(RootCmp);
|
||||||
|
advance(fixture);
|
||||||
|
|
||||||
|
router.navigateByUrl('/team/22');
|
||||||
|
advance(fixture);
|
||||||
|
|
||||||
|
expect(location.path()).toEqual('/team/22');
|
||||||
|
})));
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("should work when given a class", () => {
|
||||||
|
class AlwaysTrue implements CanActivate {
|
||||||
|
canActivate(route:ActivatedRouteSnapshot, state:RouterStateSnapshot):boolean {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeEachProviders(() => [AlwaysTrue]);
|
||||||
|
|
||||||
|
it('works',
|
||||||
|
fakeAsync(inject([Router, TestComponentBuilder, Location], (router, tcb, location) => {
|
||||||
|
router.resetConfig([
|
||||||
|
{ path: 'team/:id', component: TeamCmp, canActivate: [AlwaysTrue] }
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const fixture = tcb.createFakeAsync(RootCmp);
|
const fixture = tcb.createFakeAsync(RootCmp);
|
||||||
@ -384,6 +409,31 @@ describe("Integration", () => {
|
|||||||
expect(location.path()).toEqual('/team/33');
|
expect(location.path()).toEqual('/team/33');
|
||||||
})));
|
})));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("should work when given a class", () => {
|
||||||
|
class AlwaysTrue implements CanDeactivate<TeamCmp> {
|
||||||
|
canDeactivate(component: TeamCmp, route:ActivatedRouteSnapshot, state:RouterStateSnapshot):boolean {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeEachProviders(() => [AlwaysTrue]);
|
||||||
|
|
||||||
|
it('works',
|
||||||
|
fakeAsync(inject([Router, TestComponentBuilder, Location], (router, tcb, location) => {
|
||||||
|
router.resetConfig([
|
||||||
|
{ path: 'team/:id', component: TeamCmp, canDeactivate: [AlwaysTrue] }
|
||||||
|
]);
|
||||||
|
|
||||||
|
const fixture = tcb.createFakeAsync(RootCmp);
|
||||||
|
advance(fixture);
|
||||||
|
|
||||||
|
router.navigateByUrl('/team/22');
|
||||||
|
advance(fixture);
|
||||||
|
|
||||||
|
expect(location.path()).toEqual('/team/22');
|
||||||
|
})));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -30,6 +30,7 @@
|
|||||||
"src/create_url_tree.ts",
|
"src/create_url_tree.ts",
|
||||||
"src/directives/router_outlet.ts",
|
"src/directives/router_outlet.ts",
|
||||||
"src/directives/router_link.ts",
|
"src/directives/router_link.ts",
|
||||||
|
"src/interfaces.ts",
|
||||||
"src/utils/tree.ts",
|
"src/utils/tree.ts",
|
||||||
"src/utils/collection.ts",
|
"src/utils/collection.ts",
|
||||||
"test/utils/tree.spec.ts",
|
"test/utils/tree.spec.ts",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user