我遇到的问题是由于使用 FullCalendar 的 Angular 应用程序中的异步数据加载滞后造成的。最初,当组件加载时,日历显示为空,因为它是在异步数据(例如事件或每日出勤信息)可用之前呈现的。然后,当我在月份之间导航时,日历最终显示数据,表明数据加载和更新当时正常工作,但在应用程序首次启动期间无法正常工作。
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 interactionPlugin from '@fullcalendar/interaction';
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);
}
Load(fecha: Date) {
this.recursoFuncionarioService.AsistenciaDiariaGetItems(fecha).subscribe((response: Response) => {
this.asistenciaDiaria = response.Data;
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;
this.LoadCalendar();
});
});
}
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);
},
dayCellDidMount: (arg) => {
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(':');
}
}
<div class="grid">
<div class="col-12">
<div class="view-titulo">Consulta horario</div>
</div>
</div>
<div class="flex justify-content-evenly flex-wrap p-3">
<div>
<i class="pi pi-arrow-right" style="color: green"></i>
<label class="pl-2">Entrada</label>
</div>
<div>
<i class="pi pi-arrow-left" style="color: red"></i>
<label class="pl-2">Salida</label>
</div>
<div>
<i class="pi pi-clock" style="color: blue"></i>
<label class="pl-2">Horas trabajadas</label>
</div>
<div>
<i class="pi pi-calendar-plus" style="color: red"></i>
<label class="pl-2">Licencia médica</label>
</div>
<div>
<i class="pi pi-calendar" style="color: green"></i>
<label class="pl-2">Feriado Legal</label>
</div>
<div>
<i class="pi pi-briefcase" style="color: orange"></i>
<label class="pl-2">Permiso Administrativo</label>
</div>
</div>
<div class="grid">
<div class="col-12">
<full-calendar class="full-calendar-custom"
*ngIf="calendarOptions"
#fullcalendar
[dayCellDidMount]="dayCellDidMount"
[options]="calendarOptions">
</full-calendar>
</div>
</div>
</div>```
您可以使用 Angular 的 ResolveFn。基本上,您将从路由器调用数据提供程序功能。一旦您的异步数据提供程序函数获取所有数据,它就会加载组件。您将在 onInit 函数中准备好数据,没有任何延迟。
请参阅我写的解释此过程的另一个答案https://stackoverflow.com/a/77517484/5822844