Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix(router): preserve replaceUrl when returning a urlTree from CanAct…
…ivate (#54042) This commit will fix the issue of the setting of NavigationExtras.replaceUrl being lost when returning a urlTree from a CanActivateFn. Fixes #53503 BREAKING CHANGE: When a a guard returns a `UrlTree` as a redirect, the redirecting navigation will now use `replaceUrl` if the initial navigation was also using the `replaceUrl` option. If this is not desirable, the redirect can configure new `NavigationBehaviorOptions` by returning a `RedirectCommand` with the desired options instead of `UrlTree`. PR Close #54042
- Loading branch information
1 parent
f3b6245
commit 60f1d68
Showing
2 changed files
with
104 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
/** | ||
* @license | ||
* Copyright Google LLC All Rights Reserved. | ||
* | ||
* Use of this source code is governed by an MIT-style license that can be | ||
* found in the LICENSE file at https://angular.io/license | ||
*/ | ||
|
||
import {Location} from '@angular/common'; | ||
import {Component, inject} from '@angular/core'; | ||
import {TestBed} from '@angular/core/testing'; | ||
import {Router, withRouterConfig} from '@angular/router'; | ||
|
||
import {provideRouter} from '../src/provide_router'; | ||
|
||
describe('`navigationExtras handling with redirects`', () => { | ||
describe(`eager url updates with navigationExtra.replaceUrl`, () => { | ||
it('should preserve `NavigationExtras.replaceUrl` when redirecting from guard using urlTree', async () => { | ||
TestBed.configureTestingModule({ | ||
providers: [ | ||
provideRouter( | ||
[ | ||
{ | ||
path: 'first', | ||
component: SimpleCmp, | ||
}, | ||
{ | ||
path: 'second', | ||
component: SimpleCmp, | ||
canActivate: [() => inject(Router).createUrlTree(['unguarded'])], | ||
}, | ||
{ | ||
path: 'unguarded', | ||
component: SimpleCmp, | ||
}, | ||
], | ||
withRouterConfig({ | ||
urlUpdateStrategy: 'eager', | ||
canceledNavigationResolution: 'computed', | ||
}), | ||
), | ||
], | ||
}); | ||
const location = TestBed.inject(Location); | ||
const router = TestBed.inject(Router); | ||
await router.navigateByUrl('first'); | ||
|
||
expect(location.path()).toEqual('/first'); | ||
expect(location.getState()).toEqual(jasmine.objectContaining({ɵrouterPageId: 1})); | ||
|
||
const navPromise = router.navigateByUrl('/second', {replaceUrl: true}); | ||
expect(router.getCurrentNavigation()?.extras.replaceUrl).toEqual(true); | ||
await navPromise; | ||
expect(location.path()).toEqual('/unguarded'); | ||
expect(location.getState()).toEqual(jasmine.objectContaining({ɵrouterPageId: 1})); | ||
}); | ||
}); | ||
|
||
describe(`deferred url updates function correctly when navigationExtras.replaceUrl false`, () => { | ||
it('should work when CanActivate redirects', async () => { | ||
TestBed.configureTestingModule({ | ||
providers: [ | ||
provideRouter( | ||
[ | ||
{ | ||
path: 'first', | ||
component: SimpleCmp, | ||
}, | ||
{ | ||
path: 'second', | ||
component: SimpleCmp, | ||
canActivate: [() => inject(Router).createUrlTree(['unguarded'])], | ||
}, | ||
{ | ||
path: 'unguarded', | ||
component: SimpleCmp, | ||
}, | ||
], | ||
withRouterConfig({ | ||
urlUpdateStrategy: 'deferred', | ||
canceledNavigationResolution: 'computed', | ||
}), | ||
), | ||
], | ||
}); | ||
const router = TestBed.inject(Router); | ||
await router.navigateByUrl('/first'); | ||
const location = TestBed.inject(Location); | ||
|
||
await router.navigateByUrl('/second'); | ||
expect(location.path()).toEqual('/unguarded'); | ||
expect(location.getState()).toEqual(jasmine.objectContaining({ɵrouterPageId: 2})); | ||
|
||
location.back(); | ||
await Promise.resolve(); | ||
expect(location.path()).toEqual('/first'); | ||
expect(location.getState()).toEqual(jasmine.objectContaining({ɵrouterPageId: 1})); | ||
}); | ||
}); | ||
}); | ||
|
||
@Component({selector: 'simple-cmp', template: `simple`, standalone: true}) | ||
class SimpleCmp {} |