Quick info template in EJ2 TypeScript Scheduler control

18 Mar 202524 minutes to read

The Scheduler control displays a pop-up window (Quick Info) when users click on cells or appointments. By default, this Quick Info displays standard information, but you can customize its appearance and content using templates.

This guide demonstrates how to create custom templates for Quick Info pop-ups that appear when clicking on both empty time cells and appointments in the Scheduler control.

import { extend, Internationalization } from '@syncfusion/ej2-base';
import { Button } from '@syncfusion/ej2-buttons';
import { DropDownList } from '@syncfusion/ej2-dropdowns';
import { TextBox } from '@syncfusion/ej2-inputs';
import {
    Schedule, Day, Week, WorkWeek, Month, Agenda, EventRenderedArgs, Resize, DragAndDrop,
    ResourcesModel, PopupOpenEventArgs, EJ2Instance, CellClickEventArgs, CurrentAction
} from '@syncfusion/ej2-schedule';
import { scheduleData } from './datasource.ts';

Schedule.Inject(Day, Week, WorkWeek, Month, Agenda, Resize, DragAndDrop);

interface TemplateFunction extends Window {
    getHeaderDetails?: Function;
    getHeaderStyles?: Function;
    getEventType?: Function;
    getResourceData?: Function;
}

(window as TemplateFunction).getResourceData = (data: { [key: string]: Object }) => {
    let resources: ResourcesModel = scheduleObj.getResourceCollections().slice(-1)[0];
    let resourceData: { [key: string]: Object } = (resources.dataSource as Object[]).filter((resource: { [key: string]: Object }) =>
        resource.Id === data.RoomId)[0] as { [key: string]: Object };
    return resourceData;
};

(window as TemplateFunction).getHeaderDetails = (data: { [key: string]: Date }) => {
    let intl: Internationalization = new Internationalization();
    return intl.formatDate(data.StartTime, { type: 'date', skeleton: 'full' }) + ' (' +
        intl.formatDate(data.StartTime, { skeleton: 'hm' }) + ' - ' + intl.formatDate(data.EndTime, { skeleton: 'hm' }) + ')';
};

(window as TemplateFunction).getHeaderStyles = (data: { [key: string]: Object }) => {
    if (data.elementType === 'cell') {
        return 'align-items: center; color: #919191;';
    } else {
        let resourceData: { [key: string]: Object } = (window as TemplateFunction).getResourceData(data);
        return 'background:' + resourceData.Color + '; color: #FFFFFF;';
    }
};

let buttonClickActions: Function = (e: Event) => {
    let quickPopup: HTMLElement = scheduleObj.element.querySelector('.e-quick-popup-wrapper') as HTMLElement;
    let getSlotData: Function = (): { [key: string]: Object } => {
        let cellDetails: CellClickEventArgs = scheduleObj.getCellDetails(scheduleObj.getSelectedElements());
        let addObj: { [key: string]: Object } = {};
        addObj.Id = scheduleObj.getEventMaxID();
        addObj.Subject = ((quickPopup.querySelector('#title') as EJ2Instance).ej2_instances[0] as TextBox).value;
        addObj.StartTime = new Date(+cellDetails.startTime);
        addObj.EndTime = new Date(+cellDetails.endTime);
        addObj.Description = ((quickPopup.querySelector('#notes') as EJ2Instance).ej2_instances[0] as TextBox).value;
        addObj.RoomId = ((quickPopup.querySelector('#eventType') as EJ2Instance).ej2_instances[0] as DropDownList).value;
        return addObj;
    };
    if ((e.target as HTMLElement).id === 'add') {
        let addObj: { [key: string]: Object } = getSlotData();
        scheduleObj.addEvent(addObj);
    } else if ((e.target as HTMLElement).id === 'delete') {
        let eventDetails: { [key: string]: Object } = scheduleObj.activeEventData.event as { [key: string]: Object };
        let currentAction: CurrentAction;
        if (eventDetails.RecurrenceRule) {
            currentAction = 'DeleteOccurrence';
        }
        scheduleObj.deleteEvent(eventDetails, currentAction);
    } else {
        let isCellPopup: boolean = quickPopup.firstElementChild.classList.contains('e-cell-popup');
        let eventDetails: { [key: string]: Object } = isCellPopup ? getSlotData() :
            scheduleObj.activeEventData.event as { [key: string]: Object };
        let currentAction: CurrentAction = isCellPopup ? 'Add' : 'Save';
        if (eventDetails.RecurrenceRule) {
            currentAction = 'EditOccurrence';
        }
        scheduleObj.openEditor(eventDetails, currentAction, true);
    }
    scheduleObj.closeQuickInfoPopup();
};

