如何在Angular中检测路线变化?


427

我希望检测到我的路线更改AppComponent

此后,我将检查全局用户令牌以查看他是否已登录。然后,如果用户未登录,我可以重定向该用户。

Answers:


534

在Angular 2中,您可以subscribe(Rx事件)到Router实例。所以你可以做类似的事情

class MyClass {
  constructor(private router: Router) {
    router.subscribe((val) => /*whatever*/)
  }
}

编辑(自rc.1起)

class MyClass {
  constructor(private router: Router) {
    router.changes.subscribe((val) => /*whatever*/)
  }
}

编辑2(2.0.0起)

另请参阅:Router.events文档

class MyClass {
  constructor(private router: Router) {
    router.events.subscribe((val) => {
        // see also 
        console.log(val instanceof NavigationEnd) 
    });
  }
}

2
event._root.children[0].value._routeConfig.data希望能够有更好的方法来获取数据
Akshay

6
@Akshay您看过Todd Motto的这篇文章:[Angular 2中带有路由器事件的动态页面标题](toddmotto.com/dynamic-page-titles-angular-2-router-events
Bogac

9
为什么会发射3次?
工具包

2
@Toolkit是因为事件具有3个状态,并且成功更改了url。这3种状态分别是:“ NavigationStart”,“ NavigationEnd”和“ RoutesRecognized”
RicardoGonzales

10
您可以使用RxJS filter运算符轻松过滤事件。router.events.pipe(filter(e => e instanceof NavigationEnd).subscribe((e) => { ... }
Simon_Weaver

314

RxJS 6

router.events.pipe(filter(event => event instanceof NavigationStart))

感谢Peilonrayz(请参阅下面的评论)

新路由器> = RC.3

import { Router, NavigationStart, NavigationEnd, NavigationError, NavigationCancel, RoutesRecognized } from '@angular/router';

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

您还可以按给定事件进行过滤:

import 'rxjs/add/operator/filter';

constructor(router:Router) {
  router.events
    .filter(event => event instanceof NavigationStart)
    .subscribe((event:NavigationStart) => {
      // You only receive NavigationStart events
    });
}

使用pairwise运算符获取上一个和当前事件也是一个好主意。https://github.com/angular/angular/issues/11268#issuecomment-244601977

import 'rxjs/add/operator/pairwise';
import { Router } from '@angular/router;

export class AppComponent {
    constructor(private router: Router) {
        this.router.events.pairwise().subscribe((event) => {
            console.log(event);
        });
    };
}

1
@GunterZochbauer而不是'is'我将使用'instanceof'。“事件:事件”应放在括号中。感谢这个,非常强大的新功能!我喜欢
Maxim

2
这确实会在当前版本上引发编译错误Argument of type '(event: Event) => void' is not assignable to parameter of type
Rudi Strydom

1
@RudiStrydom&GünterZöchbauer- Argument of type '(event: Event) => void' is not assignable to parameter of type错误是因为您在过滤器代码段中预订的是Event类型的对象,而不是NavigationEvent。
Bonnici

1
第二个示例应该是NavigationEvent而不是Event。也不要忘记从@ angular / router导入“ Event as NavigationEvent”
Mick

1
有关导入的提示适用于任何希望解决此错误的人:)
Mick

91

对于Angular 7,有人应该这样写:

this.router.events.subscribe((event: Event) => {})


一个详细的示例可以如下所示:

import { Component } from '@angular/core'; 
import { Router, Event, NavigationStart, NavigationEnd, NavigationError } from '@angular/router';

@Component({
    selector: 'app-root',
    template: `<router-outlet></router-outlet>`
})
export class AppComponent {

    constructor(private router: Router) {

        this.router.events.subscribe((event: Event) => {
            if (event instanceof NavigationStart) {
                // Show loading indicator
            }

            if (event instanceof NavigationEnd) {
                // Hide loading indicator
            }

            if (event instanceof NavigationError) {
                // Hide loading indicator

                // Present error to user
                console.log(event.error);
            }
        });

   }
}

2
这很棒!很全面!对角7.完美工作
MaylorTaylor

1
通常,如果使用预加载策略,导航本身将花费很少的时间。在可用性方面,我只会在后端http请求上使用加载指示器,如果有的话。
菲尔(Phil)

5
构造函数中,您不应使用<this>,而是ngOnInit。
塞尔吉奥·里斯

1
完美,如何获取url的确切param.id?
ShibinRagh,

2
该解决方案不受组件的限制,它散布在整个应用程序中,消耗资源
M. Rafee

54

角7,如果你要subscriberouter

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

import { filter } from 'rxjs/operators';

constructor(
  private router: Router
) {
  router.events.pipe(
    filter(event => event instanceof NavigationEnd)  
  ).subscribe((event: NavigationEnd) => {
    console.log(event.url);
  });
}

2
它没有捕获重定向事件
Anandhu Ajayakumar,

40

Angular 4.x及更高版本:

这可以通过使用ActivatedRoute类的url属性如下实现,

this.activatedRoute.url.subscribe(url =>{
     console.log(url);
});

注意: 您需要从angular/router包中导入和注入提供程序

import { ActivatedRoute } from '@angular/router`

constructor(private activatedRoute : ActivatedRoute){  }

18

路由器3.0.0-beta.2应该是

this.router.events.subscribe(path => {
  console.log('path = ', path);
});

适用于当前路径,但以前如何?
tatsu

16

在angular 6和RxJS6中:

import { filter, debounceTime } from 'rxjs/operators';

 this.router.events.pipe(
      filter((event) => event instanceof NavigationEnd),
      debounceTime(40000)
    ).subscribe(
      x => {
      console.log('val',x);
      this.router.navigate(['/']); /*Redirect to Home*/
}
)

3
您错过了路由器的进口import {Router, NavigationEnd} from "@angular/router"
Damir Beylkhanov,

15

这里的答案是正确的router-deprecated。对于最新版本router

this.router.changes.forEach(() => {
    // Do whatever in here
});

要么

this.router.changes.subscribe(() => {
     // Do whatever in here
});

要了解两者之间的区别,请查看此SO问题

编辑

对于最新,您必须执行以下操作:

this.router.events.subscribe(event: Event => {
    // Handle route change
});

是否提供先前和当前路线的任何数据?
akn

router已经再次更新(我还没有更新我的答案了),所以我不知道是怎么回事了最新的。对于router我写的,你不能。@akn
Dehli

请为这个答案提供一些背景信息吗?您要用其他解决方案替换哪些行?
杰拉德·辛普森

12

在Angular 8中,您应该喜欢 this.router.events.subscribe((event: Event) => {})

例:

import { Component } from '@angular/core'; 
import { Router, Event } from '@angular/router';
import { NavigationStart, NavigationError, NavigationEnd } from '@angular/router';

@Component({
    selector: 'app-root',
    template: `<router-outlet></router-outlet>`
})
export class AppComponent {

    constructor(private router: Router) {
        //Router subscriber
        this.router.events.subscribe((event: Event) => {
            if (event instanceof NavigationStart) {
                //do something on start activity
            }

            if (event instanceof NavigationError) {
                // Handle error
                console.error(event.error);
            }

            if (event instanceof NavigationEnd) {
                //do something on end activity
            }
        });
   }
}

10

在组件中,您可能想要尝试以下操作:

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

constructor(private router: Router) {
router.events.subscribe(
        (event) => {
            if (event instanceof NavigationStart)
                // start loading pages
            if (event instanceof NavigationEnd) {
                // end of loading paegs
            }
        });
}

8

通过以下方式捕获路线更改事件:

import { Component, OnInit, Output, ViewChild } from "@angular/core";
import { Router, NavigationStart, NavigationEnd, Event as NavigationEvent } from '@angular/router';

@Component({
    selector: "my-app",
    templateUrl: "app/app.component.html",
    styleUrls: ["app/app.component.css"]
})
export class AppComponent {

    constructor(private cacheComponentObj: CacheComponent,
        private router: Router) {

        /*  Route event types
            NavigationEnd
            NavigationCancel
            NavigationError
            RoutesRecognized
        */
        router.events.forEach((event: NavigationEvent) => {

            //Before Navigation
            if (event instanceof NavigationStart) {
                switch (event.url) {
                case "/app/home":
                {
                    //Do Work
                    break;
                }
                case "/app/About":
                {
                    //Do Work
                    break;
                }
                }
            }

            //After Navigation
            if (event instanceof NavigationEnd) {
                switch (event.url) {
                case "/app/home":
                {
                    //Do Work
                    break;
                }
                case "/app/About":
                {
                    //Do Work
                    break;
                }
                }
            }
        });
    }
}

完美,如何获取url的确切param.id?
ShibinRagh

6

定位工程...

import {Component, OnInit} from '@angular/core';
import {Location} from '@angular/common';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})

