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 传递给订阅的可以是 NavigationEnd
、NavigationCancel
、NavigationError
或 NavigationStart
。真正触发路由更新的是NavigationEnd
。
我会远离使用instanceof
或event.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 => 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<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://***.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 路由器事件监听器的主要内容,如果未能解决你的问题,请参考以下文章