[angular] How to detect a route change in Angular?

I am looking to detect a route change in my AppComponent.

Thereafter I will check the global user token to see if he is logged in. Then I can redirect the user if he is not logged in.

This question is related to angular

The answer is


In the component, you might want to try this:

import {NavigationEnd, NavigationStart, Router} from '@angular/router';

constructor(private router: Router) {
router.events.subscribe(
        (event) => {
            if (event instanceof NavigationStart)
                // start loading pages
            if (event instanceof NavigationEnd) {
                // end of loading paegs
            }
        });
}

above most of solutions correct , but i am facing issue this emit multiple times 'Navigation emit' event.when i was change any route this event is triggered. So hear is the complete solution for Angular 6.

import { Subscription } from 'rxjs/Subscription';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/filter';    

export class FooComponent implements OnInit, OnDestroy {
   private _routerSub = Subscription.EMPTY;
   constructor(private router: Router){}

   ngOnInit(){
     this._routerSub = this.router.events
      .filter(event => event instanceof NavigationEnd)
      .subscribe((value) => {
         //do something with the value
     });
  }

  ngOnDestroy(){
   this._routerSub.unsubscribe();
  }
} 

I am working with angular5 application and i'm facing the same issue . when i go through Angular Documentation they provide best solution for handling router events.check following documentation.

Represents an event triggered when a navigation ends successfully

How to use this ?

import { Component, OnInit } from '@angular/core';
import { Router, ActivatedRouteSnapshot, NavigationEnd } from '@angular/router';
@Component({
    selector: 'app-navbar',
    templateUrl: './navbar.component.html',
    styleUrls: ['./navbar.component.css']
})
export class NavbarComponent implements OnInit {
    constructor(private router: Router) { }
    ngOnInit(): void {
        //calls this method when navigation ends
        this.router.events.subscribe(event => {
            if (event instanceof NavigationEnd) {
                //calls this stuff when navigation ends
                console.log("Event generated");
            }
        });
    }
}

When to use this ?

In my case my application share common dashboard for all users such as users , Admins , but i need to show and hides some navbar options as per user types.

That's why whenever url changes i need to call service method which returns logged in user information as per response i will go for further operations.


Angular 7, if you want to subscribe to router

import { Router, NavigationEnd } from '@angular/router';

import { filter } from 'rxjs/operators';

constructor(
  private router: Router
) {
  router.events.pipe(
    filter(event => event instanceof NavigationEnd)  
  ).subscribe((event: NavigationEnd) => {
    console.log(event.url);
  });
}

The cleaner way to do this would be to inherit RouteAware and implement the onNavigationEnd() method.

It's part of a library called @bespunky/angular-zen.

  1. npm install @bespunky/angular-zen

  2. Make your AppComponent extend RouteAware and add an onNavigationEnd() method.

import { Component     } from '@angular/core';
import { NavigationEnd } from '@angular/router';
import { RouteAware    } from '@bespunky/angular-zen/router-x';

@Component({
    selector   : 'app-root',
    templateUrl: './app.component.html',
    styleUrls  : ['./app.component.css']
})
export class AppComponent extends RouteAware
{    
    protected onNavigationEnd(event: NavigationEnd): void
    {
        // Handle authentication...
    }
}