(window as TemplateFunction).getEventType = (data: { [key: string]: Date }) => {
    let resourceData: { [key: string]: Object } = (window as TemplateFunction).getResourceData(data);
    return resourceData.Name;
};

let data: Object[] = <Object[]>extend([], scheduleData, null, true);
let scheduleObj: Schedule = new Schedule({
    width: '100%',
    height: '650px',
    selectedDate: new Date(2020, 0, 9),
    eventSettings: { dataSource: data },
    resources: [{
        field: 'RoomId', title: 'Room Type', name: 'MeetingRoom', textField: 'Name', idField: 'Id', colorField: 'Color',
        dataSource: [
            { Name: 'Jammy', Id: 1, Capacity: 20, Color: '#ea7a57', Type: 'Conference' },
            { Name: 'Tweety', Id: 2, Capacity: 7, Color: '#7fa900', Type: 'Cabin' },
            { Name: 'Nestle', Id: 3, Capacity: 5, Color: '#5978ee', Type: 'Cabin' },
            { Name: 'Phoenix', Id: 4, Capacity: 15, Color: '#fec200', Type: 'Conference' },
            { Name: 'Mission', Id: 5, Capacity: 25, Color: '#df5286', Type: 'Conference' },
            { Name: 'Hangout', Id: 6, Capacity: 10, Color: '#00bdae', Type: 'Cabin' },
            { Name: 'Rick Roll', Id: 7, Capacity: 20, Color: '#865fcf', Type: 'Conference' },
            { Name: 'Rainbow', Id: 8, Capacity: 8, Color: '#1aaa55', Type: 'Cabin' },
            { Name: 'Swarm', Id: 9, Capacity: 30, Color: '#df5286', Type: 'Conference' },
            { Name: 'Photogenic', Id: 10, Capacity: 25, Color: '#710193', Type: 'Conference' }
        ]
    }],
    quickInfoTemplates: {
        header: '#header-template',
        content: '#content-template',
        footer: '#footer-template'
    },
    eventRendered: (args: EventRenderedArgs) => {
        let categoryColor: string = args.data.CategoryColor as string;
        if (!args.element || !categoryColor) {
            return;
        }
        if (scheduleObj.currentView === 'Agenda') {
            (args.element.firstChild as HTMLElement).style.borderLeftColor = categoryColor;
        } else {
            args.element.style.backgroundColor = categoryColor;
        }
    },
    popupOpen: (args: PopupOpenEventArgs) => {
        if (args.type === 'QuickInfo') {
            let titleObj: TextBox = new TextBox({ placeholder: 'Title' });
            titleObj.appendTo(args.element.querySelector('#title') as HTMLElement);
            let typeObj: DropDownList = new DropDownList({
                dataSource: scheduleObj.getResourceCollections().slice(-1)[0].dataSource as { [key: string]: Object }[],
                placeholder: 'Choose Type',
                fields: { text: 'Name', value: 'Id' },
                index: 0
            });
            typeObj.appendTo(args.element.querySelector('#eventType') as HTMLElement);
            let notesObj: TextBox = new TextBox({ placeholder: 'Notes' });
            notesObj.appendTo(args.element.querySelector('#notes') as HTMLElement);

            let moreDetailsBtn: HTMLButtonElement = args.element.querySelector('#more-details') as HTMLButtonElement;
            if (moreDetailsBtn) {
                let moreObj: Button = new Button({
                    content: 'More Details', cssClass: 'e-flat',
                    isPrimary: args.element.firstElementChild.classList.contains('e-event-popup')
                });
                moreObj.appendTo(moreDetailsBtn);
                moreDetailsBtn.onclick = (e: Event) => { buttonClickActions(e); };
            }
            let addBtn: HTMLButtonElement = args.element.querySelector('#add') as HTMLButtonElement;
            if (addBtn) {
                new Button({ content: 'Add', cssClass: 'e-flat', isPrimary: true }, addBtn);
                addBtn.onclick = (e: Event) => { buttonClickActions(e); };
            }
            let deleteBtn: HTMLButtonElement = args.element.querySelector('#delete') as HTMLButtonElement;
            if (deleteBtn) {
                new Button({ content: 'Delete', cssClass: 'e-flat' }, deleteBtn);
                deleteBtn.onclick = (e: Event) => { buttonClickActions(e); };
            }
        }
    }
});
scheduleObj.appendTo('#Schedule');
<!DOCTYPE html>
<html lang="en">

