Angular 2 路由器事件监听器

Posted

技术标签:

【中文标题】Angular 2 路由器事件监听器【英文标题】:Angular 2 router event listener 【发布时间】:2016-06-25 02:06:01 【问题描述】:

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

在 Angular 1.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() in service 不工作了。

我做错了什么?

【问题讨论】:

How to detect a route change in Angular 2?的可能重复 【参考方案1】:

新路由器

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

注入Router并订阅路由变化事件

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

另见

https://angular.io/docs/ts/latest/api/router/index/Router-class.html How to detect a route change in Angular 2?

【讨论】:

我需要在每节课中运行router.subscribe(...) 还是可以运行一次?在文档中我不明白我必须在订阅功能中输入哪些参数?你能写完整的例子吗? 您可以在全局服务中执行一次,然后将服务注入您想要访问的任何位置。 如果我将它作为服务注入到其他组件中,它不会返回任何数据 您能否编辑您的问题并添加代码来演示您尝试完成的工作?我不知道您将服务注入到哪里或如何进行。 @LazarLjubenović 人们经常发现旧示例然后遇到问题,因为代码不再工作。我认为对他们来说,看到这是因为 Angular 发生了变化,并且该示例适用于较旧的 Angular 版本,这很有帮助。没有其他方法可以了解这一点,因为旧文档不再可用。【参考方案2】:

要监听所有状态变化,扩展默认 RouterOutlet 并在“激活”和“停用”处理程序中添加您自己的逻辑。

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/

【讨论】:

【参考方案3】:

您可以使用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...
  
);

【讨论】:

不想使用instanceof的考虑是什么? 这种方法也很脆弱,因为构造函数名称可以更改,例如event.constructor.name可能是'A' 这是一种非常不安全的方法,应该强烈反对。【参考方案4】:

angular 2 路由器事件有不同的类,从 router.events observable 传递给订阅的可以是 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
  

【讨论】:

【参考方案5】:

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

RouterModule.forRoot(
      appRoutes,
       
         enableTracing: true
      
)

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

【讨论】:

【参考方案6】:

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

但不要使用filter(e =&gt; e is NavigationEnd)

更好的解决方案是向filter() 添加一个“类型保护”,如下所示:

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

它包含两件事:

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

这样做的好处是,在“管道”下方的操作员,例如下面的 map 现在知道类型是 NavigationEnd,但如果没有类型保护,您将有一个类型 Event

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

【讨论】:

【参考方案7】:
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)
    
  );

【讨论】:

【参考方案8】:

有了@bespunky/angular-zen,这变得简单多了……

基本上,扩展RouteAware 类并创建on&lt;EventType&gt;() 方法:

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://***.com/a/64864103/4371525

【讨论】:

说得好。改进了我的答案。谢谢。【参考方案9】:

直接来自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 错误。

【讨论】:

以上是关于Angular 2 路由器事件监听器的主要内容,如果未能解决你的问题,请参考以下文章

如何在Angular项目里监听页面关闭、跳转事件?

Angular 被动事件监听器

angular run()运行块

如何在 Angular 1.5 组件中监听范围事件?

angular run()运行块

laravel 事件 & 监听