fix(router): redirect to root url when returned as UrlTree from guard (#28271)

When a UrlTree of root url was returned by a guard as a redirection, the
navigation was not processed. The issue came from the error handler which
incorrectly marked the router as already navigated.

Fixes #27845

PR Close #28271
This commit is contained in:
stoneLeaf 2019-01-21 10:30:32 +01:00 committed by Misko Hevery
parent 3de06dd794
commit 50732e1564
2 changed files with 61 additions and 6 deletions

View File

@ -715,9 +715,14 @@ export class Router {
/* This error type is issued during Redirect, and is handled as a cancellation
* rather than an error. */
if (isNavigationCancelingError(e)) {
this.navigated = true;
const redirecting = isUrlTree(e.url);
if (!redirecting) {
// Set property only if we're not redirecting. If we landed on a page and
// redirect to `/` route, the new navigation is going to see the `/` isn't
// a change from the default currentUrlTree and won't navigate. This is
// only applicable with initial navigation, so setting `navigated` only when
// not redirecting resolves this scenario.
this.navigated = true;
this.resetStateAndUrl(t.currentRouterState, t.currentUrlTree, t.rawUrl);
}
const navCancel =

View File

@ -2256,11 +2256,18 @@ describe('Integration', () => {
describe('should redirect when guard returns UrlTree', () => {
beforeEach(() => TestBed.configureTestingModule({
providers: [{
provide: 'returnUrlTree',
useFactory: (router: Router) => () => { return router.parseUrl('/redirected'); },
deps: [Router]
}]
providers: [
{
provide: 'returnUrlTree',
useFactory: (router: Router) => () => { return router.parseUrl('/redirected'); },
deps: [Router]
},
{
provide: 'returnRootUrlTree',
useFactory: (router: Router) => () => { return router.parseUrl('/'); },
deps: [Router]
}
]
}));
it('works', fakeAsync(inject([Router, Location], (router: Router, location: Location) => {
@ -2305,6 +2312,49 @@ describe('Integration', () => {
[NavigationEnd, '/redirected'],
]);
})));
it('works with root url',
fakeAsync(inject([Router, Location], (router: Router, location: Location) => {
const recordedEvents: any[] = [];
let cancelEvent: NavigationCancel = null !;
router.events.forEach((e: any) => {
recordedEvents.push(e);
if (e instanceof NavigationCancel) cancelEvent = e;
});
router.resetConfig([
{path: '', component: SimpleCmp},
{path: 'one', component: RouteCmp, canActivate: ['returnRootUrlTree']}
]);
const fixture = TestBed.createComponent(RootCmp);
router.navigateByUrl('/one');
advance(fixture);
expect(location.path()).toEqual('/');
expect(fixture.nativeElement).toHaveText('simple');
expect(cancelEvent && cancelEvent.reason)
.toBe('NavigationCancelingError: Redirecting to "/"');
expectEvents(recordedEvents, [
[NavigationStart, '/one'],
[RoutesRecognized, '/one'],
[GuardsCheckStart, '/one'],
[ChildActivationStart, undefined],
[ActivationStart, undefined],
[NavigationCancel, '/one'],
[NavigationStart, '/'],
[RoutesRecognized, '/'],
[GuardsCheckStart, '/'],
[ChildActivationStart, undefined],
[ActivationStart, undefined],
[GuardsCheckEnd, '/'],
[ResolveStart, '/'],
[ResolveEnd, '/'],
[ActivationEnd, undefined],
[ChildActivationEnd, undefined],
[NavigationEnd, '/'],
]);
})));
});
describe('runGuardsAndResolvers', () => {