export class AppComponent implements OnInit {

    constructor(private location: Location) {
        this.location.onUrlChange(x => this.urlChange(x));
    }

    ngOnInit(): void {}

    urlChange(x) {
        console.log(x);
    }
}

好答案。它适用于我的情况。谢谢
Umar Tariq

4

以上大多数解决方案都是正确的,但我遇到的问题是多次发出``导航发出''事件。当我更改任何路线时都会触发此事件。所以听说是Angular 6的完整解决方案。

import { Subscription } from 'rxjs/Subscription';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/filter';    

export class FooComponent implements OnInit, OnDestroy {
   private _routerSub = Subscription.EMPTY;
   constructor(private router: Router){}

   ngOnInit(){
     this._routerSub = this.router.events
      .filter(event => event instanceof NavigationEnd)
      .subscribe((value) => {
         //do something with the value
     });
  }

  ngOnDestroy(){
   this._routerSub.unsubscribe();
  }
} 

3

@Ludohen的答案很好,但是如果您不想使用,请instanceof使用以下内容

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

通过这种方式,您可以将当前事件名称检查为字符串,并且如果发生了事件,则可以执行计划函数的操作。


3
为什么不使用打字稿安全?
Pascal

@Pascal为什么讨厌?并且该Event类型导致Atom中出现错误,这就是为什么我没有使用它的原因
Khaled Al-Ansari

