Angular路由解析器未能解析

10

我在使用Angular时遇到了一个情况,其中路由数据解析器似乎准备好正确返回数据,但是后续的解析过程从未发生。这尤其奇怪,因为我有另一个组件的并排安排,而那里的功能正常。

应用程序通过HTTP检索有关事件数组的数据。在中,所有事件都由解析器响应/events返回,并且组件正确显示它们。在组件中,在我的当前配置中,我仍然通过HTTP检索所有事件,然后,在响应于/events/[the event ID]的解析器中,选择应显示其详细信息的事件。(这来自Pluralsight Angular基础课程,如果听起来很熟悉。但是我倾向于观看视频,然后按照自己的顺序进行工作,以尝试巩固我在脑海中的技能。)

remote-event.service.ts

import { Injectable, EventEmitter } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { IEvent } from './event.model';

@Injectable()
export class RemoteEventService {

  constructor(
    private http: HttpClient
  ) {}

  getEvents(): Observable<IEvent[]> {
    return this.http.get<IEvent[]>('/api/ngfdata/events.json');
  }

  getEventById(id: number): Observable<IEvent> {
    console.log(`In getEventById: id = ${id}`);
    const emitter = new EventEmitter<IEvent>();
    this.getEvents().subscribe(
      (events) => {
        emitter.emit(events.find((event) => event.id === id));
      }
    );
    return emitter;
  }

export interface ISessionSearchResult {
  eventId: number;
  sessionId: number;
  sessionName: string;
}

如果我不使用解析器,EventDetails组件就能正常工作。这样做可以: eventRoutes(这是从/events/分支出的子路由)
import { Routes } from '@angular/router';
import { EventListComponent, EventDetailsComponent,
  CreateEventComponent, UnsavedNewEventGuard,
  EventListResolver, EventDetailResolver
} from './index';

export const eventRoutes: Routes = [
  { path: 'create', component: CreateEventComponent,
    canDeactivate: [UnsavedNewEventGuard]
  },
  { path: ':id', component: EventDetailsComponent/*,
    resolve: { event: EventDetailResolver }*/
  },
  { path: '', component: EventListComponent,
    resolve: { events: EventListResolver }
  }
];

event-details.component.ts

import { Component, Input, OnInit, inject, Inject } from '@angular/core';
import { RemoteEventService } from '../shared/remote-event.service';
import { ActivatedRoute, Params } from '@angular/router';
import { IEvent } from '../shared/event.model';
import { TOASTR_TOKEN } from 'src/app/common/3rd-party/toastr.service';

@Component(
  {
    selector: 'event-detail',
    templateUrl: './event-details.component.html',
    styles: [`
      .container { padding-left: 20px; padding-right: 20px; }
      .event-image { height: 100px; }
      .btn-group:first-child {
        margin-right: 24px;
      }
      .btn-group {
        border: medium solid green;
      }
      .btn-group .btn:not(:first-child) {
        border-left: thin solid green;
      }
    `]
  }
)
export class EventDetailsComponent implements OnInit {
  event: IEvent;
  filterBy = 'all';
  sortBy = 'name';

  constructor(
    private eventService: RemoteEventService,
    private route: ActivatedRoute,
    @Inject(TOASTR_TOKEN) private toast
    ) {
      console.log('In EventDetailsComponent.constructor');
    }

/*   ngOnInit() {
    console.log('At start of EventDetailsComponent.ngOnInit');
    this.event = this.route.snapshot.data['event'];
    console.log('At end of EventDetailsComponent.ngOnInit');
  }
*/

  ngOnInit() {
    console.log('At start of EventDetailsComponent.ngOnInit');
    this.route.params.forEach((params: Params) => {
      this.eventService.getEventById(+params.id).subscribe(
        (event) => this.event = event
      );
    });
    console.log('At end of EventDetailsComponent.ngOnInit');
  }

