Angular 16 Full Calendar load data problems
Tags: angular, fullcalendar
Published: 2023-12-18 20:32:36

The issue I'm experiencing is due to a delay in asynchronous data loading in my Angular app that uses FullCalendar. Initially, when the component loads, the calendar shows the data, but when I navigate between months, no data is shown, but when I go back in the months, the calendar data appears.

`import { Component, OnInit, ViewChild, TemplateRef } from '@angular/core';
import { RecursoFuncionarioService } from 'src/app/services/recurso-funcionario.service';
import { Response } from 'src/app/helpers/classes/response';
import { CalendarOptions } from '@fullcalendar/core';
import { FullCalendarComponent } from '@fullcalendar/angular';
import dayGridPlugin from '@fullcalendar/daygrid';
import esLocale from '@fullcalendar/core/locales/es';

@Component({
  selector: 'app-consulta-horario',
  templateUrl: './consulta-horario.component.html',
  styleUrls: ['./consulta-horario.component.scss']
})
export class ConsultaHorarioComponent implements OnInit {

  calendarOptions?: CalendarOptions;
  asistenciaDiaria: any;
  datosAsistencia: { [fecha: string]: { entradaHora: string, salidaHora: string, entradaSede: string, salidaSede: string, horasTrabajadas: string } } = {};
  diasFeriadoLegal: { [key: string]: boolean } = {};
  diasPermisoAdministrativo: { [key: string]: boolean } = {};
  diasLicenciaMedica: { [key: string]: boolean } = {};
  datosCargados = false;

  @ViewChild('fullcalendar') fullcalendar?: FullCalendarComponent;
  @ViewChild('eventContent') eventContentTemplate?: TemplateRef<any>;

  constructor(
    private recursoFuncionarioService: RecursoFuncionarioService,
  ) { 
    this.asistenciaDiaria = [{}];
  }

  ngOnInit(): void {
    this.cargaInicial();
  }

  cargaInicial(): void {
    const fecha = new Date();
    this.Load(fecha, true);
  }
  
  LoadCalendar(): void {
    if(this.datosCargados){
      this.calendarOptions = {
        plugins: [
          dayGridPlugin,
        ],
        initialView: 'dayGridMonth',
        firstDay: 1,
        fixedWeekCount: true,
        showNonCurrentDates: false,
        editable: false,
        weekends: true,
        dayMaxEvents: true,
        locale: esLocale,
        eventContent: this.eventContentTemplate,

        headerToolbar: {
          left: 'prev,next',
          center: 'title',
          right: 'today'
        },
        datesSet: (dateInfo) => {
          this.Load(dateInfo.start, false);
        },
        dayCellDidMount: this.dayCellDidMount.bind(this)
      };
    }
  }

  Load(fecha: Date, isInitialLoad: boolean): void {
    this.recursoFuncionarioService.AsistenciaDiariaGetItems(fecha).subscribe((response: Response) => {
      this.asistenciaDiaria = response.Data;
      this.procesarDatosAsistencia();
      if (isInitialLoad) {
        this.LoadCalendar();
      }
    });
  }

  procesarDatosAsistencia(): void {
    const events = this.asistenciaDiaria.flatMap((item) => {
      const dia = new Date(item.Dia);

      if (item.ConFeriadoLegal) {
        this.diasFeriadoLegal[dia.toISOString().split('T')[0]] = true;
      }
      if (item.ConPermisoAdministrativo) {
        this.diasPermisoAdministrativo[dia.toISOString().split('T')[0]] = true;
      }
      if (item.ConLicenciaMedica) {
        this.diasLicenciaMedica[dia.toISOString().split('T')[0]] = true;
      }

      this.datosAsistencia[dia.toISOString().split('T')[0]] = {
        entradaHora: item.EntradaHora ? this.formatTime(item.EntradaHora) : '',
        salidaHora: item.SalidaHora ? this.formatTime(item.SalidaHora) : '',
        entradaSede: item.EntradaSede || '',
        salidaSede: item.SalidaSede || '',
        horasTrabajadas: item.MinutosTrabajados ? this.formatWorkTime(item.MinutosTrabajados) : ''
      };
      this.datosCargados = true;
    });
  }

  dayCellDidMount(arg): void {
    const fecha = arg.date.toISOString().split('T')[0];
    const datosDia = this.datosAsistencia[fecha];

    const esFeriadoLegal = !!this.diasFeriadoLegal[fecha];
    const esPermisoAdministrativo = !!this.diasPermisoAdministrativo[fecha];
    const esLicenciaMedica = !!this.diasLicenciaMedica[fecha];

    const iconContainer = document.createElement('div');
    iconContainer.style.position = 'absolute';
    iconContainer.style.bottom = '0';
    iconContainer.style.right = '0';
    iconContainer.style.padding = '5px'; 

    if (datosDia) {
      const asistenciaContainer = document.createElement('div');
      asistenciaContainer.style.position = 'absolute';
      asistenciaContainer.style.left = '0';
      asistenciaContainer.style.top = '0';
      asistenciaContainer.style.padding = '5px';
      asistenciaContainer.style.textAlign = 'left';
      asistenciaContainer.style.fontSize = '0.80rem';
      asistenciaContainer.style.fontWeight = 'bold';
  
      if (datosDia.entradaHora && datosDia.entradaSede) {
        const contenedorEntrada = document.createElement('div');
        
        const iconEntrada = document.createElement('i');
        iconEntrada.className = 'pi pi-arrow-right';
        iconEntrada.style.color = 'green';
        contenedorEntrada.appendChild(iconEntrada);
  
        const textoEntrada = document.createTextNode(` ${datosDia.entradaHora} - ${datosDia.entradaSede}`);
        contenedorEntrada.appendChild(textoEntrada);
  
        asistenciaContainer.appendChild(contenedorEntrada);
      }
  
      if (datosDia.salidaHora && datosDia.salidaSede) {
        const contenedorSalida = document.createElement('div');
        
        const iconSalida = document.createElement('i');
        iconSalida.className = 'pi pi-arrow-left';
        iconSalida.style.color = 'red';
        contenedorSalida.appendChild(iconSalida);
  
        const textoSalida = document.createTextNode(` ${datosDia.salidaHora} - ${datosDia.salidaSede}`);
        contenedorSalida.appendChild(textoSalida);
  
        asistenciaContainer.appendChild(contenedorSalida);
      }
  
      if (datosDia.horasTrabajadas) {
        const contenedorHoras = document.createElement('div');
        
        const iconHoras = document.createElement('i');
        iconHoras.className = 'pi pi-clock';
        iconHoras.style.color = 'blue';
        contenedorHoras.appendChild(iconHoras);
  
        const textoHoras = document.createTextNode(` ${datosDia.horasTrabajadas} Horas Trabajadas`);
        contenedorHoras.appendChild(textoHoras);
  
        asistenciaContainer.appendChild(contenedorHoras);
      }
  
      arg.el.appendChild(asistenciaContainer);
    }

    if (esFeriadoLegal) {
      const iconFeriado = document.createElement('i');
      iconFeriado.className = 'pi pi-calendar';
      iconFeriado.style.color = 'green';
      iconContainer.appendChild(iconFeriado);
    }

    if (esPermisoAdministrativo) {
      const iconPermisoAdministrativo = document.createElement('i');
      iconPermisoAdministrativo.className = 'pi pi-briefcase';
      iconPermisoAdministrativo.style.color = 'orange'; 
      iconContainer.appendChild(iconPermisoAdministrativo);
    }

    if (esLicenciaMedica) {
      const iconLicenciaMedica = document.createElement('i');
      iconLicenciaMedica.className = 'pi pi-calendar-plus';
      iconLicenciaMedica.style.color = 'red'; 
      iconContainer.appendChild(iconLicenciaMedica);
    }

    arg.el.style.position = 'relative';
    arg.el.appendChild(iconContainer);
  }

  formatTime(dateTimeString: string): string {
    const date = new Date(dateTimeString);
    let hours = date.getHours().toString().padStart(2, '0');
    let minutes = date.getMinutes().toString().padStart(2, '0');
    let seconds = date.getSeconds().toString().padStart(2, '0');
    return `${hours}:${minutes}:${seconds}`;
  }

  formatWorkTime(MinutosTrabajados: number): string {
    const horas = Math.floor(MinutosTrabajados / 60);
    const minutos = MinutosTrabajados % 60;
    return [
      horas.toString().padStart(2, '0'),
      minutos.toString().padStart(2, '0')
    ].join(':');
  }

}
`