Skip to content

Commit

Permalink
fix(router): preserve replaceUrl when returning a urlTree from CanAct…
Browse files Browse the repository at this point in the history
…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
blathers16 authored and dylhunn committed Mar 27, 2024
1 parent f3b6245 commit 60f1d68
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 0 deletions.
1 change: 1 addition & 0 deletions packages/router/src/router.ts
Expand Up @@ -234,6 +234,7 @@ export class Router {
// button, URL bar, etc). We want to replace that item in history
// if the navigation is rejected.
replaceUrl:
currentTransition.extras.replaceUrl ||
this.urlUpdateStrategy === 'eager' ||
isBrowserTriggeredNavigation(currentTransition.source),
// allow developer to override default options with RedirectCommand
Expand Down
103 changes: 103 additions & 0 deletions packages/router/test/router_navigation_extras.spec.ts
@@ -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 {}

0 comments on commit 60f1d68

Please sign in to comment.