HelpBot Assistant

How can I help you?

Virtual scrolling in Vue Schedule component

3 Feb 202622 minutes to read

To achieve better performance in the Scheduler when loading a large number of resources and events, we have added virtual scrolling support load a large set of resources and events instantly as you scroll. You can dynamically load large number of resources and events in the Scheduler by setting true to the allowVirtualScrolling property within the view specific settings. The virtual loading of events is possible in Agenda view, by setting allowVirtualScrolling property to true within the agenda view specific settings.

<template>
    <div id='app'>
        <div id='container'>
            <ejs-schedule id='Schedule' ref='ScheduleObj' width='100%' height='550px' :selectedDate='selectedDate'
                :eventSettings='eventSettings' :group='group'>
                <e-views>
                    <e-view option='TimelineMonth' :eventTemplate="'timelineEventTemplate'"
                        :allowVirtualScrolling='virtualScroll' isSelected="true">
                        <template v-slot:timelineEventTemplate="{ data }">
                            <div class='template-wrap' :style='{background: data.PrimaryColor}'>
                                <div class="subject" :style='{background: data.SecondaryColor}'></div>
                            </div>
                        </template>
                    </e-view>
                    <e-view option="TimelineYear" orientation="Vertical"
                        :allowVirtualScrolling="virtualScroll"></e-view>
                </e-views>
                <e-resources>
                    <e-resource field='OwnerId' title='Owner' name='Owners' :allowMultiple='allowMultiple'
                        :dataSource='ownerData' textField='Text' idField='Id' colorField='Color'>
                    </e-resource>
                </e-resources>
            </ejs-schedule>
        </div>
    </div>
</template>
<script setup>
import { provide } from "vue";
import { generateResourceData, generateStaticEvents } from './datasource.js';
import { ScheduleComponent as EjsSchedule, ViewDirective as EView, ViewsDirective as EViews, ResourcesDirective as EResources, ResourceDirective as EResource, TimelineViews, TimelineMonth, TimelineYear, Resize, DragAndDrop } from '@syncfusion/ej2-vue-schedule';

const selectedDate = new Date(2018, 4, 1);
const allowMultiple = true;
const virtualScroll = true;
const group = {
    byGroupID: false,
    resources: ['Owners']
};
const ownerData = generateResourceData(1, 300, 'Resource');
const eventSettings = { dataSource: generateStaticEvents(new Date(2018, 4, 1), 300, 12) };

provide('schedule', [TimelineViews, TimelineMonth, TimelineYear, Resize, DragAndDrop]);

</script>
<style>
@import "../node_modules/@syncfusion/ej2-base/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-vue-buttons/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-vue-calendars/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-vue-dropdowns/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-vue-inputs/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-vue-navigations/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-vue-popups/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-vue-schedule/styles/material3.css";

.e-schedule .e-timeline-month-view .template-wrap .subject {
    padding: 10px 25px;
}

.e-schedule .e-timeline-year-view .template-wrap .subject {
    padding: 1px 25px;
}

.e-schedule .e-more-event-popup .template-wrap .subject {
    padding: 0px 25px;
}

.e-schedule .template-wrap {
    width: 100%;
}

.e-schedule .e-timeline-month-view .e-resource-left-td {
    width: 150px;
}
</style>
<template>
    <div id='app'>
        <div id='container'>
            <ejs-schedule id='Schedule' ref='ScheduleObj' width='100%' height='550px' :selectedDate='selectedDate'
                :eventSettings='eventSettings' :group='group'>
                <e-views>
                    <e-view option='TimelineMonth' :eventTemplate="'timelineEventTemplate'"
                        :allowVirtualScrolling='virtualScroll' isSelected="true">
                        <template v-slot:timelineEventTemplate="{ data }">
                            <div class='template-wrap' style='{background: data.PrimaryColor}'>
                                <div class="subject" style='{background: data.SecondaryColor};'></div>
                            </div>
                        </template>
                    </e-view>
                    <e-view option="TimelineYear" orientation="Vertical"
                        :allowVirtualScrolling="virtualScroll"></e-view>
                </e-views>
                <e-resources>
                    <e-resource field='OwnerId' title='Owner' name='Owners' :allowMultiple='allowMultiple'
                        :dataSource='ownerData' textField='Text' idField='Id' colorField='Color'>
                    </e-resource>
                </e-resources>
            </ejs-schedule>
        </div>
    </div>