  flashSessionSummary(message: string) {
    this.toast.info(message);
  }
}

enter image description here

当我在上面的路由列表中取消注释解析器引用,并切换组件代码中两个 ngOnInit 中被注释掉的一个,除了顶部导航栏之外,没有显示任何内容。
我已启用路由跟踪。不使用解析器:

enter image description here

有解析器激活时:

enter image description here

这是解析器:

event-detail-resolver.service.ts

 import { Injectable, Input } from '@angular/core';
import { RemoteEventService } from '../shared/remote-event.service';
import { Resolve, ActivatedRouteSnapshot } from '@angular/router';
import { IEvent } from '../shared/event.model';

@Injectable()
export class EventDetailResolver implements Resolve<IEvent> {

  constructor(
    private eventService: RemoteEventService
  ) {}

  resolve(route: ActivatedRouteSnapshot) {
    console.log(`In resolve(): id = ${route.params.id}`);
    const e = this.eventService.getEventById(+route.params.id);
    console.log(`The observable that resolve() is about to return: ${JSON.stringify(e)}`);
    e.subscribe((evt) => console.log(`The value that the observable resolves to: ${JSON.stringify(evt)}`));
    return e;
  }

}

正如您所见,在返回Observable之前,我订阅了它,以便我可以在解析器中演示它将解析为的值--它是正确的事件对象值。如果您说在这里订阅它会阻止解析器解析,那么不,我是在调试目的之后添加的 它已经无法工作。当我将其注释掉时,我得到完全相同的结果(除了不执行console.log调用):解析器从未解析。

这很奇怪,因为我的Observable上的显式订阅表明它将产生正确的值。

确认这从未超过分辨率,请注意组件构造函数中的console.log语句从未执行,因为在运行请求通过解析器之前。

有什么想法吗?


1
你熟悉 Promise 吗?想象一下以下的代码 function foo() { let bar; new Promise(resolve => resolve(true)).then(result => bar = result); return bar; }。在这个例子中,then() 执行之前 bar 就会被返回。这与 getEventById() 中正在发生的情况非常相似。是的,pipe() 和操作符可能具有复杂的返回类型和消息,但 EventEmitter 简单地不应该在 @Injectable() 中。 - Alexander Staroselsky
getEventById(id: number): Observable<IEvent> { return this.getEvents().pipe(map(events => events.find((event) => event.id === id)) }。请将此代码翻译为中文。 - Alexander Staroselsky
@AlexanderStaroselsky,我想这就是我最初探索管道时使用的代码类型,并且它向我大喊“不!这里需要一个IEvent[] => IEvent[]!”尽管我尝试了很多排列组合,但很难记住。 - Green Grasso Holm
@AlexanderStaroselsky 但是现在它已经可以工作了。谢谢! - Green Grasso Holm
https://www.positronx.io/angular-route-resolvers/ - Billu
显示剩余4条评论
1个回答

30

尝试使用take(1)first运算符来标记Observable的完成。解析器等待Observable完成后才会继续执行。如果Observable没有完成,解析器将无法返回结果。

您的代码应该像这样:

import { Injectable, Input } from '@angular/core';
import { RemoteEventService } from '../shared/remote-event.service';
import { Resolve, ActivatedRouteSnapshot } from '@angular/router';
import { take } from 'rxjs/operators';
import { IEvent } from '../shared/event.model';

@Injectable()
export class EventDetailResolver implements Resolve<IEvent> {

  constructor(
    private eventService: RemoteEventService
  ) {}

  resolve(route: ActivatedRouteSnapshot) {
    console.log(`In resolve(): id = ${route.params.id}`);
    const e = this.eventService.getEventById(+route.params.id);
    console.log(`The observable that resolve() is about to return: ${JSON.stringify(e)}`);
    e.subscribe((evt) => console.log(`The value that the observable resolves to: ${JSON.stringify(evt)}`));
    return e.pipe(take(1));
  }

}

请查看这个 github 讨论 关于这个行为的问题。


1
我需要认真学习解析器的流程,才能理解为什么这样做是有效的。但是,只需在末尾添加".pipe(take(1))"就可以了。谢谢! - Green Grasso Holm

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接