<head>
    <title>Schedule Typescript Control</title>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta name="description" content="Typescript Schedule Control" />
    <meta name="author" content="Syncfusion" />
    <link href="index.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/29.2.4/ej2-base/styles/material.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/29.2.4/ej2-buttons/styles/material.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/29.2.4/ej2-calendars/styles/material.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/29.2.4/ej2-dropdowns/styles/material.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/29.2.4/ej2-inputs/styles/material.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/29.2.4/ej2-navigations/styles/material.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/29.2.4/ej2-popups/styles/material.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/29.2.4/ej2-schedule/styles/material.css" rel="stylesheet" />
    <script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.19.38/system.js" type="text/javascript"></script>
    <script src="systemjs.config.js" type="text/javascript"></script>

    <style>
        .quick-info-header {
            background-color: white;
            padding: 8px 18px;
        }

        .quick-info-header-content {
            justify-content: flex-end;
            display: flex;
            flex-direction: column;
            padding: 5px 10px 5px;
        }

        .quick-info-title {
            font-weight: 500;
            font-size: 16px;
            letter-spacing: 0.48px;
            height: 22px;
        }

        .duration-text {
            font-size: 11px;
            letter-spacing: 0.33px;
            height: 14px;
        }

        .content-area {
            margin: 0;
            padding: 10px;
            width: 100%;
        }

        .event-content {
            height: 90px;
            display: flex;
            flex-direction: column;
            justify-content: center;
            padding: 0 15px;
        }

        .meeting-type-wrap,
        .meeting-subject-wrap,
        .notes-wrap {
            font-size: 11px;
            color: #666;
            letter-spacing: 0.33px;
            height: 24px;
            padding: 5px;
        }

        .event-content div label {
            display: inline-block;
            min-width: 45px;
            color: #666;
        }

        .event-content div span {
            font-size: 11px;
            color: #151515;
            letter-spacing: 0.33px;
            line-height: 14px;
            padding-left: 8px;
        }

        .cell-footer {
            padding-top: 10px;
        }

        .cell-footer .e-btn {
            background-color: #ffffff;
            border-color: #878787;
            color: #878787;
        }

        .e-quick-popup-wrapper .e-cell-popup .e-popup-content {
            padding: 0 14px;
        }

        .e-quick-popup-wrapper .e-event-popup .e-popup-footer {
            display: block;
        }

        .e-quick-popup-wrapper .e-popup-footer button:first-child {
            margin-right: 5px;
        }
    </style>
<script src="https://cdn.syncfusion.com/ej2/syncfusion-helper.js" type ="text/javascript"></script>
</head>