RouteAware has other benefits such as:
? Any router event can have a handler method (Angular's supported router events).
? Use this.router to access the router
? Use this.route to access the activated route
? Use this.componentBus to access the RouterOutletComponentBus service


RxJS 6

router.events.pipe(filter(event => event instanceof NavigationStart))

Thanks to Peilonrayz (see comments below)

new router >= RC.3

import { Router, NavigationStart, NavigationEnd, NavigationError, NavigationCancel, RoutesRecognized } from '@angular/router';

constructor(router:Router) {
  router.events.forEach((event) => {
    if(event instanceof NavigationStart) {
    }
    // NavigationEnd
    // NavigationCancel
    // NavigationError
    // RoutesRecognized
  });
}

You can also filter by the given event:

import 'rxjs/add/operator/filter';

constructor(router:Router) {
  router.events
    .filter(event => event instanceof NavigationStart)
    .subscribe((event:NavigationStart) => {
      // You only receive NavigationStart events
    });
}

Using the pairwise operator to get the previous and current event also is an nice idea. https://github.com/angular/angular/issues/11268#issuecomment-244601977

import 'rxjs/add/operator/pairwise';
import { Router } from '@angular/router';

export class AppComponent {
    constructor(private router: Router) {
        this.router.events.pairwise().subscribe((event) => {
            console.log(event);
        });
    };
}

I do it like this since RC 5

this.router.events
  .map( event => event instanceof NavigationStart )
  .subscribe( () => {
    // TODO
  } );

The following KIND of works and may do the tricky for you.

// in constructor of your app.ts with router and auth services injected
router.subscribe(path => {
    if (!authService.isAuthorised(path)) //whatever your auth service needs
        router.navigate(['/Login']);
    });

Unfortunately this redirects later in the routing process than I'd like. The onActivate() of the original target component is called before the redirect.

There is a @CanActivate decorator you can use on the target component but this is a) not centralised and b) does not benefit from injected services.

It would be great if anyone can suggest a better way of centrally authorising a route before it is committed. I'm sure there must be a better way.

This is my current code (How would I change it to listen to the route change?):

import {Component, View, bootstrap, bind, provide} from 'angular2/angular2';
import {ROUTER_BINDINGS, RouterOutlet, RouteConfig, RouterLink, ROUTER_PROVIDERS, APP_BASE_HREF} from 'angular2/router';    
import {Location, LocationStrategy, HashLocationStrategy} from 'angular2/router';

import { Todo } from './components/todo/todo';
import { About } from './components/about/about';

@Component({
    selector: 'app'
})

@View({
    template: `
        <div class="container">
            <nav>
                <ul>
                    <li><a [router-link]="['/Home']">Todo</a></li>
                    <li><a [router-link]="['/About']">About</a></li>
                </ul>
            </nav>
            <router-outlet></router-outlet>
        </div>
    `,
    directives: [RouterOutlet, RouterLink]
})

@RouteConfig([
    { path: '/', redirectTo: '/home' },
    { path: '/home', component: Todo, as: 'Home' },
    { path: '/about', component: About, as: 'About' }
])

class AppComponent {    
    constructor(location: Location){
        location.go('/');
    }    
}    
bootstrap(AppComponent, [ROUTER_PROVIDERS, provide(APP_BASE_HREF, {useValue: '/'})]);

For Angular 7 someone should write like:

this.router.events.subscribe((event: Event) => {})


A detailed example can be as follows:

import { Component } from '@angular/core'; 
import { Router, Event, NavigationStart, NavigationEnd, NavigationError } from '@angular/router';

@Component({
    selector: 'app-root',
    template: `<router-outlet></router-outlet>`
})
export class AppComponent {

    constructor(private router: Router) {

        this.router.events.subscribe((event: Event) => {
            if (event instanceof NavigationStart) {
                // Show loading indicator
            }

            if (event instanceof NavigationEnd) {
                // Hide loading indicator
            }

            if (event instanceof NavigationError) {
                // Hide loading indicator

                // Present error to user
                console.log(event.error);
            }
        });

   }
}

@Ludohen answer is great, but in case you don't want to use instanceof use the following

this.router.events.subscribe(event => {
  if(event.constructor.name === "NavigationStart") {
    // do something...
  }
});

with this way you can check the current event name as a string and if the event occurred you can do what you planned your function to do.


Angular 4.x and above :

This can be achieved using url property of ActivatedRoute class as below,

this.activatedRoute.url.subscribe(url =>{
     console.log(url);
});

Note: That you need to import and inject the provider from angular/router package

import { ActivatedRoute } from '@angular/router`

and

constructor(private activatedRoute : ActivatedRoute){  }

Capture route change events in the following manner...

import { Component, OnInit, Output, ViewChild } from "@angular/core";
import { Router, NavigationStart, NavigationEnd, Event as NavigationEvent } from '@angular/router';

@Component({
    selector: "my-app",
    templateUrl: "app/app.component.html",
    styleUrls: ["app/app.component.css"]
})
export class AppComponent {

