You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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;
  }

问题排查与修复方案

核心问题

  1. 重复初始化日历实例:ngAfterViewInit中手动创建了Calendar实例,但Angular版FullCalendar是通过组件[options]自动初始化的,两个实例相互独立,导致后续事件添加到了未渲染的实例上。
  2. 事件加载时机错误:eventsSet钩子会在日历事件集合变化时触发(包括初始事件加载),在此钩子中调用loadEventsFromBackend会导致重复请求,且逻辑上不应依赖事件集合变化来拉取后端数据。
  3. 事件格式与API调用错误:
    • 转换后端数据时生成了EventApi类型数组,但addEventSource需要的是EventInput类型(原始事件数据),而非已被日历实例处理过的EventApi对象。
    • 修改INITIAL_EVENTS数组无法触发日历更新,因为initialEvents仅在组件初始化时生效,后续数组变更不会触发Angular变更检测。

修复步骤

  1. 移除手动初始化逻辑:删除ngAfterViewInit中所有手动创建Calendar实例的代码,完全依赖Angular组件的自动初始化流程。
  2. 调整数据加载时机:在ngOnInit中直接调用loadEventsFromBackend,确保组件初始化时拉取一次后端数据。
  3. 正确获取Calendar API:将@ViewChild的类型改为FullCalendarComponent,通过组件实例的getApi()方法获取正确的日历API:
    @ViewChild('fullcalendar') fullcalendar!: FullCalendarComponent;
    
  4. 修正事件转换与添加逻辑:
    • 将后端数据转换为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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 23:18:09