@hicoder/angular-cli
Version:
Angular UI componenets and service generator. It works with the mean-rest-express package to generate the end to end web application. The input to this generator is the Mongoose schema defined for the express application. mean-rest-express exposes the Res
386 lines (350 loc) • 12.3 kB
text/typescript
import { Component, OnChanges, OnInit, Input, SimpleChanges, LOCALE_ID, Inject } from '@angular/core';
import { formatDate } from '@angular/common';
import { MddsCommonService } from '@hicoder/angular-core';
import {
CalendarService,
CalendarControlType,
CalendarEventType,
CalendarMessage,
getRepeatTimeSlots,
sortAndUniqueExcludes,
} from '@hicoder/angular-calendar';
import { <%-SchemaName%>ListViewComponent } from './<%-schemaName%>-list-view.component';
export class <%-SchemaName%><%-ComponentClassName%>Component extends <%-SchemaName%>ListViewComponent implements OnInit, OnChanges {
public defaultCalendarView: string = '<%-calendarOptions.defaultView%>';
public weekHourRange: number[] = [<%-calendarOptions.weekHourRange.join(',')%>];
public isReadOnly: boolean = <%-calendarOptions.isReadOnly%>;
public groupName: string = 'Categories';
private calendarFields = {<% for (let field of briefView) {%><%
if (field.calendarGroup) {%>
group: '<%-field.fieldName%>',<%
}%><%
if (field.calendarTitle) {%>
title: '<%-field.fieldName%>',<%
}%><%
if (field.calendarStartTime) {%>
start: '<%-field.fieldName%>',<%
}%><%
if (field.calendarEndTime) {%>
end: '<%-field.fieldName%>',<%
}%><%
if (field.calendarRepeat) {%>
repeat: '<%-field.fieldName%>',<%
}%><%
if (field.calendarRepeatStart) {%>
repeatStart: '<%-field.fieldName%>',<%
}%><%
if (field.calendarRepeatEnd) {%>
repeatEnd: '<%-field.fieldName%>',<%
}%><%
}%>
};
private allGroups = new Set<string>();
private groups: string[] = [];
private calendarFetchTime = new Map<string, number>(); // timestamp, keyed by month.
private allDetails = new Map<string, any>();
private allSchedules = new Map<string, string[]>(); // schedule ids, keyed by detail id.
private newSchedules: any[] = [];
private updatedSchedules: any[] = [];
private deletedSchedules: any[] = [];
private calendarReady: boolean = false;
private timeZone: string;
private locale: string;
private calendarID: string = '';
constructor(
private commonService: MddsCommonService,
public calendarService: CalendarService) {
super();
this.locale = this.commonService.getLocale();
this.timeZone = this.commonService.getTimeZone();
this.groupName = this.calendarFields.group.charAt(0).toUpperCase() + this.calendarFields.group.slice(1);
}
override ngOnInit() {
super.ngOnInit();
}
ngOnChanges(changes: SimpleChanges) {
// Take originalList.
if (changes['originalList']) {
this.generateSchedules(this.originalList as any[], false);
}
if (changes['editHintFields']) {
this.createGroups(this.editHintFields);
}
if (changes['deletedItemIds']) {
this.deleteSchedules(this.deletedItemIds);
}
}
generateSchedules(list: any[], force: boolean) {
for (let detail of list) {
let d = this.allDetails.get(detail['_id']);
if (!force && d &&
d[this.calendarFields.title] == detail[this.calendarFields.title] &&
d[this.calendarFields.group] == detail[this.calendarFields.group] &&
d[this.calendarFields.start] == detail[this.calendarFields.start] &&
d[this.calendarFields.end] == detail[this.calendarFields.end] &&
d[this.calendarFields.repeat] == detail[this.calendarFields.repeat]
) {
// Schedule not changed. Skip;
continue;
}
// Update the saved details.
this.allDetails.set(detail['_id'], detail);
// Delete the existing schedules.
if (d) {
let oldSchedules = this.allSchedules.get(detail['_id']);
if (oldSchedules) {
for (let sId of oldSchedules) {
this.deletedSchedules.push({
id: sId,
calendarId: d[this.calendarFields.group],
});
}
}
this.allSchedules.delete(detail['_id']);
}
let grp = detail[this.calendarFields.group];
if (grp && !this.allGroups.has(grp)) {
this.groups.push(grp);
this.allGroups.add(grp);
}
let timeSlots: string[][] = [[detail[this.calendarFields.start], detail[this.calendarFields.end]]];
if (detail[this.calendarFields.repeat]) {
timeSlots = getRepeatTimeSlots(
this.timeZone,
detail[this.calendarFields.start], detail[this.calendarFields.end],
detail[this.calendarFields.repeat],
)
}
let sIds: string[] = [];
for (let [i, slot] of timeSlots.entries()) {
let sId = `${detail['_id']}_${i}`;
let schedule: any = {
id: sId,
calendarId: grp,
title: detail[this.calendarFields.title],
category: 'time',
dueDateClass: '',
isAllDay: false,
start: slot[0],
end: slot[1],
recurrenceRule: detail[this.calendarFields.repeat],
}
this.newSchedules.push(schedule);
sIds.push(sId);
}
this.allSchedules.set(detail['_id'], sIds);
}
this.notifyCalendar();
}
deleteSchedules(ids: string[]) {
for (let id of ids) {
let d = this.allDetails.get(id);
if (!d) {
continue;
}
this.allDetails.delete(id);
let oldSchedules = this.allSchedules.get(id);
if (!oldSchedules) {
continue;
}
for (let sId of oldSchedules) {
this.deletedSchedules.push({
id: sId,
calendarId: d[this.calendarFields.group],
});
}
this.allSchedules.delete(id);
}
this.notifyCalendar();
}
// Schedule has been updated.
changedSchedule(changes: any) {
if (!changes['changes'] || !changes['schedule']) {
return;
}
let detailId = this.formatID(changes['schedule']['id']);
let d = this.allDetails.get(detailId);
if (!d) {
return;
}
// Start or end time has changed for this schedule. Probably through UI drag.
if (changes['changes']['start']) {
d[this.calendarFields.start] = changes['changes']['start'].toDate().toISOString();
}
if (changes['changes']['end'] ) {
d[this.calendarFields.end] = changes['changes']['end'].toDate().toISOString();
}
// Notify the parent of the change.
this.onChanged(d);
// Update the schedule in the calendar.
this.generateSchedules([d], true);
}
viewSchedule(id: string) {
this.onEmbeddedDetail(this.formatID(id));
}
updateSchedule(id: string) {
this.onEmbeddedEdit(this.formatID(id));
}
deleteSchedule(id: string) {
this.onDelete(this.formatID(id), null);
}
deleteOneSchedule(schedule: any) {
let detailId = this.formatID(schedule.id);
let d = this.allDetails.get(detailId);
if (!d || !d.repeat) {
return;
}
let repeatObj = JSON.parse(d.repeat);
if (!repeatObj.exclude) {
repeatObj.exclude = [];
}
repeatObj.exclude.push(schedule['start'].toDate().toISOString());
repeatObj.exclude = sortAndUniqueExcludes(repeatObj.exclude, this.timeZone);
d.repeat = JSON.stringify(repeatObj);
this.onChanged(d);
this.generateSchedules([d], true);
}
addSchedule(schedule: any) {
let detail: any = {};
// Start or end time should be available for this schedule.
if (schedule['start']) {
detail[this.calendarFields.start] = schedule['start'].toDate().toISOString();
}
if (schedule['end'] ) {
detail[this.calendarFields.end] = schedule['end'].toDate().toISOString();
}
// Notify the parent of the new added schedule. Allow parent to change the start/end date.
this.onEmbeddedAdd(detail, true);
}
createGroups(editHintFields: any) {
let grps = this.editHintFields[this.calendarFields.group]||[];
grps = grps.map((x: any) => {return x['_id']});
for (let grp of grps) {
if (!grp || this.allGroups.has(grp)) {
continue;
}
this.groups.push(grp);
this.allGroups.add(grp);
}
this.notifyCalendar();
}
getCalendarData(d: Date) {
this.notifyCalendar();
let date = new Date(d);
date.setDate(1);
let year = date.getFullYear();
let month = date.getMonth();
let monthStr = formatDate(date, 'y-MM', 'en-US');
date.setMonth(month+1);
let monthStrNext = formatDate(date, 'y-MM', 'en-US');
date.setMonth(month-1);
let monthStrPre = formatDate(date, 'y-MM', 'en-US');
let monthCache = this.calendarFetchTime.get(monthStr);
let monthCacheNext = this.calendarFetchTime.get(monthStrNext);
let monthCachePre = this.calendarFetchTime.get(monthStrPre);
let currentTime = new Date().getTime();
let invalidateTime = currentTime - 1000 * 120; // 120 seconds.
let monthPreCached = monthCachePre && monthCachePre > invalidateTime;
let monthCached = monthCache && monthCache > invalidateTime;
let monthNextCached = monthCacheNext && monthCacheNext > invalidateTime;
let dateFrom = new Date(year, month-1, 1);
let dateTo = new Date(year, month+2, 1);
let detail:any = {};
detail[this.calendarFields.repeatStart] = {
to: {year:dateTo.getFullYear(), month: dateTo.getMonth()+1, day: 1},
}
detail[this.calendarFields.repeatEnd] = {
from: {year:dateFrom.getFullYear(), month: dateFrom.getMonth()+1, day: 1},
}
if (monthCached && monthPreCached && monthNextCached) {
this.processSearchContext(detail);
return;
}
this.searchListAll(detail);
this.calendarFetchTime.set(monthStrPre, currentTime);
this.calendarFetchTime.set(monthStr, currentTime);
this.calendarFetchTime.set(monthStrNext, currentTime);
}
public onCalendarMessage(message: CalendarMessage) {
// console.log('calendarEvent: ', message);
switch (message.type) {
case CalendarEventType.viewSchedule:
this.viewSchedule(message.event.id);
break;
case CalendarEventType.addSchedule:
this.addSchedule(message.event);
break;
case CalendarEventType.changedSchedule:
this.changedSchedule(message.event);
break;
case CalendarEventType.updateSchedule:
this.updateSchedule(message.event.id);
break;
case CalendarEventType.deleteSchedule:
this.deleteSchedule(message.event.id);
break;
case CalendarEventType.deleteOneSchedule:
this.deleteOneSchedule(message.event);
break;
case CalendarEventType.newDate:
if (!this.calendarReady) {
this.calendarReady = true;
this.calendarID = message.id;
// Get data with timeout to avoid list change during initialization.
setTimeout(()=> {
this.getCalendarData(message.event as Date);
}, 50)
} else {
this.getCalendarData(message.event as Date);
}
break;
}
}
private notifyCalendar() {
if (!this.calendarReady || !this.calendarID) {
return;
}
if (this.deletedSchedules.length > 0) {
let schedules = this.deletedSchedules;
this.deletedSchedules = [];
this.calendarService.publishCalendarEvent({
type: CalendarControlType.delSchedules,
id: this.calendarID,
event: schedules,
});
}
if (this.newSchedules.length > 0) {
let schedules = this.newSchedules;
this.newSchedules = [];
this.calendarService.publishCalendarEvent({
type: CalendarControlType.newSchedules,
id: this.calendarID,
event: schedules,
});
}
if (this.updatedSchedules.length > 0) {
let schedules = this.updatedSchedules;
this.updatedSchedules = [];
this.calendarService.publishCalendarEvent({
type: CalendarControlType.updSchedules,
id: this.calendarID,
event: schedules,
});
}
if (this.groups.length > 0) {
let groups = this.groups;
this.groups = [];
this.calendarService.publishCalendarEvent({
type: CalendarControlType.newGroups,
id: this.calendarID,
event: groups,
});
}
}
private formatID(id: string): string {
return id.split('_')[0];
}
}