2
@Pascal不,这是一个Angular问题,因为router事件与浏览器事件不同,所以这就是Event类型不起作用的原因!他们需要为此事件创建一个新界面,我应该从一开始就说过,但无理的否决票无济于事:)
Khaled Al-Ansari

5
由于压缩是在生产代码中执行的,instanceOf因此您应该使用它,因此您的示例也将在生产代码中工作。if(event instanceOf NavigationStart) {
米兰·贾里克

1
应该是if(event instanceof NavigationStart)
Ketan

1

我正在使用angular5应用程序,并且遇到了相同的问题。当我浏览Angular文档时,它们为处理路由器事件提供了最佳解决方案。请查看以下文档。

表示导航成功结束时触发的事件

如何使用呢?

import { Component, OnInit } from '@angular/core';
import { Router, ActivatedRouteSnapshot, NavigationEnd } from '@angular/router';
@Component({
    selector: 'app-navbar',
    templateUrl: './navbar.component.html',
    styleUrls: ['./navbar.component.css']
})
export class NavbarComponent implements OnInit {
    constructor(private router: Router) { }
    ngOnInit(): void {
        //calls this method when navigation ends
        this.router.events.subscribe(event => {
            if (event instanceof NavigationEnd) {
                //calls this stuff when navigation ends
                console.log("Event generated");
            }
        });
    }
}

什么时候使用?

就我而言,我的应用程序为所有用户(例如users,Admins)共享通用仪表板,但是我需要根据用户类型显示和隐藏一些导航栏选项。

这就是为什么每当URL更改时我都需要调用service方法,该服务方法将根据响应返回登录的用户信息,我将进行进一步的操作。


0

以下种类的作品可能会为您带来麻烦。

// in constructor of your app.ts with router and auth services injected
router.subscribe(path => {
    if (!authService.isAuthorised(path)) //whatever your auth service needs
        router.navigate(['/Login']);
    });

不幸的是,这比我希望的在路由过程中的重定向晚。的onActivate()原来的目标组件被重定向之前调用。

有一个 @CanActivate你可以在目标组件上使用的装饰,但是这是一个)不集中和b)不从所注射的服务中受益。

如果有人能提出一种在提交之前集中授权路线的更好方法,那就太好了。我相信肯定有更好的方法。

这是我当前的代码(如何更改以侦听路线更改?):

import {Component, View, bootstrap, bind, provide} from 'angular2/angular2';
import {ROUTER_BINDINGS, RouterOutlet, RouteConfig, RouterLink, ROUTER_PROVIDERS, APP_BASE_HREF} from 'angular2/router';    
import {Location, LocationStrategy, HashLocationStrategy} from 'angular2/router';

import { Todo } from './components/todo/todo';
import { About } from './components/about/about';

@Component({
    selector: 'app'
})

@View({
    template: `
        <div class="container">
            <nav>
                <ul>
                    <li><a [router-link]="['/Home']">Todo</a></li>
                    <li><a [router-link]="['/About']">About</a></li>
                </ul>
            </nav>
            <router-outlet></router-outlet>
        </div>
    `,
    directives: [RouterOutlet, RouterLink]
})

@RouteConfig([
    { path: '/', redirectTo: '/home' },
    { path: '/home', component: Todo, as: 'Home' },
    { path: '/about', component: About, as: 'About' }
])

class AppComponent {    
    constructor(location: Location){
        location.go('/');
    }    
}    
bootstrap(AppComponent, [ROUTER_PROVIDERS, provide(APP_BASE_HREF, {useValue: '/'})]);

我见过人们扩展routerOutlet来添加他们的身份验证代码,这是一种方法。有谈话在GitHub上有关,但没有定论。这里是Auth0的方式:auth0.com/blog/2015/05/14/...
丹尼斯Smolek

谢谢您的答复。您知道用于学习Angular 2的authService的任何优秀视频吗?
AngularM,2015年

0

从RC 5开始我就这样

this.router.events
  .map( event => event instanceof NavigationStart )
  .subscribe( () => {
    // TODO
  } );

0

只需对AppRoutingModule进行更改即可

@NgModule({
imports: [RouterModule.forRoot(routes, { scrollPositionRestoration: 'enabled' })],
  exports: [RouterModule]
})

0

角度8.检查当前路径是否为基本路径

  baseroute: boolean;
  constructor(
    private router: Router,
  ) {
    router.events.subscribe((val: any) => {
      if (val.url == "/") {
        this.baseroute = true;
      } else {
        this.baseroute = false;
      }
    });
  }

0

我会这样写:

ngOnInit() {
this.routed = this.router.events.map( event => event instanceof NavigationStart )
  .subscribe(() => {
  } );
}

ngOnDestroy() {
this.routed.unsubscribe();
}

-3

Angular 8的简单答案

constructor(private route:ActivatedRoute) {
  console.log(route);
}

1
这不是仅在实例上执行,而是:仅一次!这不是解决方案。
Satria
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.