Overview

The active areas feature of the Angular Scheduler component allows adding different types of action buttons to the Scheduler row header columns.

In this tutorial, we explore the following options:

  • context menu that is activated using a hover icon

  • hyperlink-like button with custom text

  • custom action icons

In addition to the active areas, it is also possible to add custom Angular components to the Scheduler row headers:

The project includes a trial version of DayPilot Pro for JavaScript (see License below).

License

Licensed for testing and evaluation purposes. Please see the license agreement included in the sample project. You can use the source code of the tutorial if you are a licensed user of DayPilot Pro for JavaScript.

Context Menu Triggered by Active Area

Angular Scheduler Row Header Actions - Hover Icon with Context Menu

The first example uses a hover active area (visibility: "Hover") to show a button that activates a context menu:

Angular Scheduler Row Header Actions - Open Context Menu

The active area is added to the first row header column using onBeforeHeaderRender event handler.

scheduler.component.ts

onBeforeRowHeaderRender: args => {

  args.row.columns[0].areas = [
    {
      right: 5,
      top: 8,
      height: 20,
      width: 20,
      padding: 2,
      visibility: "Hover",
      backColor: "#666666",
      fontColor: "#ffffff",
      symbol: "/icons/daypilot.svg#threedots-v",
      action: "ContextMenu",
      menu: this.rowMenu,
      style: "cursor: pointer; border-radius: 50%;"
    }
  ];
  
  // ...

}

This active area displays the “vertical three dots” icon from the daypilot.svg icon bundle.

Context menu definition:

rowMenu: DayPilot.Menu = new DayPilot.Menu({
  items: [
    {
      text: "Open",
      onClick: args => {
        const row = args.source;
        DayPilot.Modal.alert("Opening: " + row.data.myData);
      }
    },
    { 
      text: "Delete",
      onClick: async args => {
        const row = args.source;
        const modal = await DayPilot.Modal.confirm("Do you want to delete this row? " + row.data.myData);
        this.scheduler.control.rows.remove(row);
      }
    }
  ]
});

The onClick event handler of the “Open” menu items shows a simple modal dialog with custom row data.

The “Delete” menu item asks for a confirmation and deletes the row from the Scheduler using the rows.remove() method.

Hyperlink Button

Angular Scheduler Row Header Actions - Hyperlink Button

Another type of active area adds a custom text that is styled as a hyperlink.

The onClick event handler of the active area lets you specify the click action. You can use it to call Angular methods available in your component.

onBeforeRowHeaderRender: args => {

  // ...

  if (args.row.columns[1]) {
    args.row.columns[1].areas = [
      {
        left: 0,
        right: 0,
        top: 0,
        bottom: 0,
        html: "Details...",
        verticalAlignment: "center",
        horizontalAlignment: "center",
        style: "cursor: pointer; text-decoration: underline; color: #cc0000;",
        onClick: (clickArgs: any) => {
          const row = clickArgs.source;
          DayPilot.Modal.alert("You clicked a row with the following data: " + row.data.myData);
        }
      }
    ];
  }

}

Multiple Action Icons in a Cell

Angular Scheduler Row Header Actions - Icon with Custom Action

The last column displays three icons with different colors and custom actions.

onBeforeRowHeaderRender: args => {

  // ...

  if (args.row.columns[2]) {
    args.row.columns[2].areas = [
      {
        left: 5,
        top: 8,
        width: 20,
        height: 20,
        backColor: "#3498db",
        fontColor: "#ffffff",
        symbol: "/icons/daypilot.svg#plus-2",
        padding: 2,
        style: "cursor: pointer; border-radius: 50%;",
        toolTip: "Blye",
        onClick: (clickArgs: any) => {
          const row = clickArgs.source;
          DayPilot.Modal.alert("You clicked a blue icon: " + row.data.myData);
        }
      },
      {
        left: 30,
        top: 8,
        width: 20,
        height: 20,
        backColor: "#2ecc71",
        fontColor: "#ffffff",
        symbol: "/icons/daypilot.svg#minichevron-down-2",
        padding: 2,
        style: "cursor: pointer; border-radius: 50%;",
        toolTip: "Green",
        onClick: (clickArgs: any) => {
          const row = clickArgs.source;
          DayPilot.Modal.alert("You clicked a green icon: " + row.data.myData);
        }
      },
      {
        left: 55,
        top: 8,
        width: 20,
        height: 20,
        backColor: "#ffd551",
        fontColor: "#ffffff",
        symbol: "/icons/daypilot.svg#x-2",
        padding: 2,
        style: "cursor: pointer; border-radius: 50%;",
        toolTip: "Yellow",
        onClick: (clickArgs: any) => {
          const row = clickArgs.source;
          DayPilot.Modal.alert("You clicked a yellow icon: " + row.data.myData);
        }
      },
    ];
  }

}

