【问题标题】:Angular 2 router event listenerAngular 2 路由器事件监听器
【发布时间】: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 不工作了。

我做错了什么?

【问题讨论】:

标签: javascript angular typescript angular2-routing


【解决方案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

另见

【讨论】:

  • 我需要在每节课中运行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://stackoverflow.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 错误。

            【讨论】:

              猜你喜欢
              • 2018-04-17
              • 2020-05-07
              • 2018-04-15
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2020-03-27
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多