Angular 2 router event listener

Viewed 117649

How to listen state change in Angular 2 router?

In Angular 1.x I used this event:

$rootScope.$on('$stateChangeStart',
    function(event,toState,toParams,fromState,fromParams, options){ ... })

So, if I use this eventlistener in Angular 2:

window.addEventListener("hashchange", () => {return console.log('ok')}, false);

it isn't return 'ok', then change state from JS, only then browser history.back() function run.

Use router.subscribe() function as the service:

import {Injectable} from 'angular2/core';
import {Router} from 'angular2/router';

@Injectable()
export class SubscribeService {
    constructor (private _router: Router) {
        this._router.subscribe(val => {
            console.info(val, '<-- subscribe func');
        })
    }
}

Inject service in component which init in routing:

import {Component} from 'angular2/core';
import {Router} from 'angular2/router';

@Component({
    selector: 'main',
    templateUrl: '../templates/main.html',
    providers: [SubscribeService]
})
export class MainComponent {
    constructor (private subscribeService: SubscribeService) {}
}

I inject this service in other components such as in this example. Then I change state, console.info() in service not working.

What I do wrong?

9 Answers

You can also filter events with filter().

But don't just use filter(e => e is NavigationEnd)

A much better solution is to add a 'type guard' to filter() like this:

 filter((e): e is NavigationEnd => e instanceof NavigationEnd), 

It contains two things:

  • e is NavigationEnd this is the assertion you're defining a function for (this is typescript syntax and is completely stripped out of the transpiled javascript)
  • e instanceof NavigationEnd this is the actual runtime code that checks the type

The nice thing with this is that operators further down 'the pipe', like map below now know the type is NavigationEnd, but without the type-guard you'd have a type Event.

If you only need to check for one event type then this is the cleanest way to do so. This also appears to be necessary in strict mode to avoid compiler errors.

enter image description here

Straight from the docs

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

this.router.events.pipe(
  filter((e: any): e is RouterEvent => e instanceof RouterEvent)
).subscribe((evt: RouterEvent) => {
  if (evt instanceof NavigationEnd) {
    console.log(evt.url)
  }
})

Although the docs give the code filter((e: Event) but I changed this to filter((e: any) or you get linting errors in WebStorm.

import { Router,NavigationEnd  } from '@angular/router';
constructor(private route:Router){

  this.routeEvent(this.route);

}
routeEvent(router: Router){
  router.events.subscribe(e => {
    if(e instanceof NavigationEnd){
      console.log(e)
    }
  });
}

in angular2, go to file "app.modules.ts"->imports

RouterModule.forRoot(
      appRoutes,
      { 
         enableTracing: true
      }
)

in enableTracing true show routeEvents in console in enableTracing false hide routeEvents in console

With @bespunky/angular-zen this has become a lot simpler...

Basically, extend the RouteAware class and create an on<EventType>() method:

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

@Component({
    selector   : 'app-demo',
    templateUrl: './demo.component.html',
    styleUrls  : ['./demo.component.css']
})
export class DemoComponent extends RouteAware
{
    // ✨ Any router event can have a handler method.
    // See https://angular.io/guide/router#router-events for a complete list of angular's 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
    
    protected onNavigationStart(event: NavigationStart): void
    {
        console.log(`Navigation started for: ${event.url}`);
    }

    protected onRoutesRecognized(event: RoutesRecognized): void
    {
        console.log('Recognized routes.');
    }
    
    protected onNavigationEnd(event: NavigationEnd): void
    {
        console.log(`Navigation ended for: ${event.url}`);
    }
}

Take a look at this answer: https://stackoverflow.com/a/64864103/4371525

Related