<body>
    <div class="control-section">
        <div class="content-wrapper">
            <div id="Schedule"></div>
        </div>
    </div>

    <script id="header-template" type="text/x-template">
        <div class="quick-info-header">
            <div class="quick-info-header-content" style="${getHeaderStyles(data)}">
                <div class="quick-info-title">${if (elementType == "cell")}Add Appointment${else}Appointment Details${/if}</div>
                <div class="duration-text">${getHeaderDetails(data)}</div>
            </div>
        </div>
    </script>

    <script id="content-template" type="text/x-template">
        <div class="quick-info-content">
            ${if (elementType == "cell")}
            <div class="e-cell-content">
                <div class="content-area">
                    <input id="title" placeholder="Title" />
                </div>
                <div class="content-area">
                    <input id="eventType" placeholder="Choose Type" />
                </div>
                <div class="content-area">
                    <input id="notes" placeholder="Notes" />
                </div>
            </div>
            ${else}
            <div class="event-content">
                <div class="meeting-type-wrap">
                    <label>Subject</label>:
                    <span>${Subject}</span>
                </div>
                <div class="meeting-subject-wrap">
                    <label>Type</label>:
                    <span>${getEventType(data)}</span>
                </div>
                <div class="notes-wrap">
                    <label>Notes</label>:
                    <span>${Description}</span>
                </div>
            </div>
            ${/if}
        </div>
    </script>

    <script id="footer-template" type="text/x-template">
        <div class="quick-info-footer">
            ${if (elementType == "cell")}
            <div class="cell-footer">
                <button id="more-details">More Details</button>
                <button id="add">Add</button>
            </div>
            ${else}
            <div class="event-footer">
                <button id="delete">Delete</button>
                <button id="more-details">More Details</button>
            </div>
            ${/if}
        </div>
    </script>
</body>

</html>
export let scheduleData: Object[] = [
        {
            RoomId: 10,
            Id: 1,
            Subject: "Board Meeting",
            Description: "Meeting to discuss business goal of 2020.",
            StartTime: "2020-01-05T04:00:00.000Z",
            EndTime: "2020-01-05T05:30:00.000Z"
        },
        {
            RoomId: 8,
            Id: 2,
            Subject: "Training session on JSP",
            Description: "Knowledge sharing on JSP topics.",
            StartTime: "2020-01-07T04:00:00.000Z",
            EndTime: "2020-01-07T05:30:00.000Z"
        },
        {
            RoomId: 3,
            Id: 3,
            Subject: "Sprint Planning with Team members",
            Description: "Planning tasks for sprint.",
            StartTime: "2020-01-09T04:00:00.000Z",
            EndTime: "2020-01-09T05:30:00.000Z"
        },
         {
             RoomId: 2,
             Id: 4,
             Subject: "Meeting with Client",
             Description: "Customer meeting to discuss features.",
             StartTime: "2020-01-11T03:30:00.000Z",
             EndTime: "2020-01-11T05:00:00.000Z"
         },
         {
             RoomId: 5,
             Id: 5,
             Subject: "Support Meeting with Managers",
             Description: "Meeting to discuss support plan.",
             StartTime: "2020-01-06T06:30:00.000Z",
             EndTime: "2020-01-06T08:00:00.000Z"
         },
         {
             RoomId: 1,
             Id: 6,
             Subject: "Client Meeting",
             Description: "Meeting to discuss client requirements.",
             StartTime: "2020-01-08T06:00:00.000Z",
             EndTime: "2020-01-08T07:30:00.000Z"
         },
         {
             RoomId: 7,
             Id: 7,
             Subject: "Appraisal Meeting",
             Description: "Meeting to discuss employee appraisals.",
             StartTime: "2020-01-10T05:30:00.000Z",
             EndTime: "2020-01-10T07:00:00.000Z"
         },
         {
             RoomId: 6,
             Id: 8,
             Subject: "HR Meeting",
             Description: "Meeting to discuss HR plans.",
             StartTime: "2020-01-05T07:30:00.000Z",
             EndTime: "2020-01-05T09:00:00.000Z"
         },
         {
             RoomId: 4,
             Id: 9,
             Subject: "Customer Meeting",
             Description: "Meeting to discuss customer reported issues.",
             StartTime: "2020-01-09T07:00:00.000Z",
             EndTime: "2020-01-09T08:30:00.000Z"
         },
         {
             RoomId: 9,
             Id: 10,
             Subject: "Board Meeting",
             Description: "Meeting to discuss business plans.",
             StartTime: "2020-01-11T07:30:00.000Z",
             EndTime: "2020-01-11T09:00:00.000Z"
         }
    ];