feat(router): add an extra argument to CanDeactivate interface (#13560)

Adds a `nextState` argument to access the future url from `CanDeactivate`.

BEFORE:

    canDeactivate(component: T, route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean>|Promise<boolean>|boolean;

AFTER:

    canDeactivate(component: T, currentRoute: ActivatedRouteSnapshot, currentState: RouterStateSnapshot, nextState?: RouterStateSnapshot): Observable<boolean>|Promise<boolean>|boolean;

Closes #9853
This commit is contained in:
Dzmitry Shylovich 2016-12-28 01:08:06 +03:00 committed by Hans
parent 445ed43b9a
commit 69fa3bbc03
4 changed files with 75 additions and 8 deletions

View File

@ -185,8 +185,9 @@ export interface CanActivateChild {
*
* canDeactivate(
* component: TeamComponent,
* route: ActivatedRouteSnapshot,
* state: RouterStateSnapshot
* currentRoute: ActivatedRouteSnapshot,
* currentState: RouterStateSnapshot,
* nextState: RouterStateSnapshot
* ): Observable<boolean>|Promise<boolean>|boolean {
* return this.permissions.canDeactivate(this.currentUser, route.params.id);
* }
@ -223,7 +224,8 @@ export interface CanActivateChild {
* providers: [
* {
* provide: 'canDeactivateTeam',
* useValue: (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => true
* useValue: (component: TeamComponent, currentRoute: ActivatedRouteSnapshot, currentState:
* RouterStateSnapshot, nextState: RouterStateSnapshot) => true
* }
* ]
* })
@ -233,8 +235,9 @@ export interface CanActivateChild {
* @stable
*/
export interface CanDeactivate<T> {
canDeactivate(component: T, route: ActivatedRouteSnapshot, state: RouterStateSnapshot):
Observable<boolean>|Promise<boolean>|boolean;
canDeactivate(
component: T, currentRoute: ActivatedRouteSnapshot, currentState: RouterStateSnapshot,
nextState?: RouterStateSnapshot): Observable<boolean>|Promise<boolean>|boolean;
}
/**

View File

@ -985,9 +985,10 @@ export class PreActivation {
const guard = this.getToken(c, curr);
let observable: Observable<boolean>;
if (guard.canDeactivate) {
observable = wrapIntoObservable(guard.canDeactivate(component, curr, this.curr));
observable =
wrapIntoObservable(guard.canDeactivate(component, curr, this.curr, this.future));
} else {
observable = wrapIntoObservable(guard(component, curr, this.curr));
observable = wrapIntoObservable(guard(component, curr, this.curr, this.future));
}
return first.call(observable);
});

View File

@ -1647,6 +1647,69 @@ describe('Integration', () => {
expect(location.path()).toEqual('/simple');
})));
describe('next state', () => {
let log: string[];
class ClassWithNextState implements CanDeactivate<TeamCmp> {
canDeactivate(
component: TeamCmp, currentRoute: ActivatedRouteSnapshot,
currentState: RouterStateSnapshot, nextState: RouterStateSnapshot): boolean {
log.push(currentState.url, nextState.url);
return true;
}
}
beforeEach(() => {
log = [];
TestBed.configureTestingModule({
providers: [
ClassWithNextState, {
provide: 'FunctionWithNextState',
useValue: (cmp: any, currentRoute: ActivatedRouteSnapshot,
currentState: RouterStateSnapshot, nextState: RouterStateSnapshot) => {
log.push(currentState.url, nextState.url);
return true;
}
}
]
});
});
it('should pass next state as the 4 argument when guard is a class',
fakeAsync(inject([Router, Location], (router: Router, location: Location) => {
const fixture = createRoot(router, RootCmp);
router.resetConfig(
[{path: 'team/:id', component: TeamCmp, canDeactivate: [ClassWithNextState]}]);
router.navigateByUrl('/team/22');
advance(fixture);
expect(location.path()).toEqual('/team/22');
router.navigateByUrl('/team/33');
advance(fixture);
expect(location.path()).toEqual('/team/33');
expect(log).toEqual(['/team/22', '/team/33']);
})));
it('should pass next state as the 4 argument when guard is a function',
fakeAsync(inject([Router, Location], (router: Router, location: Location) => {
const fixture = createRoot(router, RootCmp);
router.resetConfig([
{path: 'team/:id', component: TeamCmp, canDeactivate: ['FunctionWithNextState']}
]);
router.navigateByUrl('/team/22');
advance(fixture);
expect(location.path()).toEqual('/team/22');
router.navigateByUrl('/team/33');
advance(fixture);
expect(location.path()).toEqual('/team/33');
expect(log).toEqual(['/team/22', '/team/33']);
})));
});
describe('should work when given a class', () => {
class AlwaysTrue implements CanDeactivate<TeamCmp> {

View File

@ -47,7 +47,7 @@ export interface CanActivateChild {
/** @stable */
export interface CanDeactivate<T> {
canDeactivate(component: T, route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean;
canDeactivate(component: T, currentRoute: ActivatedRouteSnapshot, currentState: RouterStateSnapshot, nextState?: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean;
}
/** @stable */