Full Source Code

Here is the full source code of our Angular Scheduler component configured to display different types of action icons, buttons, and hyperlinks in the row headers (scheduler.component.ts);

import {Component, ViewChild, AfterViewInit} from '@angular/core';
import {DayPilot, DayPilotModule, DayPilotSchedulerComponent} from 'daypilot-pro-angular';
import {DataService} from './data.service';

@Component({
  selector: 'scheduler-component',
  standalone: true,
  imports: [DayPilotModule],
  providers: [DataService],
  template: `<daypilot-scheduler [config]="config" [events]="events" #scheduler></daypilot-scheduler>`,
  styles: [``]
})
export class SchedulerComponent implements AfterViewInit {

  @ViewChild('scheduler')
  scheduler!: DayPilotSchedulerComponent;

  events: DayPilot.EventData[] = [];

  rowMenu: DayPilot.Menu = new DayPilot.Menu({
    items: [
      {
        text: "Open",
        onClick: args => {
          const row = args.source;
          DayPilot.Modal.alert("Opening: " + row.data.myData);
        }
      },
      { text: "Delete",
        onClick: async args => {
          const row = args.source;
          const modal = await DayPilot.Modal.confirm("Do you want to delete this row? " + row.data.myData);
          this.scheduler.control.rows.remove(row);
        }
      }
    ]
  });

  config: DayPilot.SchedulerConfig = {
    timeHeaders: [{groupBy: "Month"},{groupBy: "Day", format: "d"}],
    scale: "Day",
    days: DayPilot.Date.today().daysInMonth(),
    startDate: DayPilot.Date.today().firstDayOfMonth(),
    timeRangeSelectedHandling: "Enabled",
    onTimeRangeSelected: async (args) => {
      const scheduler = args.control;
      const modal = await DayPilot.Modal.prompt("Create a new event:", "Event 1");
      scheduler.clearSelection();
      if (modal.canceled) { return; }
      scheduler.events.add({
        start: args.start,
        end: args.end,
        id: DayPilot.guid(),
        resource: args.resource,
        text: modal.result
      });
    },
    rowHeaderColumns: [
      { text: "Name", width: 140 },
      { text: "Click" },
      { text: "Actions"}
    ],
    treeEnabled: true,
    onBeforeRowHeaderRender: args=> {
      args.row.columns[0].areas = [
        {
          right: 5,
          top: 8,
          height: 20,
          width: 20,
          padding: 2,
          visibility: "Hover",
          backColor: "#666666",
          fontColor: "#ffffff",
          symbol: "/icons/daypilot.svg#threedots-v",
          action: "ContextMenu",
          menu: this.rowMenu,
          style: "cursor: pointer; border-radius: 50%;"
        }
      ];

      if (args.row.columns[1]) {
        args.row.columns[1].areas = [
          {
            left: 0,
            right: 0,
            top: 0,
            bottom: 0,
            html: "Details...",
            verticalAlignment: "center",
            horizontalAlignment: "center",
            style: "cursor: pointer; text-decoration: underline; color: #cc0000;",
            onClick: (clickArgs: any) => {
              const row = clickArgs.source;
              DayPilot.Modal.alert("You clicked a row with the following data: " + row.data.myData);
            }
          }
        ];
      }

      if (args.row.columns[2]) {
        args.row.columns[2].areas = [
          {
            left: 5,
            top: 8,
            width: 20,
            height: 20,
            backColor: "#3498db",
            fontColor: "#ffffff",
            symbol: "/icons/daypilot.svg#plus-2",
            padding: 2,
            style: "cursor: pointer; border-radius: 50%;",
            toolTip: "Blye",
            onClick: (clickArgs: any) => {
              const row = clickArgs.source;
              DayPilot.Modal.alert("You clicked a blue icon: " + row.data.myData);
            }
          },
          {
            left: 30,
            top: 8,
            width: 20,
            height: 20,
            backColor: "#2ecc71",
            fontColor: "#ffffff",
            symbol: "/icons/daypilot.svg#minichevron-down-2",
            padding: 2,
            style: "cursor: pointer; border-radius: 50%;",
            toolTip: "Green",
            onClick: (clickArgs: any) => {
              const row = clickArgs.source;
              DayPilot.Modal.alert("You clicked a green icon: " + row.data.myData);
            }
          },
          {
            left: 55,
            top: 8,
            width: 20,
            height: 20,
            backColor: "#ffd551",
            fontColor: "#ffffff",
            symbol: "/icons/daypilot.svg#x-2",
            padding: 2,
            style: "cursor: pointer; border-radius: 50%;",
            toolTip: "Yellow",
            onClick: (clickArgs: any) => {
              const row = clickArgs.source;
              DayPilot.Modal.alert("You clicked a yellow icon: " + row.data.myData);
            }
          },
        ];
      }

    }
  };

