DevExtreme v24.1 is now available.

Explore our newest features/capabilities and share your thoughts with us.

Your search did not match any results.

Angular Scheduler - Virtual Scrolling

With virtual scrolling, you can improve the overall performance of your application and reduce load times when our Scheduler component is bound to a large data set. When virtual scrolling is enabled, our Scheduler only renders visible appointments. When an appointment leaves the viewport, the Scheduler removes it from the DOM.

To enable virtual scrolling mode, set the scrolling.mode property to "virtual".

Virtual scrolling is available for all views except "agenda".

Backend API
<dx-scheduler [height]="730" [currentDate]="currentDate" [dataSource]="appointments" currentView="Timeline" [startDayHour]="8" [endDayHour]="20" [cellDuration]="60" [showAllDayPanel]="false" [groups]="['humanId']" > <dxi-view type="timelineWorkWeek" name="Timeline" groupOrientation="vertical" ></dxi-view> <dxi-view type="workWeek" groupOrientation="vertical"></dxi-view> <dxi-view type="month" groupOrientation="horizontal"></dxi-view> <dxi-resource fieldExpr="humanId" [dataSource]="resources" label="Employee"> </dxi-resource> <dxo-scrolling mode="virtual"> </dxo-scrolling> </dx-scheduler>
import { NgModule, Component, enableProdMode } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { DxSchedulerModule } from 'devextreme-angular'; import { Resource, Appointment, Service } from './app.service'; if (!/localhost/.test(document.location.host)) { enableProdMode(); } @Component({ selector: 'demo-app', templateUrl: `app/app.component.html`, styleUrls: [`app/app.component.css`], providers: [Service], }) export class AppComponent { resources: Resource[]; appointments: Appointment[]; currentDate: Date = new Date(2021, 1, 2); constructor(service: Service) { const startDay = new Date(2021, 1, 1); const endDay = new Date(2021, 1, 28); const startDayHour = 8; const endDayHour = 20; this.appointments = service.generateAppointments(startDay, endDay, startDayHour, endDayHour); this.resources = service.resources; } } @NgModule({ imports: [ BrowserModule, DxSchedulerModule, ], declarations: [AppComponent], bootstrap: [AppComponent], }) export class AppModule { } platformBrowserDynamic().bootstrapModule(AppModule);
::ng-deep .dx-scheduler-timeline .dx-scheduler-date-table .dx-scheduler-cell-sizes-vertical, ::ng-deep .dx-scheduler-date-table .dx-scheduler-cell-sizes-vertical { height: 100px; } ::ng-deep .dx-scheduler-timeline .dx-scheduler-date-table .dx-scheduler-cell-sizes-horizontal, ::ng-deep .dx-scheduler-timeline .dx-scheduler-header-panel .dx-scheduler-cell-sizes-horizontal, ::ng-deep .dx-scheduler-date-table .dx-scheduler-cell-sizes-horizontal { width: 150px; }
import { Injectable } from '@angular/core'; export class Appointment { text: string; startDate: Date; endDate: Date; humanId: number; } export class Resource { id: number; text: string; color: string; } const resources: Resource[] = [{ id: 0, text: 'David Carter', color: '#74d57b', }, { id: 1, text: 'Emma Lewis', color: '#1db2f5', }, { id: 2, text: 'Noah Hill', color: '#f5564a', }, { id: 3, text: 'William Bell', color: '#97c95c', }, { id: 4, text: 'Jane Jones', color: '#ffc720', }, { id: 5, text: 'Violet Young', color: '#eb3573', }, { id: 6, text: 'Samuel Perry', color: '#a63db8', }, { id: 7, text: 'Luther Murphy', color: '#ffaa66', }, { id: 8, text: 'Craig Morris', color: '#2dcdc4', }, { id: 9, text: 'Sandy Wood', color: '#c34cb9', }, { id: 10, text: 'Susan Bennett', color: '#3d44ec', }, { id: 11, text: 'Lilly Barnes', color: '#4ddcca', }, { id: 12, text: 'Marcus Price', color: '#2ec98d', }, { id: 13, text: 'David Stewart', color: '#3ff6ca', }, { id: 14, text: 'Joseph Smith', color: '#f665aa', }, { id: 15, text: 'Carter Wilson', color: '#d1c974', }, { id: 16, text: 'Wyatt Lopez', color: '#ff6741', }, { id: 17, text: 'John Long', color: '#ee53dc', }, { id: 18, text: 'Jack Rivera', color: '#795ac3', }, { id: 19, text: 'Victoria Adams', color: '#ff7d8a', }, { id: 20, text: 'Madison Anderson', color: '#4cd482', }, { id: 21, text: 'Luna Moore', color: '#9d67cc', }, { id: 22, text: 'Michael Bailey', color: '#5ab1ef', }, { id: 23, text: 'Jenny Powell', color: '#68e18f', }, { id: 24, text: 'Daniel Peterson', color: '#4dd155', }, { id: 25, text: 'Gabriel Gray', color: '#ef9e44', }, { id: 26, text: 'Anthony Robinson', color: '#45a5cc', }, { id: 27, text: 'Ellie Tomson', color: '#a067bd', }, { id: 28, text: 'Natalie Adams', color: '#3d44ec', }, { id: 29, text: 'Sofia Green', color: '#4ddcca', }]; const appointmentsText: string[] = [ 'Google AdWords Strategy', 'New Brochures', 'Brochure Design Review', 'Website Re-Design Plan', 'Rollout of New Website and Marketing Brochures', 'Update Sales Strategy Documents', 'Non-Compete Agreements', 'Approve Hiring of John Jeffers', 'Update NDA Agreement', 'Update Employee Files with New NDA', 'Submit Questions Regarding New NDA', 'Submit Signed NDA', 'Review Revenue Projections', 'Comment on Revenue Projections', 'Provide New Health Insurance Docs', 'Review Changes to Health Insurance Coverage', 'Review Training Course for any Ommissions', 'Recall Rebate Form', 'Create Report on Customer Feedback', 'Review Customer Feedback Report', 'Customer Feedback Report Analysis', 'Prepare Shipping Cost Analysis Report', 'Provide Feedback on Shippers', 'Select Preferred Shipper', 'Complete Shipper Selection Form', 'Upgrade Server Hardware', 'Upgrade Personal Computers', 'Upgrade Apps to Windows RT or stay with WinForms', 'Estimate Time Required to Touch-Enable Apps', 'Report on Tranistion to Touch-Based Apps', 'Submit New Website Design', 'Create Icons for Website', 'Create New Product Pages', 'Approve Website Launch', 'Update Customer Shipping Profiles', 'Create New Shipping Return Labels', 'Get Design for Shipping Return Labels', 'PSD needed for Shipping Return Labels', 'Contact ISP and Discuss Payment Options', 'Prepare Year-End Support Summary Report', 'Review New Training Material', 'Distribute Training Material to Support Staff', 'Training Material Distribution Schedule', 'Approval on Converting to New HDMI Specification', 'Create New Spike for Automation Server', 'Code Review - New Automation Server', 'Confirm Availability for Sales Meeting', 'Reschedule Sales Team Meeting', 'Send 2 Remotes for Giveaways', 'Discuss Product Giveaways with Management', 'Replace Desktops on the 3rd Floor', 'Update Database with New Leads', 'Mail New Leads for Follow Up', 'Send Territory Sales Breakdown', 'Territory Sales Breakdown Report', 'Report on the State of Engineering Dept', 'Staff Productivity Report', ]; @Injectable() export class Service { get resources() { return resources; } get appointmentsText() { return appointmentsText; } getRandomDuration(duratiuonState): number { const durationMin = Math.floor((duratiuonState % 23) / 3 + 5) * 15; return durationMin * 60 * 1000; } getRandomText(textIndex): string { return appointmentsText[textIndex % appointmentsText.length]; } filterAppointmentsByTime(appointments, startDayHour, endDayHour): Appointment[] { const result = []; for (let i = 0; i < appointments.length; i++) { const startDate = appointments[i].startDate; const endDate = appointments[i].endDate; if (startDate.getDay() === endDate.getDay() && startDate.getHours() >= startDayHour - 1 && endDate.getHours() <= endDayHour - 1) { result.push(appointments[i]); } } return result; } generateAppointments(startDay, endDay, startDayHour, endDayHour): Appointment[] { const appointments = []; let textIndex = 0; let durationState = 1; const durationIncrement = 19; for (let i = 0; i < resources.length; i++) { let startDate = startDay; while (startDate.getTime() < endDay.getTime()) { durationState += durationIncrement; const endDate = new Date(startDate.getTime() + this.getRandomDuration(durationState)); appointments.push({ text: this.getRandomText(textIndex), startDate, endDate, humanId: resources[i].id, }); textIndex++; durationState += durationIncrement; startDate = new Date(endDate.getTime() + this.getRandomDuration(durationState)); } } return this.filterAppointmentsByTime(appointments, startDayHour, endDayHour); } }
// In real applications, you should not transpile code in the browser. // You can see how to create your own application with Angular and DevExtreme here: // https://js.devexpress.com/Documentation/Guide/Angular_Components/Getting_Started/Create_a_DevExtreme_Application/ const componentNames = [ 'accordion', 'action-sheet', 'autocomplete', 'bar-gauge', 'box', 'bullet', 'button-group', 'button', 'calendar', 'chart', 'check-box', 'circular-gauge', 'color-box', 'context-menu', 'data-grid', 'date-box', 'date-range-box', 'defer-rendering', 'diagram', 'draggable', 'drawer', 'drop-down-box', 'drop-down-button', 'file-manager', 'file-uploader', 'filter-builder', 'form', 'funnel', 'gallery', 'gantt', 'html-editor', 'linear-gauge', 'list', 'load-indicator', 'load-panel', 'lookup', 'map', 'menu', 'multi-view', 'nested', 'number-box', 'pie-chart', 'pivot-grid-field-chooser', 'pivot-grid', 'polar-chart', 'popover', 'popup', 'progress-bar', 'radio-group', 'range-selector', 'range-slider', 'recurrence-editor', 'resizable', 'responsive-box', 'sankey', 'scheduler', 'scroll-view', 'select-box', 'slider', 'sortable', 'sparkline', 'speed-dial-action', 'splitter', 'switch', 'tab-panel', 'tabs', 'tag-box', 'text-area', 'text-box', 'tile-view', 'toast', 'toolbar', 'tooltip', 'tree-list', 'tree-map', 'tree-view', 'validation-group', 'validation-summary', 'validator', 'vector-map', ]; window.exports = window.exports || {}; window.config = { transpiler: 'ts', typescriptOptions: { module: 'system', emitDecoratorMetadata: true, experimentalDecorators: true, }, meta: { 'typescript': { 'exports': 'ts', }, 'devextreme/time_zone_utils.js': { 'esModule': true, }, 'devextreme/localization.js': { 'esModule': true, }, 'devextreme/viz/palette.js': { 'esModule': true, }, '@angular/platform-browser-dynamic': { 'esModule': true, }, '@angular/platform-browser': { 'esModule': true, }, '@angular/core': { 'esModule': true, }, '@angular/common': { 'esModule': true, }, '@angular/common/http': { 'esModule': true, }, '@angular/compiler': { 'esModule': true, }, '@angular/animations': { 'esModule': true, }, '@angular/forms': { 'esModule': true, }, }, paths: { 'npm:': 'https://unpkg.com/', 'bundles:': '../../../../bundles/', }, map: { 'ts': 'npm:plugin-typescript@4.2.4/lib/plugin.js', 'typescript': 'npm:typescript@4.2.4/lib/typescript.js', /* @angular */ '@angular/compiler': 'bundles:@angular/compiler.umd.js', '@angular/platform-browser-dynamic': 'bundles:@angular/platform-browser-dynamic.umd.js', '@angular/core': 'bundles:@angular/core.umd.js', '@angular/core/primitives/signals': 'bundles:@angular/core.primitives.signals.umd.js', '@angular/common': 'bundles:@angular/common.umd.js', '@angular/common/http': 'bundles:@angular/common-http.umd.js', '@angular/platform-browser': 'bundles:@angular/platform-browser.umd.js', '@angular/platform-browser/animations': 'bundles:@angular/platform-browser.umd.js', '@angular/forms': 'bundles:@angular/forms.umd.js', /* devextreme */ 'devextreme': 'npm:devextreme@24.1.7/cjs', '@devextreme/runtime': 'npm:@devextreme/runtime@3.0.13', 'devextreme/bundles/dx.all': 'npm:devextreme@24.1.7/bundles/dx.all.js', 'devextreme-quill': 'npm:devextreme-quill@1.7.1/dist/dx-quill.min.js', 'devexpress-diagram': 'npm:devexpress-diagram@2.2.12', 'devexpress-gantt': 'npm:devexpress-gantt@4.1.56', /* devextreme-angular umd maps */ 'devextreme-angular': 'bundles:devextreme-angular/devextreme-angular.umd.js', 'devextreme-angular/core': 'bundles:devextreme-angular/devextreme-angular-core.umd.js', 'devextreme-angular/http': 'bundles:devextreme-angular/devextreme-angular-http.umd.js', ...componentNames.reduce((acc, name) => { acc[`devextreme-angular/ui/${name}`] = `bundles:devextreme-angular/devextreme-angular-ui-${name}.umd.js`; return acc; }, {}), 'jszip': 'npm:jszip@3.10.1/dist/jszip.min.js', 'tslib': 'npm:tslib@2.6.1/tslib.js', 'rxjs': 'npm:rxjs@7.5.3/dist/bundles/rxjs.umd.js', 'rxjs/operators': 'npm:rxjs@7.5.3/dist/cjs/operators/index.js', 'rrule': 'npm:rrule@2.6.4/dist/es5/rrule.js', 'luxon': 'npm:luxon@1.28.1/build/global/luxon.min.js', 'es6-object-assign': 'npm:es6-object-assign@1.1.0', 'inferno': 'npm:inferno@7.4.11/dist/inferno.min.js', 'inferno-compat': 'npm:inferno-compat/dist/inferno-compat.min.js', 'inferno-create-element': 'npm:inferno-create-element@7.4.11/dist/inferno-create-element.min.js', 'inferno-dom': 'npm:inferno-dom/dist/inferno-dom.min.js', 'inferno-hydrate': 'npm:inferno-hydrate@7.4.11/dist/inferno-hydrate.min.js', 'inferno-clone-vnode': 'npm:inferno-clone-vnode/dist/inferno-clone-vnode.min.js', 'inferno-create-class': 'npm:inferno-create-class/dist/inferno-create-class.min.js', 'inferno-extras': 'npm:inferno-extras/dist/inferno-extras.min.js', // Prettier 'prettier/standalone': 'npm:prettier@2.8.8/standalone.js', 'prettier/parser-html': 'npm:prettier@2.8.8/parser-html.js', }, packages: { 'app': { main: './app.component.ts', defaultExtension: 'ts', }, 'devextreme': { defaultExtension: 'js', }, 'devextreme/events/utils': { main: 'index', }, 'devextreme/events': { main: 'index', }, 'es6-object-assign': { main: './index.js', defaultExtension: 'js', }, 'rxjs': { defaultExtension: 'js', }, 'rxjs/operators': { defaultExtension: 'js', }, }, packageConfigPaths: [ 'npm:@devextreme/*/package.json', 'npm:@devextreme/runtime@3.0.13/inferno/package.json', 'npm:rxjs@7.5.3/package.json', 'npm:rxjs@7.5.3/operators/package.json', 'npm:devexpress-diagram@2.2.12/package.json', 'npm:devexpress-gantt@4.1.56/package.json', ], }; System.config(window.config); // System.import('@angular/compiler').catch(console.error.bind(console));
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" lang="en"> <head> <title>DevExtreme Demo</title> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5.0" /> <link rel="stylesheet" type="text/css" href="https://cdn3.devexpress.com/jslib/24.1.7/css/dx.light.css" /> <script src="https://unpkg.com/core-js@2.6.12/client/shim.min.js"></script> <script src="https://unpkg.com/zone.js@0.13.3/bundles/zone.umd.min.js"></script> <script src="https://unpkg.com/reflect-metadata@0.1.13/Reflect.js"></script> <script src="https://unpkg.com/systemjs@0.21.3/dist/system.js"></script> <script src="config.js"></script> <script> System.import("app").catch(console.error.bind(console)); </script> </head> <body class="dx-viewport"> <div class="demo-container"> <demo-app>Loading...</demo-app> </div> </body> </html>