Angular-Calendar自定义单元格模板异步加载事件失败排查
问题:angular-calendar异步加载事件无法显示,始终停留在加载状态
我正在开发一款使用angular-calendar生成日历的应用,用于展示酒店房间的每日价格。
组件代码(component.ts)如下:
events$!: Observable<CalendarEvent<{ hotel: Hotel }>[]> ngOnInit(): void { this.fetchEvents() } fetchEvents(): void{ this.hotelService.getHotels().pipe( map((hotels: Hotel[]) => { const events: CustomCalendarEvent[] = []; hotels.forEach(hotel => { if (hotel.rooms.single) { hotel.rooms.single.daily_prices.forEach(element => { events.push({ start: new Date(element.start), price: element.price, title: '', allDay: true, }) }) } }) return events }) ).subscribe(events => { this.events$ = of(events) console.log(events) }) }
通过服务获取数据后,控制台输出显示数据正常,但HTML模板始终显示加载状态:
<ng-template #customCellTemplate let-day="day" let-locale="locale" class="cell"> <div class="cal-cell-top"> <span class="cal-day-number"> {{ day.date | calendarDate:'monthViewDayNumber':locale }} </span> </div> <small class="price-cell"> Precio habitación: <br> <strong>{{ day.events$ }}</strong> </small> </ng-template> <ng-template #loading> <div class="text-center"> <i class="fas fa-spin fa-spinner fa-5x"></i> <br /> Loading events... </div> </ng-template> <div *ngIf="events$ | async; else loading; let events"> <div [ngSwitch]="view"> <mwl-calendar-month-view *ngSwitchCase="'month'" [viewDate]="viewDate" [locale]="locale" [weekStartsOn]="weekStartsOn" [weekendDays]="weekendDays" [cellTemplate]="customCellTemplate" [events]="events"> </mwl-calendar-month-view> </div> </div>
参考angular-calendar官方文档示例实现,但仅能看到加载模板。尝试硬编码事件时,日历可正常显示数据,推测问题出在异步数据获取环节。请问为何异步加载的事件无法显示?我哪里操作有误?
问题分析与解决
你代码里有两个关键错误:
1. events$的赋值方式错误
你现在是在subscribe回调里把events$赋值为of(events),但组件初始化时async管道已经尝试订阅events$(此时它还未被赋值,是undefined),当你后续给events$赋值新的Observable时,async管道不会自动重新订阅,导致模板一直停留在加载状态。
正确的做法是直接把API请求的Observable赋值给events$,不需要手动subscribe:
events$!: Observable<CustomCalendarEvent[]>; ngOnInit(): void { this.fetchEvents(); } fetchEvents(): void { this.events$ = this.hotelService.getHotels().pipe( map((hotels: Hotel[]) => { const events: CustomCalendarEvent[] = []; hotels.forEach(hotel => { if (hotel.rooms.single) { hotel.rooms.single.daily_prices.forEach(element => { events.push({ start: new Date(element.start), price: element.price, title: '', allDay: true, }); }); } }); return events; }) ); }
这样events$从一开始就是一个有效的Observable,async管道会自动订阅它,当API数据返回时自动更新模板。
2. 自定义单元格模板里的事件引用错误
angular-calendar的day对象里的events是同步数组,不是Observable,你写{{ day.events$ }}是错误的。需要遍历day.events数组,取出对应的价格值:
<ng-template #customCellTemplate let-day="day" let-locale="locale" class="cell"> <div class="cal-cell-top"> <span class="cal-day-number"> {{ day.date | calendarDate:'monthViewDayNumber':locale }} </span> </div> <small class="price-cell" *ngIf="day.events.length > 0"> Precio habitación: <br> <strong>{{ (day.events[0] as CustomCalendarEvent).price }}</strong> </small> </ng-template>
这里假设每个日期对应一个价格事件,如果有多个,可以用*ngFor遍历显示。
修改这两处后,异步加载的事件就能正常显示在日历中了。
内容的提问来源于stack exchange,提问作者Alexander Romero
相关产品推荐
相关产品推荐

