71

Let's presume I got this router config

export const EmployeeRoutes = [
   { path: 'sales', component: SalesComponent },
   { path: 'contacts', component: ContactsComponent }
];

and have navigated to the SalesComponent via this URL

/department/7/employees/45/sales

Now I'd like to go to contacts, but as I don't have all the parameters for an absolute route (e.g. the department ID, 7 in the above example) I'd prefer to get there using a relative link, e.g.

[routerLink]="['../contacts']"

or

this.router.navigate('../contacts')

which unfortunately doesn't work. There may be an obvious solution but I'm not seeing it. Can anyone help out here please?

Thorsten Westheider
  • 10,572
  • 14
  • 55
  • 97

4 Answers4

126

If you are using the new router (3.0.0-beta2), you can use the ActivatedRoute to navigate to relative path as follow:

constructor(private router: Router, private r:ActivatedRoute) {} 

///
// DOES NOT WORK SEE UPDATE
goToContact() {
  this.router.navigate(["../contacts"], { relativeTo: this.r });
}

Update 08/02/2019 Angular 7.1.0

current route: /department/7/employees/45/sales

the old version will do: /department/7/employees/45/sales/contacts

As per @KCarnaille's comment the above does not work with the latest Router. The new way is to add .parent to this.r so

    // Working(08/02/2019) 
    // DOES NOT WORK SEE UPDATE
    goToContact() {
       this.router.navigate(["../contacts"], { relativeTo: this.r.parent });
    }

the update will do: /department/7/employees/45/contacts

Update 12/12/2021 Angular > 10

As @ziz194 mention it, this is how it now works.

constructor(private router: Router, private r:ActivatedRoute) {} 

goToContact() {
  this.router.navigate(["contacts"], { relativeTo: this.r });
}
Raphaël Balet
  • 6,334
  • 6
  • 41
  • 78
Harry Ninh
  • 16,288
  • 5
  • 60
  • 54
  • 1
    I'm actually in the same situation, but it doesn't work for me... something has changed since the official release ? – KCarnaille Mar 01 '17 at 16:44
  • @KCarnaille Hmm I'm on @angular/router 3.0.0 (finished, not beta) and it's still working properly. Not sure if they have any breaking changes since then. – Harry Ninh Mar 01 '17 at 23:10
  • @Harry Ninh It was my fault actually. A simple issue with path structure :) It's working – KCarnaille Mar 02 '17 at 08:52
  • 4
    How can we do that from html with `routerLink` ? – maxime1992 May 11 '17 at 07:42
  • Great! I have been baffled by this for about an hour. – Paul Johnson Mar 28 '19 at 16:09
  • 3
    This actually shouldn't be solved by `.parent` just remove the ../ and keep just "contacts" in the path, like this: `this.router.navigate(["contacts"], { relativeTo: this.r});` – ziz194 Jul 11 '19 at 08:27
  • This is good info. Shame angular don't include USEFUL information like this in their docs, like all will every need is a heroes app. How complex can you make routing? You have a tree, absolute and relative paths, arrgh. – godhar Aug 01 '19 at 20:49
  • 1
    I really dont understand this logic. So RELATIVE to the parent route, we navigate back one level (grandparent), and move forward to contacts. This should give us /department/7/employees/contacts – Craig Oct 02 '19 at 15:53
89

The RouterLink directive always treats the provided link as a delta to the current URL:

[routerLink]="['/absolute']"
[routerLink]="['../../parent']"
[routerLink]="['../sibling']"
[routerLink]="['./child']"     // or
[routerLink]="['child']" 

// with route param     ../sibling;abc=xyz
[routerLink]="['../sibling', {abc: 'xyz'}]"
// with query param and fragment   ../sibling?p1=value1&p2=v2#frag
[routerLink]="['../sibling']" [queryParams]="{p1: 'value', p2: 'v2'}" fragment="frag"

The navigate() method requires a starting point (i.e., the relativeTo parameter). If none is provided, the navigation is absolute:

constructor(private router: Router, private route: ActivatedRoute) {}

this.router.navigate(["/absolute/path"]);
this.router.navigate(["../../parent"], {relativeTo: this.route});
this.router.navigate(["../sibling"],   {relativeTo: this.route});
this.router.navigate(["./child"],      {relativeTo: this.route}); // or
this.router.navigate(["child"],        {relativeTo: this.route});

// with route param     ../sibling;abc=xyz
this.router.navigate(["../sibling", {abc: 'xyz'}], {relativeTo: this.route});
// with query param and fragment   ../sibling?p1=value1&p2=v2#frag
this.router.navigate(["../sibling"], {relativeTo: this.route, 
    queryParams: {p1: 'value', p2: 'v2'}, fragment: 'frag'});

// RC.5+: navigate without updating the URL 
this.router.navigate(["../sibling"], {relativeTo: this.route, skipLocationChange: true});
Kael
  • 858
  • 9
  • 21
Mark Rajcok
  • 362,217
  • 114
  • 495
  • 492
  • 2
    Why does navigate behave differently? – Kugel Nov 10 '17 at 03:07
  • 4
    do you have a working example of the [routerLink] directive working with sibling route? It keeps switching to an absolute route for me if I use [routerLink]="['../sibling']" – Jonathan002 Nov 06 '18 at 03:30
  • router.navigate() requires the first argument to be an array - but none of these work even as an array – inorganik Mar 09 '19 at 00:51
  • @Jonathan002, same for me. Did you find anything that works? – Marian Simonca Nov 12 '19 at 20:19
  • 5
    This answer isn't completely accurate. A path `../something` is *relativeTo* where the **component** is rendered in the router tree, but it is not relative to the **current route** because that could be a route deeper in the route tree from where the `[routerLink]` was applied. If you're trying to do a breadcrumb as an example in the header of your page. It will be relative to where that header is rendered, and most likely not what you want. – Reactgular Apr 05 '20 at 11:07
  • 1
    @Reactgular Then what's the best way to do breadcrumbs? Use `router.createUrlLink` to create an absolute link based on the **currentRoute** somewhere and use that inside the router link? – macwier Apr 07 '20 at 06:49
  • @Botis I updated my breadcrumbs by listening for changes to `router.events` and `NavigationEnd`, then I recursive walk `router.routerState.snapshot.root` and rebuild the breadcrumb trail from there. I check for `data` attached to route children to see if I should create a breadcrumb for that route, and then I convert `activatedRouteSnapshot.pathFromRoot` into a string as the URL for that crumb. Yes, it's very complicated. Sadly, couldn't find a better way. – Reactgular Apr 07 '20 at 10:42
  • @Reactgular the answer is in fact accurate. "../something" is a relative path, it will be relative to something. In the case of the [routerLink] directive, if [relativeTo] is not specified, it will be relative to the activated route of the host scope (which has always been the intended behavior). Paths in a breadcrumb should always be absolute. – Jandro Rojas Feb 24 '22 at 22:31
  • "The RouterLink directive always treats the provided link as a delta to the current URL" - That's not true. It's relative to the activated route of the component you are in, which is either the current url or a subset of it. (it it has more active children.) – Shachar Har-Shuv Aug 25 '22 at 15:18
0

I tried the following and it worked:

this.router.navigate(["contacts"], { relativeTo: this.r.parent });

kellermat
  • 2,859
  • 2
  • 4
  • 21
0

For some reason, possibly related to multiple outlet I'm having in the same view, I either had to target this.activatedRoute.parent.parent (very inconvenient) or to target the current view and use the double dot path.

    this.router.navigate(['..', "contacts"], { relativeTo: this.activatedRoute });
Flavien Volken
  • 19,196
  • 12
  • 100
  • 133