Making the syncfusion scheduler component your own

Eveline Coene
6 min readMar 27, 2021

I needed a quick scheduling component that I could easily customize for a project I was working on. The scheduler component was a great choice because it does a lot of work for you, because you can overwrite a lot of options and really make it yours. However, there were some things that I didn’t find how to customize immediately. These are the experiences I will share with you.

Add syncfusion component

Before I can explain how I customized the scheduler component to you, you have to install the standard one.

Step 1:

npm install @syncfusion/ej2-angular-schedule --save

Step 2: Register the schedule module in your app.module.ts file.

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
// import the ScheduleModule for the Schedule component
import { ScheduleModule } from '@syncfusion/ej2-angular-schedule';
import { AppComponent } from './app.component';

@NgModule({
//declaration of ej2-angular-schedule module into NgModule
imports: [ BrowserModule, ScheduleModule ],
declarations: [ AppComponent ],
bootstrap: [ AppComponent ]
})
export class AppModule { }

Step 3: Add css reference in your [src/styles/styles.css]

@import '../node_modules/@syncfusion/ej2-base/styles/material.css'; @import '../node_modules/@syncfusion/ej2-buttons/styles/material.css'; @import '../node_modules/@syncfusion/ej2-calendars/styles/material.css'; @import '../node_modules/@syncfusion/ej2-dropdowns/styles/material.css'; @import '../node_modules/@syncfusion/ej2-inputs/styles/material.css'; @import '../node_modules/@syncfusion/ej2-lists/styles/material.css'; @import '../node_modules/@syncfusion/ej2-popups/styles/material.css'; @import '../node_modules/@syncfusion/ej2-navigations/styles/material.css'; @import '../node_modules/@syncfusion/ej2-angular-schedule/styles/material.css';

Step 4: Initialize the schedule component

import { Component } from '@angular/core';
import { DayService, WeekService, WorkWeekService, MonthService, AgendaService } from '@syncfusion/ej2-angular-schedule';

@Component({
selector: 'app-root',
providers: [DayService, WeekService, WorkWeekService, MonthService, AgendaService],
// specifies the template string for the Schedule component
template: `<ejs-schedule> </ejs-schedule>`
})
export class AppComponent { }

Customizations

Quick popup customization

I only wanted to change the color of the quick popup. And didn’t want to have to overwrite the whole template to change this one thing.

.e-quick-popup-wrapper .e-event-popup .e-popup-header {background-color: var(--color-primary);}

Editor window customization

The second thing I wanted to change was the width of the editor popup, but changing the width on the editor template doesn’t work. So what you need to do is overwrite the dialog class with !important.

.e-popup.e-popup-open.e-dialog{ width: 600px !important; }

Add data to appointments manually

For most fields, you can just add the e-field class and the scheduler component processes the field value internally. But it doesn’t work with some components like the chip component from Angular Material.

You place the fields you want to show in the editor window in a template.

<ejs-schedule #scheduleObj>
<ng-template #editorTemplate let-data>
//fields
</ng-template>
</ejs-schedule>

For my project, I wanted to pass a list of chosen objects to the server. To do that you need to define the attribute in your component.

When you open the popup, you need to set the attribute to the correct value.

When you close the popup, you need to check if the data is given (this means you clicked on save) and if it is then you need to set the data you want to send to the server.

If you receive an Array of objects from the backend, you need to give an array of objects back to the backend. Otherwise, you get a problem when you drag and drop the appointment, because then you only change the start and end time. You get a problem when drag-drop sends objects and a normal edit sends only ids.

