Angular 路由解析器未解析
Angular routing resolver not resolving
我遇到了 Angular 的情况,其中路由数据解析器似乎已准备好正确 return 数据,但解析永远不会发生。这特别奇怪,因为我有一个用于另一个组件的并行 ar运行gement,它在那里工作正常。
应用程序通过 HTTP 检索有关一组事件的数据。在 EventListComponent
中,所有事件都由解析器 return 编辑以响应 /events
,组件会正确显示它们。在 EventDetails
组件中,在我当前的 ar运行gement 中,我仍然通过 HTTP 检索所有事件,然后在解析器中响应 /events/[the event ID]
,选择应该的事件显示其详细信息。 (这是来自 Pluralsight Angular 基础课程,以防听起来很熟悉。但我倾向于观看视频,然后按照自己的顺序学习它们,以尝试巩固我头脑中的技能。)
远程-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/出发的子路线b运行)
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 }
}
];
事件-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);
}
}
当我在上面的路由列表中取消注释解析器引用,并在上面的组件代码中切换两个ngOnInit中的哪一个被注释掉时,除了顶部的导航栏之外没有任何显示。
我启用了路由跟踪。不使用解析器:
解析器处于活动状态:
这是解析器:
事件详情-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;
}
}
如您所见,在 returning Observable 之前,我订阅了它,这样我就可以在解析器中演示它将解析到的值——这是正确的事件对象值。以免你说在这里订阅它会阻止解析器解析,好吧,不,我为了调试目的添加了它 after 它已经不起作用了。当我将其注释掉时,我得到完全相同的结果(除了未执行 console.log 调用):解析器永远不会解析。
这很奇怪,因为我对 Observable 的显式订阅表明它会产生正确的值。
确认这永远不会超过解决方案,请注意组件构造函数中的 console.log 语句永远不会执行,就像我 运行 通过解析器请求之前一样。
有什么想法吗?
尝试使用
take(1)
或 first
运算符来标记Observable
的完成。 resolve 在继续之前等待 Observable 完成。如果 Observable 没有完成,resovler 将不会 return.
你的代码应该是这样的:
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 discussion 关于这个行为。
我遇到了 Angular 的情况,其中路由数据解析器似乎已准备好正确 return 数据,但解析永远不会发生。这特别奇怪,因为我有一个用于另一个组件的并行 ar运行gement,它在那里工作正常。
应用程序通过 HTTP 检索有关一组事件的数据。在 EventListComponent
中,所有事件都由解析器 return 编辑以响应 /events
,组件会正确显示它们。在 EventDetails
组件中,在我当前的 ar运行gement 中,我仍然通过 HTTP 检索所有事件,然后在解析器中响应 /events/[the event ID]
,选择应该的事件显示其详细信息。 (这是来自 Pluralsight Angular 基础课程,以防听起来很熟悉。但我倾向于观看视频,然后按照自己的顺序学习它们,以尝试巩固我头脑中的技能。)
远程-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/出发的子路线b运行)
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 }
}
];
事件-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);
}
}
当我在上面的路由列表中取消注释解析器引用,并在上面的组件代码中切换两个ngOnInit中的哪一个被注释掉时,除了顶部的导航栏之外没有任何显示。
我启用了路由跟踪。不使用解析器:
解析器处于活动状态:
这是解析器:
事件详情-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;
}
}
如您所见,在 returning Observable 之前,我订阅了它,这样我就可以在解析器中演示它将解析到的值——这是正确的事件对象值。以免你说在这里订阅它会阻止解析器解析,好吧,不,我为了调试目的添加了它 after 它已经不起作用了。当我将其注释掉时,我得到完全相同的结果(除了未执行 console.log 调用):解析器永远不会解析。
这很奇怪,因为我对 Observable 的显式订阅表明它会产生正确的值。
确认这永远不会超过解决方案,请注意组件构造函数中的 console.log 语句永远不会执行,就像我 运行 通过解析器请求之前一样。
有什么想法吗?
尝试使用
take(1)
或 first
运算符来标记Observable
的完成。 resolve 在继续之前等待 Observable 完成。如果 Observable 没有完成,resovler 将不会 return.
你的代码应该是这样的:
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 discussion 关于这个行为。