【问题标题】:Angular 2 router event not firing first time?Angular 2 路由器事件没有第一次触发?
【发布时间】:2021-12-23 19:27:31
【问题描述】:

我正在从一个组件路由到另一个组件。路线完成后,我想使用上一条路线中的 URL。我已将下面的代码放入被路由到的组件的构造函数中,但它不会在第一条路由上触发。在第一条路线之后,该函数每次都会触发。

this.router.events
      .filter(e => e instanceof NavigationEnd)
      .pairwise().subscribe((e) => {
          console.log(e);
    });

如果我删除成对函数,它似乎会在第一条路线上触发,但它只列出当前路线,而不是前一条路线。

 router.events
  .filter(e => e instanceof NavigationEnd)
  .subscribe(e => {
    console.log(e);
 });

我的目标是在新组件被路由到时检索先前的路由。我在这里做错了什么?

【问题讨论】:

  • 这确实需要一个服务,或者至少在你的根组件中需要一个处理程序。

标签: angular angular2-routing


【解决方案1】:

我看到所有这些解决方案都转发给route.snapshot,并尝试将 NavigationEnd 操作与它结合起来。但是,如果您尝试访问该页面 - 它不会被 router.events 的第一个 NavigationEnd 触发。快照也会触发父路由,而不是子路由。所以首先你可以使用子路由,但它也不会作为第一个被触发......

this.router.events.pipe(
  filter((event) => event instanceof NavigationEnd),
  map(() => this.aRoute.snapshot),
  map((route) => {
    while (route.firstChild) {
      route = route.firstChild;
    }
    return route;
  })
)

所以对我来说最好的解决方案是在正确当前路线上第一次开火是使用router.events + router.url 和一个类型NavigationEnd

 this.router.events
   .pipe(
      filter((event) => event instanceof NavigationEnd),
      startWith(this.router)
   )
   .subscribe((event: NavigationEnd) => {
     // there will be first router.url - and next - router events
 })

【讨论】:

  • 爱它!第二种解决方案很有帮助,遗憾的是,默认情况下不会发出任何事件。
  • 第二个解决方案真的很有帮助!谢谢
  • 非常感谢!我使用 startWith 和空的,在顶部,我的错。
  • 第二种解决方案确实帮助我修复了组件的第一次加载。谢谢
  • 好的,但是为什么它一直在增加射击次数。
【解决方案2】:

答案已经给出:NavigationEnd 事件在组件注册时已经引发。我不喜欢“Dimitar Tachev”通过主题代理这些事件来创建解决方法的想法。 就我而言,解决方案是:

  1. Component 像以前一样订阅NavigationEnd 事件。
  2. 使ComponentngOnInit 方法中从注入的Route 对象加载初始状态。

最后另一种解决方案是将路由更改事件的订阅移动到组件的构造函数中。

【讨论】:

  • 感谢您的回答。我认为将订阅移动到构造函数在这里应该没问题,这对我来说似乎是最简单的解决方案。
【解决方案3】:

我有完全相同的场景,我发现it's too late 在子组件的构造函数中成对订阅 NavigationEnd。

您可以在根组件中订阅路由器并通过服务共享路由数据,如下例所示:

events.service.ts

import { Injectable } from '@angular/core';
import { RouteChanged } from '../models/route-changed.model';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';

@Injectable()
export class EventsService {
  public routeChanged = new BehaviorSubject<RouteChanged>({ prevRoute: '/', currentRoute: '/' });

  constructor() {
  }
}

app.component.ts(你的根组件)

...

@Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit, OnDestroy {
    private subscriptions = new Subscription();

    constructor(private eventsService: EventsService) {
            this.subscriptions.add(this.router.events
                .filter(event => event instanceof NavigationEnd)
                .pairwise()
                .subscribe(navigationEvents => {
                    const prevPage = <NavigationEnd>navigationEvents[0];
                    const currentPage = <NavigationEnd>navigationEvents[1];
                    this.eventsService.routeChanged.next(
                        { prevRoute: prevPage.urlAfterRedirects, currentRoute: currentPage.urlAfterRedirects });
                }));
        }

    ngOnDestroy(): void {
        this.subscriptions.unsubscribe();
    }

    ...
}

你的目标路由.ts

...
constructor(private eventsService: EventsService) {
    this.subscriptions.add(this.eventsService.routeChanged.subscribe((routeChanged) => {
        // use routeChanged.prevRoute and routeChanged.currentRoute here...
    }));
}

附:如果您的子组件在页面加载后订阅,请务必在服务中使用 BehaviorSubjectReplaySubject 以获得正确的先前路由事件。

