Features

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.

Event Context Menu

angular scheduler dynamic context menu enabled

The event context menu is defined using the contextMenu property of the config object.

Our example menu has a “Delete” item that removes the selected event from the Angular Scheduler UI and four items for changing the event color (blue, green, yellow, red).

config: DayPilot.SchedulerConfig = {
  contextMenu: new DayPilot.Menu({
    items: [
      { text: "Delete", onClick: (args) => { this.scheduler.control.events.remove(args.source); } },
      { text: "-"},
      {
        text: "Blue",
        icon: "icon icon-blue",
        tags: { color: "#1155cc" },
        onClick: args => { this.updateColor(args.source, args.item.tags.color); }
      },
      {
        text: "Green",
        icon: "icon icon-green",
        tags: { color: "#6aa84f" },
        onClick: args => { this.updateColor(args.source, args.item.tags.color); }
      },
      {
        text: "Yellow",
        icon: "icon icon-yellow",
        tags: { color: "#f1c232" },
        onClick: args => { this.updateColor(args.source, args.item.tags.color); }
      },
      {
        text: "Red",
        icon: "icon icon-red",
        tags: { color: "#cc0000" },
        onClick: args => { this.updateColor(args.source, args.item.tags.color); }
      },
    ],
    onShow: args => {
      let e = args.source;

      // disable deleting for events that start in the past
      if (e.start() < DayPilot.Date.today()) {
        args.menu.items[0].disabled = true;
      }
      else {
        args.menu.items[0].disabled = false;
      }
    }
  }),

  // ...

};

The updateColor method changes the bar color of the selected Scheduler event:

updateColor(e: DayPilot.Event, color: string): void {
  e.data.barColor = color;
  this.scheduler.control.events.update(e);
}

Dynamic Modification of the Event Context Menu

angular scheduler dynamic context menu disabled

You can modify the context menu properties upon invocation by handling the onShow event handler.

This event handler checks the event start date. If the event starts before today, the first menu item ("Delete" action) is disabled.

config: DayPilot.SchedulerConfig = {
  contextMenu: new DayPilot.Menu({
    items: [
      { text: "Delete", onClick: (args) => { this.scheduler.control.events.remove(args.source); } },
      // ...
    ],
    onShow: args => {
      let e = args.source;

      // disable deleting for events that start in the past
      if (e.start() < DayPilot.Date.today()) {
        args.menu.items[0].disabled = true;
      }
      else {
        args.menu.items[0].disabled = false;
      }
    }
  }),
  // ...
}

Dynamic Row Header Context Menu

The same method can be used to customize the row header context menu depending on the row. This example displays a different menu for resource groups and resources.

Context menu for parent rows (groups):

angular scheduler dynamic context menu group

Context menu for resources (rows without children):

angular scheduler dynamic context menu resource

In this example, we have defined two different menu item arrays. We use the onShow event handler to set the items property as needed.

The implementation of the “Details” menu item is different for each row type:

  • The group details modal dialog contains only the group name.

  • The resource details modal dialog lets you edit the resource name and capacity.

config: DayPilot.SchedulerConfig = {
  contextMenuResource: new DayPilot.Menu({
    onShow: args => {
      let hasChildren = args.source.children().length > 0;
      if (hasChildren) {
        args.menu.items = this.menuItemsGroup;
      }
      else {
        args.menu.items = this.menuItemsResource;
      }
    }
  })
}

menuItemsGroup: DayPilot.MenuItemData[] = [
  {
    text: "Group Details",
    onClick: async args => {
      const form = [
        {name: "Name", id: "name"},
      ];

      const row = args.source;

      const modal = await DayPilot.Modal.form(form, row.data);

      if (modal.canceled) {
        return;
      }

      this.scheduler.control.rows.update(modal.result);

    }
  }
];