</template>
<script>
import { generateResourceData, generateStaticEvents } from './datasource.js';
import { ScheduleComponent, ViewDirective, ViewsDirective, ResourcesDirective, ResourceDirective, TimelineViews, TimelineMonth, TimelineYear, Resize, DragAndDrop } from '@syncfusion/ej2-vue-schedule';

export default {
    name: "App",
    components: {
        "ejs-schedule": ScheduleComponent,
        "e-views": ViewsDirective,
        "e-view": ViewDirective,
        "e-resources": ResourcesDirective,
        "e-resource": ResourceDirective
    },
    data: function () {
        return {
            selectedDate: new Date(2018, 4, 1),
            allowMultiple: true,
            virtualScroll: true,
            group: {
                byGroupID: false,
                resources: ['Owners']
            },
            ownerData: generateResourceData(1, 300, 'Resource'),
            eventSettings: { dataSource: generateStaticEvents(new Date(2018, 4, 1), 300, 12) }
        }
    },
    provide: {
        schedule: [TimelineViews, TimelineMonth, TimelineYear, Resize, DragAndDrop]
    }
}
</script>
<style>
@import "../node_modules/@syncfusion/ej2-base/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-vue-buttons/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-vue-calendars/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-vue-dropdowns/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-vue-inputs/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-vue-navigations/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-vue-popups/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-vue-schedule/styles/material3.css";

.e-schedule .e-timeline-month-view .template-wrap .subject {
    padding: 10px 25px;
}

.e-schedule .e-timeline-year-view .template-wrap .subject {
    padding: 1px 25px;
}

.e-schedule .e-more-event-popup .template-wrap .subject {
    padding: 0px 25px;
}

.e-schedule .template-wrap {
    width: 100%;
}

.e-schedule .e-timeline-month-view .e-resource-left-td {
    width: 150px;
}
</style>

For now, the virtual loading of resources and events is not supported in MonthAgenda, Year and TimelineYear (Horizontal Orientation) views.

Enabling lazy loading for appointments

Lazy loading complements virtual scrolling by fetching appointment data from the server on demand, instead of loading all events upfront.

How lazy loading works

When lazy loading is enabled:

  • Initially, events are fetched only for the visible resources and current date range.
  • As the user scrolls, the Scheduler issues additional server requests.
  • Each request includes:
    • The resource IDs currently in view
    • The active date range
  • The server returns only the events relevant to those resources and dates.

This ensures optimal network usage and faster initial load times.


Enabling lazy loading

To enable lazy loading, set the enableLazyLoading property to true in the view configuration.

<template>
    <div id='app'>
        <div id='container'>
            <ejs-schedule ref='ScheduleObj' width='100%' height='550px' :selectedDate='selectedDate' readonly='true'
                :eventSettings='eventSettings' :group='group'>
                <e-views>
                    <e-view option='TimelineMonth' :enableLazyLoading='enableLazyLoad' isSelected="true"></e-view>
                </e-views>
                <e-resources>
                    <e-resource field='ResourceId' title='Resource' name='Resources' :dataSource='resourceData'
                        textField='Text' idField='Id' colorField='Color'>
                    </e-resource>
                </e-resources>
            </ejs-schedule>
        </div>
    </div>
</template>
<script setup>
import { provide } from "vue";
import { generateResourceData } from './datasource.js';
import { ScheduleComponent as EjsSchedule, ViewDirective as EView, ViewsDirective as EViews, ResourcesDirective as EResources, ResourceDirective as EResource, TimelineMonth } from '@syncfusion/ej2-vue-schedule';
import { DataManager, WebApiAdaptor } from '@syncfusion/ej2-data';