【讨论】:

    【解决方案4】:

    2019,这仍然是个问题

    这是获取所有路由更改的方法包括子组件中的页面加载路由。

      import { filter } from 'rxjs/operators';
    
      constructor(
        private router: Router, 
        private route: ActivatedRoute
      ) {
        // First route event on load
        this.setFromSnapshot(this.route.snapshot);
        // Subsequent events after load
        this.router.events
          .pipe(filter(event => event instanceof ActivationStart))
          .subscribe((event:ActivationStart) => this.setFromSnapshot(event.snapshot));
      }
    
      setFromSnapshot(snapshot:ActivatedRouteSnapshot) {
        // Do something
      }
    
    

    【讨论】:

      【解决方案5】:

      pairwise 在第二次和后续发射时发射 (http://reactivex.io/rxjs/class/es6/Observable.js~Observable.html#instance-method-pairwise)

      所以设置初始值很重要,例如:

      this.router.events
        .startWith(new NavigationEnd(0, '/', '/'))
        .filter(e => e instanceof NavigationEnd)
        .pairwise()
        .subscribe((e) => {
            console.log(e);
        });
      

      【讨论】:

      • 由于成对运算符不会在第一次运行时触发,它正在等待下一个值:rxmarbles.com/#pairwise
      • 正如我所写的,设置 startWith 操作符很重要,让它在第一次运行时工作。
      • 成对来自 OP,所以我认为这是已知的。这里的关键是 startWith。这是我能接受的唯一解决方案。
      【解决方案6】:

      原因是导航事件发生时你的组件还不存在。

      创建一个在app.component.ts 中注入的全局服务,并将其也注入到您的组件中。

      在服务上创建“助手”可观察对象。

      // RouterEventsService.ts 
      navigationStart$ = this.router.events.pipe(filter((e): e is NavigationStart => e instanceof NavigationStart));
      navigationEnd$ = this.router.events.pipe(filter((e): e is NavigationEnd => e instanceof NavigationEnd));
      
      navigationEndReplay$ = this.navigationEnd$.pipe(shareReplay(1));
      

      在服务的构造函数中,您必须订阅navigationEndReplay$ 事件。这意味着稍后在您的组件中,您可以订阅它并获取缓存的最新结束事件。

      // RouterEventsService.ts constructor
      this.routerEvents.navigationEndReplay$.subscribe();   // note: unsubscribe in app.component not needed
      

      然后你可以在你的组件中使用routerEvents.navigationEndReplay$,它会在组件初始化后立即触发。您可以在构造函数中使用它,ngOnInitngAfterContentInit 等。

      最好同时保持navigationEnd$navigationEndReplay$ 是明确的。

      【讨论】:

      • 这与 David 的答案非常相似,但我从服务中公开了一个实际的 observable,而不是属性。除了在服务中触发“订阅”之外,您无需订阅任何内容,直到您需要它。然后,您可以使用navigationEndReplay$ 作为您自己代码中的真正可观察对象。注意:我没有在这里实现 pairWise,但我认为这是个好主意。
      • Simon Weaver,我想你的意思是在app.component中添加订阅
      【解决方案7】:

      受先前答案的启发,我提出了以下解决方案:

        import { filter, map, startWith } from 'rxjs/operators';
      
        constructor(
          private router: Router, 
          private route: ActivatedRoute
        ) {
          this.router.events
            .pipe(
              filter(event => event instanceof ActivationStart),
              map(({ snapshot }) => snapshot),
              startWith(this.route.snapshot)
            )
            .subscribe((snapshot: ActivatedRouteSnapshot) => /** ... do something */ )
        }
      

      【讨论】:

        【解决方案8】:

        我的案子是这样处理的

        //Whatever I was doing in route.events subcription put it into method and call it onInit()
        this.validateRoute(this.router.url);
        
        //Then make subscription to route changes and do it again on each, can do this in constructor or onInit() doesnt matter
        this.router.events
          .pipe(
             filter((e) => e instanceof NavigationEnd),
             map((event) => (event as NavigationEnd).urlAfterRedirects),
             untilDestroyed(this),
           )
           .subscribe((url) => {
              this.validateRoute(url);
           });
        

        【讨论】:

          【解决方案9】:

          我只是偶然发现了同样的问题并找到了原因:订阅路由器事件的服务从未被依赖注入器实例化,因为服务没有在该路由处注入。

          服务似乎只有在被注入某处时才会被实例化。

          如果整个代码(而不是事件)被调用过,请检查您的第一条路线。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2016-06-25
            • 1970-01-01
            • 2017-09-25
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多