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:
parent
445ed43b9a
commit
69fa3bbc03
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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 */
|
||||
|
|
Loading…
Reference in New Issue