In Angular, how do you determine the active route?

317,118

Solution 1

With the new Angular router, you can add a [routerLinkActive]="['your-class-name']" attribute to all your links:

<a [routerLink]="['/home']" [routerLinkActive]="['is-active']">Home</a>

Or the simplified non-array format if only one class is needed:

<a [routerLink]="['/home']" [routerLinkActive]="'is-active'">Home</a>

Or an even simpler format if only one class is needed:

<a [routerLink]="['/home']" routerLinkActive="is-active">Home</a>

See the poorly documented routerLinkActive directive for more info. (I mostly figured this out via trial-and-error.)

UPDATE: Better documentation for the routerLinkActive directive can now be found here. (Thanks to @Victor Hugo Arango A. in the comments below.)

Solution 2

I've replied this in another question but I believe it might be relevant to this one as well. Here's a link to the original answer: Angular 2: How to determine active route with parameters?

I've been trying to set the active class without having to know exactly what's the current location (using the route name). The is the best solution I have got to so far is using the function isRouteActive available in the Router class.

router.isRouteActive(instruction): Boolean takes one parameter which is a route Instruction object and returns true or false whether that instruction holds true or not for the current route. You can generate a route Instruction by using Router's generate(linkParams: Array). LinkParams follows the exact same format as a value passed into a routerLink directive (e.g. router.isRouteActive(router.generate(['/User', { user: user.id }])) ).