menuItemsResource: DayPilot.MenuItemData[] = [
  {
    text: "Resource Details",
    onClick: async args => {
      const form = [
        {name: "Name", id: "name"},
        {name: "Capacity", id: "capacity"},
      ];

      const row = args.source;

      const modal = await DayPilot.Modal.form(form, row.data);

      if (modal.canceled) {
        return;
      }

      this.scheduler.control.rows.update(modal.result);
    }
  }
];

Source Code

Here is the full source code of the customized Angular Scheduler component that uses dynamically-modified context menus (scheduler.component.ts):

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

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

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

  events: DayPilot.EventData[] = [];

  config: DayPilot.SchedulerConfig = {
    timeHeaders: [
      {groupBy:"Month"},
      {groupBy:"Day",format:"d"}
    ],
    rowHeaderColumns: [
      {title: "Name"},
      {title: "Capacity", display: "capacity"}
    ],
    scale: "Day",
    days: 31,
    startDate: "2024-07-01",
    treeEnabled: true,
    separators: [
      {color: "red", location: DayPilot.Date.today() }
    ],
    onTimeRangeSelected: async (args) => {
      const dp = args.control;
      const modal = await DayPilot.Modal.prompt("Create a new event:", "Event 1");
      dp.clearSelection();
      if (!modal.result) { return; }
      dp.events.add({
        start: args.start,
        end: args.end,
        id: DayPilot.guid(),
        resource: args.resource,
        text: modal.result
      });

    },
    contextMenu: new DayPilot.Menu({
      items: [
        { text: "Delete", onClick: (args) => { this.scheduler.control.events.remove(args.source); } },
        { text: "-"},
        {
          text: "Blue",
          icon: "icon icon-blue",
          tags: { color: "#1155cc" },
          onClick: args => { this.updateColor(args.source, args.item.tags.color); }
        },
        {
          text: "Green",
          icon: "icon icon-green",
          tags: { color: "#6aa84f" },
          onClick: args => { this.updateColor(args.source, args.item.tags.color); }
        },
        {
          text: "Yellow",
          icon: "icon icon-yellow",
          tags: { color: "#f1c232" },
          onClick: args => { this.updateColor(args.source, args.item.tags.color); }
        },
        {
          text: "Red",
          icon: "icon icon-red",
          tags: { color: "#cc0000" },
          onClick: args => { this.updateColor(args.source, args.item.tags.color); }
        },
      ],
      onShow: args => {
        let e = args.source;

        // disable deleting for events that start in the past
        if (e.start() < DayPilot.Date.today()) {
          // @ts-ignore
          args.menu.items[0].disabled = true;
        }
        else {
          // @ts-ignore
          args.menu.items[0].disabled = false;
        }
      }
    }),
    contextMenuResource: new DayPilot.Menu({
      onShow: args => {
        let hasChildren = args.source.children().length > 0;
        if (hasChildren) {
          args.menu.items = this.menuItemsGroup;
        }
        else {
          args.menu.items = this.menuItemsResource;
        }
      }
    })
  };

  menuItemsGroup: DayPilot.MenuItemData[] = [
    {
      text: "Group Details",
      onClick: async args => {
        const form = [
          {name: "Name", id: "name"},
        ];

        const row = args.source;

        const modal = await DayPilot.Modal.form(form, row.data);

        if (modal.canceled) {
          return;
        }

        this.scheduler.control.rows.update(modal.result);

      }
    }
  ];

  menuItemsResource: DayPilot.MenuItemData[] = [
    {
      text: "Resource Details",
      onClick: async args => {
        const form = [
          {name: "Name", id: "name"},
          {name: "Capacity", id: "capacity"},
        ];

        const row = args.source;

        const modal = await DayPilot.Modal.form(form, row.data);

        if (modal.canceled) {
          return;
        }

        this.scheduler.control.rows.update(modal.result);
      }
    }
  ];

  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;
    });
  }

  updateColor(e: DayPilot.Event, color: string): void {
    e.data.barColor = color;
    this.scheduler.control.events.update(e);
  }

}