Skip to content

bug: relative navigation doesn't work inside route guards #22763

Closed

Description

@jorroll

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

Activity

  1. added this to the needsTriage milestone on Mar 14, 2018
  2. modified the milestones: needsTriage, Backlog on Mar 16, 2018
  3. jasonaden commented on Mar 16, 2018

    @jasonaden
    Contributor

    Looks related to #17023

  4. jorroll commented on Mar 17, 2018

    @jorroll
    ContributorAuthor

    This issue appears to be more nuanced then I originally thought. Before posting, I had noticed that the injected ActivatedRoute appeared to be the root route, so I tried looping through the ActivatedRoute and then using the result for relative navigation, like so

    constructor(
      private router: Router,
      private route: ActivatedRoute,
    ) {}
    
    canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
      const emailAddress = next.queryParams['emailAddress'] as string | undefined;
    
      let route = this.route;
    
      while (route.firstChild) {route = route.firstChild}
    
      if (emailAddress) {
        this.router.navigate(['../sibling'], {relativeTo: route});
      }
    
      return true;
    }
    

    This didn't work however, so I created this issue.

    Just fooling around now though, I see this DOES work when navigating from the /parent/sibling route to the /parent/child route. It DOES NOT work when navigating from /parent to /parent/child.

  5. jorroll commented on Mar 17, 2018

    @jorroll
    ContributorAuthor

    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).

  6. SamVerschueren commented on Apr 6, 2018

    @SamVerschueren
    Contributor

    Having the same issue. I need to relatively route from what the route will be, not from the current ActivatedRoute. Didn't find a workaround yet except from fiddling with the raw url.

  7. jorroll commented on Apr 6, 2018

    @jorroll
    ContributorAuthor

    Ya, my current workaround is to fiddle with the raw url.

  8. SamVerschueren commented on Apr 9, 2018

    @SamVerschueren
    Contributor

    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.

  9. mantuonweb commented on Apr 26, 2019

    @mantuonweb

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

  10. jorroll commented on Apr 26, 2019

    @jorroll
    ContributorAuthor

    @mantuonweb you didn't actually read this thread did you?

  11. 23 remaining items

  12. atscott commented on Apr 22, 2022

    @atscott
    Contributor

    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.

  13. angular-automatic-lock-bot commented on Jun 24, 2022

    @angular-automatic-lock-bot

    This issue has been automatically locked due to inactivity.
    Please file a new issue if you are encountering a similar or related problem.

    Read more about our automatic conversation locking policy.

    This action has been performed automatically by a bot.

  14. locked and limited conversation to collaborators on Jun 24, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions