Angular 2 路由器事件侦听器

Angular 2 router event listener

如何在 Angular 2 路由器中监听状态变化?

在Angular1.x我使用了这个事件:

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

所以,如果我在 Angular 2:

中使用这个事件监听器
window.addEventListener("hashchange", () => {return console.log('ok')}, false);

不是return'ok',然后从JS改变状态,只有浏览器history.back()函数运行.

使用 router.subscribe() 函数作为服务:

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');
        })
    }
}

在路由中初始化的组件中注入服务:

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) {}
}

我将此服务注入到其他组件中,例如本例中的组件。然后我改变状态,console.info() in service not working.

我做错了什么?

新路由器

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

注入路由器并订阅路由更改事件

import {Router} from 'angular2/router';

class MyComponent {
  constructor(router:Router) {
    router.subscribe(...)
  }
}

注意

对于新路由器,不要忘记从 router 模块导入 NavigationStart

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

因为如果您不导入它 instanceof 将无法工作并且会出现错误 NavigationStart is not defined

另见

要监听所有状态更改,请扩展默认 RouterOutlet 并在 'activate' 和 'deactivate' 处理程序中添加您自己的逻辑。

import {Directive} from 'angular2/core';
import {Router, RouterOutlet, ComponentInstruction} from 'angular2/router';

@Directive({
  selector: 'router-outlet'
})

export class MyOwnRouterOutlet extends RouterOutlet {
  ...

  activate() {
    console.log('Hello from the new router outlet!');
  }
}

从此处的 'Custom Router Outlet' 示例复制而来:https://auth0.com/blog/2016/01/25/angular-2-series-part-4-component-router-in-depth/

您可以使用 instanceof 作为 @GünterZöchbauer 的回答

this.router.events.subscribe(event => {
  if(event instanceof NavigationStart) {
    // do something...
  }
}

或者您可以使用 lazier 方法,但请记住在函数仍在运行时可以轻松更改构造函数名称!

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

angular 2 个路由器事件有不同的 classes,从 router.events observable 传递给订阅的内容可以是 NavigationEndNavigationCancelNavigationErrorNavigationStart。实际触发路由更新的将是 NavigationEnd.

我会远离使用 instanceofevent.constructor.name 因为在 缩小 之后 class 名称将被破坏它无法正常工作.

您可以改用路由器的 isActive 功能,如此处所示 https://angular.io/docs/ts/latest/api/router/index/Router-class.html

this.routerEventSubscription = this._router.events.subscribe((event: any) => {
  if (this._router.isActive(events.url, false)) { 
    // true if the url route is active
  }
}

在 angular2 中,转到文件 "app.modules.ts"->imports

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

in enableTracing true 在控制台中显示 routeEvents 在 enableTracing false 中隐藏 routeEvents in console

您还可以使用 filter().

过滤事件

但不要只是使用filter(e => e is NavigationEnd)

更好的解决方案是将 'type guard' 添加到 filter(),如下所示:

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

它包含两件事:

  • e is NavigationEnd 这是您要为其定义函数的断言(这是打字稿语法,已完全从转译的 javascript 中删除)
  • e instanceof NavigationEnd 这是检查类型的实际运行时代码

这样做的好处是运算符进一步向下 'the pipe',例如下面的 map 现在知道类型是 NavigationEnd,但是如果没有类型保护,您将有一个输入 Event.

如果您只需要检查一种事件类型,那么这是最简洁的方法。这在严格模式下似乎也是必要的,以避免编译器错误。

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)
    }
  });
}

使用 @bespunky/angular-zen 这变得简单多了...

基本上,扩展 RouteAware class 并创建一个 on<EventType>() 方法:

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}`);
    }
}

看看这个答案:

直接来自 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)
  }
})

尽管文档给出了代码 filter((e: Event) 但我将其更改为 filter((e: any) 否则您会在 WebStorm 中遇到 linting 错误。