  constructor(private ds: DataService) {
  }

  ngAfterViewInit(): void {
    this.ds.getResources().subscribe(result => this.config.resources = result);

    const from = this.scheduler.control.visibleStart();
    const to = this.scheduler.control.visibleEnd();
    this.ds.getEvents(from, to).subscribe(result => {
      this.events = result;
    });
  }

}

The DataService class loads sample data. Note that the items of the resources array contain a custom data field (myData).

You can find the source code in the data.service.ts file:

import {Injectable} from '@angular/core';
import {DayPilot} from 'daypilot-pro-angular';
import {HttpClient} from '@angular/common/http';
import {Observable} from 'rxjs';

@Injectable()
export class DataService {

  resources: DayPilot.ResourceData[] = [
    {
      name: 'Group A', id: 'GA', myData: "Group A Details", expanded: true, children: [
        {name: 'Resource 1', id: 'R1', myData: "Resource 1 Details"},
        {name: 'Resource 2', id: 'R2', myData: "Resource 2 Details"},
        {name: 'Resource 3', id: 'R3', myData: "Resource 3 Details"},
        {name: 'Resource 4', id: 'R4', myData: "Resource 4 Details"}
      ]
    },
    {
      name: 'Group B', id: 'GB', myData: "Group B Details",  expanded: true, children: [
        {name: 'Resource 5', id: 'R5', myData: "Resource 5 Details"},
        {name: 'Resource 6', id: 'R6', myData: "Resource 6 Details"},
        {name: 'Resource 7', id: 'R7', myData: "Resource 7 Details"},
        {name: 'Resource 8', id: 'R8', myData: "Resource 8 Details"}
      ]
    }
  ];

  events: DayPilot.EventData[] = [
    {
      id: '1',
      resource: 'R1',
      start: '2025-10-03',
      end: '2025-10-08',
      text: 'Scheduler Event 1',
      barColor: '#e69138'
    },
    {
      id: '2',
      resource: 'R3',
      start: '2025-10-02',
      end: '2025-10-05',
      text: 'Scheduler Event 2',
      barColor: '#6aa84f'
    },
    {
      id: '3',
      resource: 'R3',
      start: '2025-10-06',
      end: '2025-10-09',
      text: 'Scheduler Event 3',
      barColor: '#3c78d8'
    }
  ];

  constructor(private http: HttpClient) {
  }

  getEvents(from: DayPilot.Date, to: DayPilot.Date): Observable<any[]> {

    // simulating an HTTP request
    return new Observable(observer => {
      setTimeout(() => {
        observer.next(this.events);
      }, 200);
    });

    // return this.http.get("/api/events?from=" + from.toString() + "&to=" + to.toString());
  }

  getResources(): Observable<any[]> {

    // simulating an HTTP request
    return new Observable(observer => {
      setTimeout(() => {
        observer.next(this.resources);
      }, 200);
    });

    // return this.http.get("/api/resources");
  }

}

To download the complete Angular project, please see the link at the top of the tutorial.