fix(router): canDeactivate guards are not triggered for componentless routes
Closes #12375
This commit is contained in:
parent
7221632228
commit
b74185369f
|
@ -764,7 +764,7 @@ export class PreActivation {
|
||||||
});
|
});
|
||||||
forEach(
|
forEach(
|
||||||
prevChildren,
|
prevChildren,
|
||||||
(v: any, k: string) => this.deactivateOutletAndItChildren(v, outletMap._outlets[k]));
|
(v: any, k: string) => this.deactiveRouteAndItsChildren(v, outletMap._outlets[k]));
|
||||||
}
|
}
|
||||||
|
|
||||||
traverseRoutes(
|
traverseRoutes(
|
||||||
|
@ -794,14 +794,7 @@ export class PreActivation {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (curr) {
|
if (curr) {
|
||||||
// if we had a normal route, we need to deactivate only that outlet.
|
this.deactiveRouteAndItsChildren(currNode, outlet);
|
||||||
if (curr.component) {
|
|
||||||
this.deactivateOutletAndItChildren(curr, outlet);
|
|
||||||
|
|
||||||
// if we had a componentless route, we need to deactivate everything!
|
|
||||||
} else {
|
|
||||||
this.deactivateOutletMap(parentOutletMap);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.checks.push(new CanActivate(futurePath));
|
this.checks.push(new CanActivate(futurePath));
|
||||||
|
@ -816,19 +809,17 @@ export class PreActivation {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private deactivateOutletAndItChildren(route: ActivatedRouteSnapshot, outlet: RouterOutlet): void {
|
private deactiveRouteAndItsChildren(
|
||||||
if (outlet && outlet.isActivated) {
|
route: TreeNode<ActivatedRouteSnapshot>, outlet: RouterOutlet): void {
|
||||||
this.deactivateOutletMap(outlet.outletMap);
|
const prevChildren: {[key: string]: any} = nodeChildrenAsMap(route);
|
||||||
this.checks.push(new CanDeactivate(outlet.component, route));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private deactivateOutletMap(outletMap: RouterOutletMap): void {
|
forEach(prevChildren, (v: any, k: string) => {
|
||||||
forEach(outletMap._outlets, (v: RouterOutlet) => {
|
const childOutlet = outlet ? outlet.outletMap._outlets[k] : null;
|
||||||
if (v.isActivated) {
|
this.deactiveRouteAndItsChildren(v, childOutlet);
|
||||||
this.deactivateOutletAndItChildren(v.activatedRoute.snapshot, v);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const component = outlet && outlet.isActivated ? outlet.component : null;
|
||||||
|
this.checks.push(new CanDeactivate(component, route.value));
|
||||||
}
|
}
|
||||||
|
|
||||||
private runCanActivate(future: ActivatedRouteSnapshot): Observable<boolean> {
|
private runCanActivate(future: ActivatedRouteSnapshot): Observable<boolean> {
|
||||||
|
|
|
@ -1055,7 +1055,11 @@ describe('Integration', () => {
|
||||||
|
|
||||||
describe('CanDeactivate', () => {
|
describe('CanDeactivate', () => {
|
||||||
describe('should not deactivate a route when CanDeactivate returns false', () => {
|
describe('should not deactivate a route when CanDeactivate returns false', () => {
|
||||||
|
let log: any;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
log = [];
|
||||||
|
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
providers: [
|
providers: [
|
||||||
{
|
{
|
||||||
|
@ -1076,6 +1080,13 @@ describe('Integration', () => {
|
||||||
return a.params['name'] === 'victor';
|
return a.params['name'] === 'victor';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
provide: 'RecordingDeactivate',
|
||||||
|
useValue: (c: any, a: ActivatedRouteSnapshot, b: RouterStateSnapshot) => {
|
||||||
|
log.push(['Deactivate', a.routeConfig.path]);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
},
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1103,27 +1114,37 @@ describe('Integration', () => {
|
||||||
expect(canceledStatus).toEqual(false);
|
expect(canceledStatus).toEqual(false);
|
||||||
})));
|
})));
|
||||||
|
|
||||||
it('works (componentless route)',
|
it('works with componentless routes',
|
||||||
fakeAsync(inject([Router, Location], (router: Router, location: Location) => {
|
fakeAsync(inject([Router, Location], (router: Router, location: Location) => {
|
||||||
const fixture = createRoot(router, RootCmp);
|
const fixture = createRoot(router, RootCmp);
|
||||||
|
|
||||||
router.resetConfig([{
|
router.resetConfig([
|
||||||
path: 'parent/:id',
|
{
|
||||||
canDeactivate: ['CanDeactivateParent'],
|
path: 'grandparent',
|
||||||
children: [{path: 'simple', component: SimpleCmp}]
|
canDeactivate: ['RecordingDeactivate'],
|
||||||
}]);
|
children: [{
|
||||||
|
path: 'parent',
|
||||||
|
canDeactivate: ['RecordingDeactivate'],
|
||||||
|
children: [{
|
||||||
|
path: 'child',
|
||||||
|
canDeactivate: ['RecordingDeactivate'],
|
||||||
|
children: [{path: 'simple', component: SimpleCmp}]
|
||||||
|
}]
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
{path: 'simple', component: SimpleCmp}
|
||||||
|
]);
|
||||||
|
|
||||||
router.navigateByUrl('/parent/22/simple');
|
router.navigateByUrl('/grandparent/parent/child/simple');
|
||||||
advance(fixture);
|
advance(fixture);
|
||||||
expect(location.path()).toEqual('/parent/22/simple');
|
expect(location.path()).toEqual('/grandparent/parent/child/simple');
|
||||||
|
|
||||||
router.navigateByUrl('/parent/33/simple');
|
router.navigateByUrl('/simple');
|
||||||
advance(fixture);
|
advance(fixture);
|
||||||
expect(location.path()).toEqual('/parent/33/simple');
|
|
||||||
|
|
||||||
router.navigateByUrl('/parent/44/simple');
|
expect(log).toEqual([
|
||||||
advance(fixture);
|
['Deactivate', 'child'], ['Deactivate', 'parent'], ['Deactivate', 'grandparent']
|
||||||
expect(location.path()).toEqual('/parent/33/simple');
|
]);
|
||||||
})));
|
})));
|
||||||
|
|
||||||
it('works with a nested route',
|
it('works with a nested route',
|
||||||
|
|
Loading…
Reference in New Issue