const dataManager = new DataManager({
    url: 'https://services.syncfusion.com/vue/production/api/VirtualEventData',
    adaptor: new WebApiAdaptor,
    crossDomain: true
});

const selectedDate = new Date(2023, 3, 1);
const enableLazyLoad = true;
const group = {
    resources: ['Resources']
};
const resourceData = generateResourceData(1, 1000, 'Resource');
const eventSettings = { dataSource: dataManager };

provide('schedule', [TimelineMonth]);

</script>
<style>
@import "../node_modules/@syncfusion/ej2-base/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-vue-buttons/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-vue-calendars/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-vue-dropdowns/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-vue-inputs/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-vue-navigations/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-vue-popups/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-vue-schedule/styles/material3.css";
</style>
<template>
    <div id='app'>
        <div id='container'>
            <ejs-schedule ref='ScheduleObj' width='100%' height='550px' :selectedDate='selectedDate' readonly='true'
                :eventSettings='eventSettings' :group='group'>
                <e-views>
                    <e-view option='TimelineMonth' :enableLazyLoading='enableLazyLoad' isSelected="true"></e-view>
                </e-views>
                <e-resources>
                    <e-resource field='ResourceId' title='Resource' name='Resources' :dataSource='resourceData'
                        textField='Text' idField='Id' colorField='Color'>
                    </e-resource>
                </e-resources>
            </ejs-schedule>
        </div>
    </div>
</template>
<script>
import { generateResourceData } from './datasource.js';
import { ScheduleComponent, ViewDirective, ViewsDirective, ResourcesDirective, ResourceDirective, TimelineMonth } from '@syncfusion/ej2-vue-schedule';
import { DataManager, WebApiAdaptor } from '@syncfusion/ej2-data';

const dataManager = new DataManager({
    url: 'https://services.syncfusion.com/vue/production/api/VirtualEventData',
    adaptor: new WebApiAdaptor,
    crossDomain: true
});

export default {
    name: "App",
    components: {
        "ejs-schedule": ScheduleComponent,
        "e-views": ViewsDirective,
        "e-view": ViewDirective,
        "e-resources": ResourcesDirective,
        "e-resource": ResourceDirective
    },
    data: function () {
        return {
            selectedDate: new Date(2023, 3, 1),
            enableLazyLoad: true,
            group: {
                resources: ['Resources']
            },
            resourceData: generateResourceData(1, 1000, 'Resource'),
            eventSettings: { dataSource: dataManager }
        }
    },
    provide: {
        schedule: [TimelineMonth]
    }
}
</script>
<style>
@import "../node_modules/@syncfusion/ej2-base/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-vue-buttons/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-vue-calendars/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-vue-dropdowns/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-vue-inputs/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-vue-navigations/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-vue-popups/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-vue-schedule/styles/material3.css";
</style>

Here’s the server-side controller code that retrieves appointment data based on the resource IDs provided as query parameters:

using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System;
using Microsoft.EntityFrameworkCore;
using System.Linq;
using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.OData.Query;

namespace LazyLoadingServices.Controllers
{
    public class VirtualEventDataController : Controller
    {
        private readonly EventsContext dbContext;

        [HttpGet]
        [EnableQuery]
        [Route("api/VirtualEventData")]
        public IActionResult GetData([FromQuery] Params param)
        {
            IQueryable<EventData> query = dbContext.Events;
            // Filter the appointment data based on the ResourceId query params.
            if (!string.IsNullOrEmpty(param.ResourceId))
            {
                string[] resourceId = param.ResourceId.Split(',');
                query = query.Where(data => resourceId.Contains(data.ResourceId.ToString()));
            }
            return Ok(query.ToList());
        }
    }
    public class Params
    {
        public DateTime? StartDate { get; set; }
        public DateTime? EndDate { get; set; }
        public string ResourceId { get; set; }
    }
}

Note:

  • The property will be effective, when large number of resources and appointments bound to the Scheduler.
  • This property is applicable only when resource grouping is enabled in Scheduler.

See Also

For a complete overview of resource scheduling features, visit the Vue Scheduler feature tour page. Explore live examples at Vue Scheduler example to knows how to present and manipulate data.