public chosenGroups: Group[]onPopupOpen(args:PopupOpenEventArgs): void {  if (args.type === 'Editor') {
this.chosenGroups = (
((args.data) as { [key: string]: Group[] }).groups
) || [];
}
onPopupClose(args: PopupCloseEventArgs): void { if (args.type === 'Editor' && !isNullOrUndefined(args.data)) { ( (args.data) as { [key: string]: Object })
.groups = this.chosenGroups;
}}

Back to the quick popup

I wanted to add extra data when saving an appointment via the quick popup. The data didn’t need to be visible on the popup so I didn’t change the default template.

In my application, you can get a calendar from different people. And I wanted to add which calendar you were currently viewing.

So I added the data when I closed the QuickInfo.

onPopupClose(args: PopupCloseEventArgs): void {  if (args.data && args.type === 'QuickInfo') {    if (args.data['groups'] == undefined) {      ( (args.data) as { [key: string]: Object }).groups =
new Array(new Group(this.id));
this.chosenGroups = new Array(this.groups
.find(group => group.id === this.id))
}
}
}

You can also achieve the same result by putting the data in the URL but in my case, I wanted to see a calendar from a certain person and be able to change his appointment to another person (making it disappear from their roster) or add multiple people. So in my case was placing the data in the URL not an option.

Making autocomplete visible

Nice component right? The autocomplete wasn’t visible on the editor window. The z-index needs to be adjusted on different classes to make it visible because the popup has a dynamic z-index that looks for the highest z-index on the page and sets the highest z-index + 1.

The only way to overwrite this is with !important because you can’t get to the popup child component in your scheduler component.

To change the z-index of the autocomplete you need to define the z-index on the cdk-overlay-container class.

.e-dlg-overlay { z-index:1000 !important; }div.e-dlg-container.e-dlg-center-center.e-schedule-dialog-container{z-index:1002 !important;}#_dialog_wrapper { z-index:1002 !important; }.cdk-overlay-container { z-index: 1004; }

Learn how to make this component on this material.angular.io website:

Server side

To send data to the server you need to use a syncfusion datamanager an adapter. I used the UrlAdaptor. This allows you to define two different URLs, one for getting your appointments and one for updating them.

The datamanager sends a post request for the get and the update. So you need to adjust your backend to the wishes of the frontend component.

this.datamanager = new DataManager({  url: `${this.env.apiUrl}/lessons/${this.planningId}`,  crudUrl: this.env.apiUrl + '/lessons/update/' + this.planningId,  adaptor: new UrlAdaptor,  crossDomain: true})this.eventSettings = {  dataSource: this.datamanager,}

In your html file:

<ejs-schedule #scheduleObj [eventSettings]="eventSettings">
</ejs-schedule>

For your update you get a batchrequest. This contains 5 attributes

Object[] added, Object[] changed, Object[] deleted, localDateTime startDate and localDateTime endDate.

You can assume you either get added, changed or deleted, never two different actions at the same time. So you need to check which attribute has an object. The delete can contain multiple objects.

@PostMapping("/update/{planningId}")
public LessonBlockDto updateLessonBlock(
@PathVariable String planningId,
@RequestBody ScheduleDto batchRequest){

if ( batchRequest.getAdded().length!=0 ||
batchRequest.getChanged().length!=0 ) {
LessonBlockDto updateDto = batchRequest.getAdded().length!=0
? batchRequest.getAdded()[0] : batchRequest.getChanged()[0];

LessonBlock entity =
modelMapper.map(updateDto, LessonBlock.class);

return planningService.updateLessonBlock(entity, planningId)
.map(e -> modelMapper.map(e, LessonBlockDto.class));
} else if(batchRequest.getDeleted().length!=0) {

for (LessonBlockDeleteDto lessonBlockDeleteDto :
batchRequest.getDeleted()) {

planningService.delete(lessonBlockDeleteDto.getId()
, planningId);
}
return modelMapper.map(
batchRequest.getDeleted()[0],LessonBlockDto.class));
}
return null;
}

Conclusion

The scheduler component from syncfusion is an amazing component that can save you a lot of time. But your backend might need to adapt to the way the Schedule component of SyncFusion saves/fetches data. The main difficulty I found was when I wanted to change something from the popup because it was integrated into the scheduler. I couldn’t adjust it the way I normally would when I made the popup component myself. Luckily you can overwrite a lot with !important. In the end, I could make all the changes I wanted except setting the days on the y -axis and hours on the x-axis. But who knows maybe this will be possible on the next version?

Sources:

--

--