    constructor(private cacheComponentObj: CacheComponent,
        private router: Router) {

        /*  Route event types
            NavigationEnd
            NavigationCancel
            NavigationError
            RoutesRecognized
        */
        router.events.forEach((event: NavigationEvent) => {

            //Before Navigation
            if (event instanceof NavigationStart) {
                switch (event.url) {
                case "/app/home":
                {
                    //Do Work
                    break;
                }
                case "/app/About":
                {
                    //Do Work
                    break;
                }
                }
            }

            //After Navigation
            if (event instanceof NavigationEnd) {
                switch (event.url) {
                case "/app/home":
                {
                    //Do Work
                    break;
                }
                case "/app/About":
                {
                    //Do Work
                    break;
                }
                }
            }
        });
    }
}

Angular 8. Check whether the current route is the base route.

  baseroute: boolean;
  constructor(
    private router: Router,
  ) {
    router.events.subscribe((val: any) => {
      if (val.url == "/") {
        this.baseroute = true;
      } else {
        this.baseroute = false;
      }
    });
  }

In Angular 10, you can do something like the following...

    import { Component, OnInit } from '@angular/core';
    import { Router, NavigationEnd } from '@angular/router';
    import { filter } from 'rxjs/operators';
    
    @Component({
      selector: 'app-my-class',
      templateUrl: './my-class.component.html',
      styleUrls: ['./my-class.component.scss']
    })
    export class MyClassComponent implements OnInit {
      constructor(private router: Router) {}
    
      ngOnInit(): void {
        this.router.events
        .pipe(filter(event => event instanceof NavigationEnd))  
        .subscribe((event: NavigationEnd) => {
          // code goes here...
        });
      }
    }

simple answer for Angular 8.*

constructor(private route:ActivatedRoute) {
  console.log(route);
}

The answers here are correct for router-deprecated. For the latest version of router:

this.router.changes.forEach(() => {
    // Do whatever in here
});

or

this.router.changes.subscribe(() => {
     // Do whatever in here
});

To see the difference between the two, please check out this SO question.

Edit

For the latest you must do:

this.router.events.subscribe(event: Event => {
    // Handle route change
});

In angular 6 and RxJS6:

import { filter, debounceTime } from 'rxjs/operators';

 this.router.events.pipe(
      filter((event) => event instanceof NavigationEnd),
      debounceTime(40000)
    ).subscribe(
      x => {
      console.log('val',x);
      this.router.navigate(['/']); /*Redirect to Home*/
}
)

In Angular 8 you should do like this.router.events.subscribe((event: Event) => {})

Example:

import { Component } from '@angular/core'; 
import { Router, Event } from '@angular/router';
import { NavigationStart, NavigationError, NavigationEnd } from '@angular/router';

@Component({
    selector: 'app-root',
    template: `<router-outlet></router-outlet>`
})
export class AppComponent {

    constructor(private router: Router) {
        //Router subscriber
        this.router.events.subscribe((event: Event) => {
            if (event instanceof NavigationStart) {
                //do something on start activity
            }

            if (event instanceof NavigationError) {
                // Handle error
                console.error(event.error);
            }

            if (event instanceof NavigationEnd) {
                //do something on end activity
            }
        });
   }
}

Just make changes on AppRoutingModule like

@NgModule({
imports: [RouterModule.forRoot(routes, { scrollPositionRestoration: 'enabled' })],
  exports: [RouterModule]
})

Location works...

import {Component, OnInit} from '@angular/core';
import {Location} from '@angular/common';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})

export class AppComponent implements OnInit {

    constructor(private location: Location) {
        this.location.onUrlChange(x => this.urlChange(x));
    }

    ngOnInit(): void {}

    urlChange(x) {
        console.log(x);
    }
}

I would write something like this:

ngOnInit() {
this.routed = this.router.events.map( event => event instanceof NavigationStart )
  .subscribe(() => {
  } );
}

ngOnDestroy() {
this.routed.unsubscribe();
}

Router 3.0.0-beta.2 should be

this.router.events.subscribe(path => {
  console.log('path = ', path);
});