This is how the RouteConfig could look like (I've tweaked it a bit to show the usage of params):

@RouteConfig([
  { path: '/', component: HomePage, name: 'Home' },
  { path: '/signin', component: SignInPage, name: 'SignIn' },
  { path: '/profile/:username/feed', component: FeedPage, name: 'ProfileFeed' },
])

And the View would look like this:

<li [class.active]="router.isRouteActive(router.generate(['/Home']))">
   <a [routerLink]="['/Home']">Home</a>
</li>
<li [class.active]="router.isRouteActive(router.generate(['/SignIn']))">
   <a [routerLink]="['/SignIn']">Sign In</a>
</li>
<li [class.active]="router.isRouteActive(router.generate(['/ProfileFeed', { username: user.username }]))">
    <a [routerLink]="['/ProfileFeed', { username: user.username }]">Feed</a>
</li>

This has been my preferred solution for the problem so far, it might be helpful for you as well.

Solution 3

Small improvement to @alex-correia-santos answer based on https://github.com/angular/angular/pull/6407#issuecomment-190179875

import {Router, RouteConfig, ROUTER_DIRECTIVES} from 'angular2/router';
// ...
export class App {
  constructor(private router: Router) {
  }

  // ...

  isActive(instruction: any[]): boolean {
    return this.router.isRouteActive(this.router.generate(instruction));
  }
} 

And use it like this:

<ul class="nav navbar-nav">
    <li [class.active]="isActive(['Home'])">
        <a [routerLink]="['Home']">Home</a>
    </li>
    <li [class.active]="isActive(['About'])">
        <a [routerLink]="['About']">About</a>
    </li>
</ul>

Solution 4

I solved a problem I encountered in this link and I find out that there is a simple solution for your question. You could use router-link-active instead in your styles.

@Component({
   styles: [`.router-link-active { background-color: red; }`]
})
export class NavComponent {
}

Solution 5

You can check the current route by injecting the Location object into your controller and checking the path(), like so:

class MyController {
    constructor(private location:Location) {}

    ...  location.path(); ...
}

You will have to make sure to import it first:

import {Location} from "angular2/router";

You can then use a regular expression to match against the path that's returned to see which route is active. Note that the Location class returns a normalized path regardless of which LocationStrategy you're using. So even if you're using the HashLocationStragegy the paths returned will still be of the form /foo/bar not #/foo/bar

Share:
317,118
Michael Oryl
Author by

Michael Oryl

I'm the Director of Web Development for a life insurance company. In my previous life I was the founder and editor-in-chief of MobileBurn.com. For work I focus mostly on JavaScript projects using Node.js, Angular, and Node. I also dabble in iOS/Swift when needed, though I claim no proficiency there. I used to spend a lot of time with Java/Groovy, but I don't work in that space any longer. For fun, I like working on Arduino and similar micro-controllers for robotics projects as well as generic "maker" stuff. #SOreadytohelp

Updated on January 02, 2021

Comments

  • Michael Oryl
    Michael Oryl over 3 years

    NOTE: There are many different answers here, and most have been valid at one time or another. The fact is that what works has changed a number of times as the Angular team has changed its Router. The Router 3.0 version that will eventually be the router in Angular breaks many of these solutions, but offers a very simple solution of its own. As of RC.3, the preferred solution is to use [routerLinkActive] as shown in this answer.

    In an Angular application (current in the 2.0.0-beta.0 release as I write this), how do you determine what the currently active route is?

    I'm working on an app that uses Bootstrap 4 and I need a way to mark navigation links/buttons as active when their associated component is being shown in a <router-output> tag.

    I realize that I could maintain the state myself when one of the buttons is clicked upon, but that wouldn't cover the case of having multiple paths into the same route (say a main navigation menu as well as a local menu in the main component).

    Any suggestions or links would be appreciated. Thanks.

  • Philip
    Philip over 8 years
    I think this solution is better than the accepted one since it uses Angular API's instead of regex matching the path. It still is a bit ugly though, it would nice to just be able to do router.isRouteActive('Home').
  • Alex Santos
    Alex Santos over 8 years
    @Philip I agree with you, it's a nice way to decouple it from the actual path, but it's not as pretty as I'd expect as well. I guess you could write a wrapper around it and make it much smaller which would improve readability.
  • Gabu
    Gabu about 8 years
    I believe this should be the accepted answer. Angular2's router will add this automatically for you.
  • laifjei
    laifjei about 8 years
    unfortunately, that class add on <a>, not on <li>
  • Nikita Vlasenko
    Nikita Vlasenko about 8 years
    I faced some issues upon trying to implement the solution, so maybe for some can be useful: stackoverflow.com/questions/35882998/…
  • Nikita Vlasenko
    Nikita Vlasenko about 8 years
    should not forget to put Router in class's constructor
  • ComFreek
    ComFreek about 8 years
    In the latest Angular 2 version I used name instead of as in the router configuration object.
  • Shri
    Shri about 8 years
    unfortunately it doesn't really work nicely. i have one dynamically generated menu and router-link-active class is not being added to the active a or li. but there is one place i'm using bootstrap nav-tabs and it works there.
  • Pratik Kelwalkar
    Pratik Kelwalkar about 8 years
    and a style in the component styles : [.active { background-color: aliceblue; }]. +1 it works
  • tchelidze
    tchelidze almost 8 years
    in Angular2 RC, Router no longer has methods isRouteActive and generate.
  • František Žiačik
    František Žiačik almost 8 years
    This can be used in rc1 instead: router.urlTree.contains(router.createUrlTree(['Home']))
  • tchelidze
    tchelidze almost 8 years
    @FrantišekŽiačik does contains checks whether treeB is subtree of treeB ?
  • František Žiačik
    František Žiačik almost 8 years
    @tchelidze no, it doesn't, however if you inject RouteSegment too, you can generate full path like this router.createUrlTree(['Home'], this.routeSegment). Also, please note this anwer based on Günter Zöchbauer's stackoverflow.com/questions/36537878/… in which there is a another way to do this without poluting your template
  • tchelidze
    tchelidze almost 8 years
    @FrantišekŽiačik router.urlTree is not same as router.createUrlTree(['Home'], this.routeSegment), or what's diff ?
  • František Žiačik
    František Žiačik almost 8 years
    @tchelidze Can you pls rephrase the question, I don't follow what you're asking. To clarify, if you do this (I didn't try this though), router.urlTree.contains(router.createUrlTree(['Home', this.routeSegment])), it should work when Home is a not a root path, eg /parent-route/home.
  • tchelidze
    tchelidze almost 8 years
    @FrantišekŽiačik In linked answer, you keep track of currentUrl manually via ` this.currentUrl = this.router.createUrlTree(this.routerLink, this.routeSegment);. Question is, why is this necessary ? would not this.currentUrl` be same as router.urlTree.
  • František Žiačik
    František Žiačik almost 8 years
    @tchelidze Oh right, I didn't actually answer your original question. It is necessary because currentUrl is not a complete path when routeSegment is not used while urlTree, apparently, is, and contains doesn't check if the currentUrl is a subtree of urlTree (though I would expect the opposite looking at the method name). Didn't see the implementation of contains though, so it's just my guess. Also, if you check how RouterLink is implemented, it does exactly like that (actually it's extracted from there).
  • Ravinder Payal
    Ravinder Payal almost 8 years
    why @RouteConfig is not present in release candidate version
  • Kelvin Dealca
    Kelvin Dealca almost 8 years
    Great solution, with this you can make the router private.
  • Methodician
    Methodician almost 8 years
    This is a much better solution and is especially useful for a noob who forgets little things like creating the constructor and passing in the router! You totally deserve the check.
  • un33k
    un33k almost 8 years
    Please note, that you need to add the above directive to all your links. Then is-active will be added as a class to the active link only.
  • atsituab
    atsituab almost 8 years
    as soon as I import it I get an Cannot read property 'isSkipSelf' of null error. any ideas?
  • Matej
    Matej almost 8 years
    Is there any way for it to activate when the route's children are active? The code has an @input for options, but exact: false activates the class whenever the current route's siblings are also active.
  • Gab
    Gab almost 8 years
    @jessepinho Can you use this from router.navigate() ? If not, how can you use it with a router.navigate() ?
  • Kamil Kiełczewski
    Kamil Kiełczewski almost 8 years
    Nice idea but in angular2.0.0rc3 i change this.router.isRouteActive... to this._location.path() and put on top this: import { Location } from '@angular/common';
  • jessepinho
    jessepinho almost 8 years
    @GabrieleB-David I haven't tested it, but I would assume that router.navigate() would work just fine. routerLinkActive is simply an indicator of whether this link represents the active route.
  • Oblivion2000
    Oblivion2000 almost 8 years
    In the given example, [routerLinkActive] is added to the <a /> tag, however, it can also be put on other elements if the class is required elsewhere. <ul><li [routerLinkActive]="['active']"><a [routerLink]="[myRoute.Route]">
  • DenisKolodin
    DenisKolodin almost 8 years
    That's why I need a location name! )
  • Mickael Caruso
    Mickael Caruso almost 8 years
    @jessepinho - Tried it - [routerLinkActive]="'active'" will only work if you also have [routerLink] in the a-tag. If you had (click)="navitageTo('/route')" instead of [routerLink], and in that function, you called this.router.navigate(route), the new component will load but your active CSS class won't be applied. I'm trying do something else in that function besides this.router.navigate(..).
  • Michael Oryl
    Michael Oryl over 7 years
    What is the purpose of the exact option? Does that make it not match sub-paths like /home/whatever?
  • Artem Zinoviev
    Artem Zinoviev over 7 years
    yes, exact mean, that route will have same name. And this is required if you plan use it with tools like twitter bootstrap. It already handle active link state and without exact option will not working. (not sure about how does it work in core, i was try it and it working for me)
  • Øystein Amundsen
    Øystein Amundsen over 7 years
    There seems to be missing something from the code. Where is containsTree defined?
  • Shivang Gupta
    Shivang Gupta over 7 years
    .subscribe not available on Router instance.
  • user3728728
    user3728728 over 7 years
    This also can work routerLinkActive="active", just don't forget to use routerLink="/some_link" , i.e <li role="presentation" routerLinkActive="active"><a routerLink="/some_link">link</a></li>
  • S..
    S.. over 7 years
    @codeninja if the child route contains the parent route exactly then active class is applied to the parent. e.g going to 'articles/1' results in 'articles' link being active in my case.
  • Victor Hugo Arango A.
    Victor Hugo Arango A. over 7 years
    The example in the official documentation could help you: angular.io/docs/ts/latest/api/router/index/…
  • Rohit Parte
    Rohit Parte about 5 years
    Perfect solution for applying conditions on active routes.Where @angular/router or else didn't worked.
  • Yulian
    Yulian over 4 years
    Wow, this is so helpful. Thanks!
  • AzizStark
    AzizStark almost 4 years
    [routerLinkActiveOptions]="{exact: true}"
  • Tejashree
    Tejashree about 3 years
    You really saved my time. I am looking for this only... Thanks a lot! I am using ng-bootstrap:6.2.X and Angular: 9.1.14
  • Saurabh Gangamwar
    Saurabh Gangamwar over 2 years
    These methods are not present in angular 8
  • Christopher Smit
    Christopher Smit about 2 years
    isRouteActive and generate does not exists on Router