angular: bug: relative navigation doesn't work inside route guards

I’m submitting a…


[x] Bug report  

Current behavior

Inside a route guard, injecting ActivatedRoute and attempting relative navigation via this.router.navigate(['../sibling'], {relativeTo: this.route}) results in a Error: Cannot match any routes. URL Segment: 'sibling'

Expected behavior

Relative navigation should work normally

Minimal reproduction of the problem with instructions

A stackblitz example of the issue can be seen here: https://stackblitz.com/edit/angular-gitter-k7fjds

What is the motivation / use case for changing the behavior?

  1. This appears to be a bug
  2. If a user navigates to a child route and includes a specific queryParam, I’d like to redirect the user to a sibling route. Currently, this does not appear to be possible inside a route guard with relative navigation.

Additional Information

It appears that the ActivatedRoute injected into a route guard is always an instance of the root route. Inside a route guard, this.router.navigate(['.'], {relativeTo: this.route}) always takes the user back to the root route, rather than navigating them to the same route they are currently on.

Environment


Angular version: 5.0.0


Browser:
- [x] Chrome (desktop) version 64

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Reactions: 42
  • Comments: 15 (8 by maintainers)

Commits related to this issue

Most upvoted comments

Any update on this issue?

Still having this Issue in Angular 9 … It’s a pity, that there is no priority in fixing this.

I wrote this small utility function that I use in my guard

private parseUrl(url: string, redirectTo: string) {
	const urlTokens = url.split('/');
	const redirectToTokens = redirectTo.split('/');

	let token = redirectToTokens.shift();

	while (token) {
		if (token !== '.' && token !== '..') {
			redirectToTokens.unshift(token);
			break;
		}

		if (token === '..') {
			urlTokens.pop();
		}

		token = redirectToTokens.shift();
	}

	urlTokens.push(...redirectToTokens);

	return urlTokens.join('/');
}

You can call it the following ways

parseUrl('/foo/bar/baz', './qux');
//=> /foo/bar/baz/qux

parseUrl('/foo/bar/baz', '../qux');
//=> /foo/bar/qux

parseUrl('/foo/bar/baz', '../../qux');
//=> /foo/qux

parseUrl('/foo/bar/baz', '..');
//=> /foo/bar

The first argument that I pass in is state.url because that’s the route I want to go to. The returned URL can then be used with this.router.navigateByUrl(parseUrl(state.url, '../sibling'));.

I might be missing some edge cases but it works for me, so thought I could share it.

The TL;DR for this one is that this is something I would absolutely love to do but will be a breaking change to update router.createUrlTree to do it.

The implementation for creating a URL tree is really confusing and actually requires that the ActivatedRoute/ActivatedRouteSnapshot be part of the current router url. In reality, we should really be able to create a url from any ActivatedRouteSnapshot, but that requires the shape of the ActivatedRoute/ActivatedRouteSnapshot to be valid (which is commonly not the case in tests due to stubbing this out).

There is maybe an opportunity to introduce this new way of creating the URL as a standalone function exported as public API in the router and then later making the breaking change to include it in router.createUrlTree. This would be problematic for discovery and documentation but would at least provide an early opt-in for anyone trying to do this.

I am facing the same issue relative routing does not working in guard services? Guys did you get any fix

It really is a pity that this is so hard. Another workaround was mentioned in #28682, but this also doesn’t seem to work anymore with Angular 9:

return (this.router.parseUrl(`${state.url}/../unknown`));

For me the “expected” solution would be to accept a RouterStateSnapshot in Router.createUrlTree. From what I can tell snapshot property is the only thing from the route that is used anyway: https://github.com/angular/angular/blob/5edeee69dd0a7ffff0790d6f817c95f58914996d/packages/router/src/create_url_tree.ts#L145-L158

Called from https://github.com/angular/angular/blob/5edeee69dd0a7ffff0790d6f817c95f58914996d/packages/router/src/create_url_tree.ts#L27

Which in turn is called from https://github.com/angular/angular/blob/9.0.2/packages/router/src/router.ts#L951

@jasonaden (or anyone from the Angular team): This seems like a trivial fix, so I guess im am overlooking something?

The issue seems to be that the current ActivatedRoute is being injected and used (which makes sense, seeing as it is the currently activated route), and there isn’t a way to get what will become the active route in an ActivatedRoute format. (the next route is only provided as an ActivatedRouteSnapshot, which can’t be used for relative navigation).