Angular中FullCalendar无法显示后端返回事件的技术问题
问题描述
静态JSON文件中的事件可在FullCalendar中正常显示,但后端接口返回的事件无法展示。新增事件功能正常,事件可保存至数据库,相关代码如下:
AppComponent 组件代码
export class AppComponent implements OnInit, AfterViewInit { @ViewChild('fullcalendar', { static: true }) fullcalendar!: ElementRef; ngOnInit() { console.log('AppComponent initialized'); } ngAfterViewInit() { console.log('AppComponent view initialized'); const fullCalendarElement = this.fullcalendar?.nativeElement; if (fullCalendarElement && fullCalendarElement.fullCalendar) { const calendarApi = new FullCalendar.Calendar(fullCalendarElement, { plugins: [dayGridPlugin, interactionPlugin, timeGridPlugin, listPlugin], // ... other options }); calendarApi.render(); if (calendarApi) { console.log('Calendar API:', calendarApi); } else { console.error('FullCalendar API is undefined'); } } else { console.error('FullCalendar ElementRef or fullCalendar property is undefined'); } } calendarVisible = true; calendarOptions: CalendarOptions = { plugins: [interactionPlugin, dayGridPlugin, timeGridPlugin, listPlugin], headerToolbar: { left: 'prev,next today', center: 'title', right: 'dayGridMonth,timeGridWeek,timeGridDay,listWeek' }, initialView: 'dayGridMonth', initialEvents: INITIAL_EVENTS, weekends: true, editable: true, selectable: true, selectMirror: true, dayMaxEvents: true, select: this.handleDateSelect.bind(this), eventClick: this.handleEventClick.bind(this), eventsSet: this.loadEventsFromBackend.bind(this) }; currentEvents: EventApi[] = []; constructor(private changeDetector: ChangeDetectorRef, private http: HttpClient) {} handleCalendarToggle() { this.calendarVisible = !this.calendarVisible; } handleWeekendsToggle() { this.calendarOptions = { ...this.calendarOptions, weekends: !this.calendarOptions.weekends, }; } loadEventsFromBackend() { this.http.get<any[]>('http://localhost:8080/api/events/all').subscribe(data => { console.log('Backend Data:', data); const backendEvents: EventApi[] = data.map((event: ModuleInterfaceModule) => { return { id: event.id.toString(), title: event.title, start: parseISO(event.start), end: parseISO(event.end), backgroundColor: event.color, startStr: event.start, endStr: event.end, //other properties }; }); if (INITIAL_EVENTS.length < backendEvents.length) { for (let i = 0; i < backendEvents.length; i++) { let addevent: EventInput = { id: backendEvents[i].id, title: backendEvents[i].title, start: backendEvents[i].startStr, end: backendEvents[i].endStr, color: backendEvents[i].backgroundColor }; INITIAL_EVENTS.push(addevent); } } console.log('test events', INITIAL_EVENTS); this.currentEvents = backendEvents; this.changeDetector.detectChanges(); const calendarApi = this.fullcalendar?.nativeElement.fullCalendar; if (calendarApi) { console.log('Adding Event Source:', backendEvents); calendarApi.addEventSource(backendEvents); } }); } }
静态事件定义代码
const TODAY_STR = new Date().toISOString().replace(/T.*$/, ''); // 今日的YYYY-MM-DD格式 export let INITIAL_EVENTS: EventInput[] = [ { id: createEventId(), title: '全天事件', start: TODAY_STR }, { id: createEventId(), title: '定时事件', start: TODAY_STR + 'T00:00:00', end: TODAY_STR + 'T03:00:00' }, { id: createEventId(), title: '定时事件', start: TODAY_STR + 'T12:00:00', end: TODAY_STR + 'T15:00:00' }, { id: createEventId(), title: '定时事件', start: '2024-01-28' + 'T11:00:00', end: '2024-01-28' + 'T12:00:00', backgroundColor:'red', } ];
HTML模板代码
<full-calendar #fullcalendar *ngIf="calendarVisible" [options]="calendarOptions"> <ng-template #eventContent let-arg> <b></b> <i>{{ arg.event.title }}</i> </ng-template> </full-calendar>
ModuleInterfaceModule 实体类代码
export class ModuleInterfaceModule { id!: number; title!: string; start!: string; end!: string; color!: string; }
问题排查与修复方案
核心问题
- 重复初始化日历实例:
ngAfterViewInit中手动创建了Calendar实例,但Angular版FullCalendar是通过组件[options]自动初始化的,两个实例相互独立,导致后续事件添加到了未渲染的实例上。 - 事件加载时机错误:
eventsSet钩子会在日历事件集合变化时触发(包括初始事件加载),在此钩子中调用loadEventsFromBackend会导致重复请求,且逻辑上不应依赖事件集合变化来拉取后端数据。 - 事件格式与API调用错误:
- 转换后端数据时生成了
EventApi类型数组,但addEventSource需要的是EventInput类型(原始事件数据),而非已被日历实例处理过的EventApi对象。 - 修改
INITIAL_EVENTS数组无法触发日历更新,因为initialEvents仅在组件初始化时生效,后续数组变更不会触发Angular变更检测。
- 转换后端数据时生成了
修复步骤
- 移除手动初始化逻辑:删除
ngAfterViewInit中所有手动创建Calendar实例的代码,完全依赖Angular组件的自动初始化流程。 - 调整数据加载时机:在
ngOnInit中直接调用loadEventsFromBackend,确保组件初始化时拉取一次后端数据。 - 正确获取Calendar API:将
@ViewChild的类型改为FullCalendarComponent,通过组件实例的getApi()方法获取正确的日历API:@ViewChild('fullcalendar') fullcalendar!: FullCalendarComponent; - 修正事件转换与添加逻辑:
- 将后端数据转换为
EventInput类型,无需转成EventApi,FullCalendar会自动解析ISO格式的日期字符串。 - 通过Calendar API的
addEventSource方法添加事件,或直接更新calendarOptions.events(需使用不可变对象更新以触发变更检测)。
- 将后端数据转换为
修复后的关键代码示例
// 修改ViewChild类型 @ViewChild('fullcalendar') fullcalendar!: FullCalendarComponent; ngOnInit() { console.log('AppComponent initialized'); this.loadEventsFromBackend(); // 初始化时加载后端数据 } // 移除ngAfterViewInit中的所有代码 loadEventsFromBackend() { this.http.get<any[]>('http://localhost:8080/api/events/all').subscribe(data => { console.log('Backend Data:', data); // 转换为EventInput类型,直接使用后端返回的日期字符串 const backendEvents: EventInput[] = data.map((event: ModuleInterfaceModule) => ({ id: event.id.toString(), title: event.title, start: event.start, end: event.end, backgroundColor: event.color })); // 获取正确的Calendar API实例 const calendarApi = this.fullcalendar.getApi(); if (calendarApi) { // 可选:清空现有事件 // calendarApi.removeAllEvents(); // 添加后端事件源 calendarApi.addEventSource(backendEvents); } // 更新当前事件列表 this.currentEvents = calendarApi?.getEvents() || []; this.changeDetector.detectChanges(); }); }
内容的提问来源于stack exchange,提问作者fahed
相关产品推荐
相关产品推荐

