Angular 2路由器事件监听器

74

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

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

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

因此,如果我在Angular 2中使用此事件监听器:

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

如果它不返回'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()不起作用。

我做错了什么?


可能是如何在Angular 2中检测路由更改?的重复问题。 - Narottam Goyal
9个回答

154

新路由器

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未定义

也请参见:


我需要在每个类中运行router.subscribe(...)吗?还是只需要运行一次?在文档中,我不明白在subscribe函数中应该输入哪些参数。你能给出一个完整的例子吗? - Yegor
你可以在全局服务中执行一次,然后将该服务注入到任何需要访问的地方。 - Günter Zöchbauer
如果我将其作为服务注入到其他组件中,它不会返回任何数据。 - Yegor
不要在组件上使用(providers: [SubscribeService]),而是只将SubscribeService添加到bootstrap(AppComponent, [OtherProviders, SubscribeService])中,其他地方都不需要。 - Günter Zöchbauer
4
人们经常会找到旧的示例然后遇到问题,因为代码不再起作用了。我认为对于他们来说,看到这是因为Angular已经更改,而该示例适用于较早版本的Angular,这是有帮助的。没有其他方法可以学习这一点,因为旧文档已不可用。 - Günter Zöchbauer
显示剩余5条评论

12

你还可以使用filter()来过滤事件。

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

一个更好的解决方案是为filter()添加一个“类型保护”,像这样:

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

它包含两个东西:

  • e是NavigationEnd这是你正在为其定义函数的断言(这是typescript语法,并且完全被剥离了transpiled javascript)
  • e instanceof NavigationEnd这是实际运行时检查类型的代码

好处是,像下面的map操作符之类的“管道”更深层次的操作现在知道类型是NavigationEnd,但如果没有类型保护,你会有一个Event类型。

如果您只需要检查一个事件类型,则这是最干净的方法。这也似乎是在严格模式下避免编译器错误所必需的。

enter image description here


6
您可以像@GünterZöchbauer所回答的那样使用instanceof
this.router.events.subscribe(event => {
  if(event instanceof NavigationStart) {
    // do something...
  }
}

您可以采用更为简单的方法,但请记住构造函数名称可以轻易更改,而函数仍将继续工作!

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

12
你不想使用instanceof的考虑是什么? - David Walschots
5
这种方法对代码压缩敏感,因为构造函数的名称可能会被修改,例如 event.constructor.name 可能变为 'A' - Zak Henry
1
这是一种非常不安全的方法,应该严格禁止使用。 - Bryan Rayner

4
直接从文档中摘录:
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 错误。


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

0
在Angular2中,前往文件“app.modules.ts”->导入。
RouterModule.forRoot(
      appRoutes,
      { 
         enableTracing: true
      }
)

在 enableTracing 为 true 时,在控制台中显示 routeEvents。 在 enableTracing 为 false 时,隐藏控制台中的 routeEvents。


0

Angular 2路由器事件有不同的类,从router.events可观察对象传递到订阅的内容可以是NavigationEndNavigationCancelNavigationErrorNavigationStart。实际触发路由更新的将是NavigationEnd

我建议避免使用instanceofevent.constructor.name,因为在缩小后,类名会被破坏,这样将无法正常工作。

您可以使用路由器的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
  }
}

0

使用@bespunky/angular-zen,这变得更加简单了...

基本上,扩展RouteAware类并创建一个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}`);
    }
}

看一下这个答案: https://stackoverflow.com/a/64864103/4371525


说得好。改进了我的答案。谢谢你。 - Shy Agam

-1

为了监听所有状态变化,请扩展默认的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!');
  }
}

从这里复制“自定义路由出口”的示例:https://auth0.com/blog/2016/01/25/angular-2-series-part-4-component-router-in-depth/


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接