Overview

  • The row sorting feature allows the JavaScript Scheduler to sort rows using the specified row field.

  • The sorting icons in the column headers are activated automatically for columns that have the sort field set.

  • You can also use the Scheduler API to change the sort order.

This Scheduler can automatically enable sorting support if there are multiple row header columns defined. It builds upon the new Tabular mode, which allows specifying the column content using a property of the resource object. 

The Tabular mode (see rowHeaderColumnsMode) and the row sorting are supported since version 2019.4.4064.

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.

Scheduler Rows

Let's say we want to display a schedule for multiple storage locations. These locations will be displayed as rows in the Scheduler:

JavaScript Scheduler Component Row Sorting - Locations on the Vertical Axis

The rows are defined using the resources array:

const resources = [
  {id: "R1", name: "Storage Location #1"},
  {id: "R2", name: "Storage Location #2"},
  {id: "R3", name: "Storage Location #3"},
  {id: "R4", name: "Storage Location #4"},
  {id: "R5", name: "Storage Location #5"},
  {id: "R6", name: "Storage Location #6"}
];
scheduler.update({resources});

The id and name properties are required. The id specifies a unique value that is used to find matching events/reservations and the name defines the text to be displayed in the row header.

Row Header Columns with Additional Details

Each of the location has additional properties (capacity, distance) which we want to display in the Scheduler as well.

JavaScript Scheduler Component Row Sorting - Row Header Columns with Details

We will define the columns using the rowHeaderColumns property:

const scheduler = new DayPilot.Scheduler("scheduler", {
  rowHeaderColumns: [
    { name: "Name"},
    { name: "Capacity"},
    { name: "Distance"}
  ],
  // ...
});

Extended Row Data

JavaScript Scheduler Component Row Sorting - Row Header Column Data

In order to display the data for the additional columns, we need to add them to the source data in the resources array.

Note that these properties are not predefined. You can use any name that doesn't have a special meaning (see resources). We will use capacity and distance:

const resources = [
  {id: "R1", name: "Storage Location #1", capacity: 40, distance: 100},
  {id: "R2", name: "Storage Location #2", capacity: 15, distance: 20},
  {id: "R3", name: "Storage Location #3", capacity: 5, distance: 20},
  {id: "R4", name: "Storage Location #4", capacity: 20, distance: 100},
  {id: "R5", name: "Storage Location #5", capacity: 20, distance: 200},
  {id: "R6", name: "Storage Location #6", capacity: 25, distance: 300}
];
scheduler.update({resources});

We tell the Scheduler to load the data from these properties using the display property of the rowHeaderColumns items:

const scheduler = new DayPilot.Scheduler("scheduler", {
    rowHeaderColumns: [
      { name: "Name", display: "name"},
      { name: "Capacity", display: "capacity"},
      { name: "Distance", display: "distance"}
    ],
  // ...
});

You can see that we have used our new properties (capacity, distance) but also the predefined name property - both will work fine.

Automatic Row Sorting

JavaScript Scheduler Component Row Sorting - Built-In Sort Icons

In order to activate the built-in row sorting support, we just need to define the field that will be used for each column using the sort property or the rowHeaderColumns array:

const scheduler = new DayPilot.Scheduler("scheduler", {
  rowHeaderColumns: [
    { name: "Name", width: 150, display: "name", sort: "name"},
    { name: "Capacity", display: "capacity", sort: "capacity"},
    { name: "Distance", display: "distance", sort: "distance"}
  ],
  // ...
});

You can see that the Scheduler display the sorting icon (▲) in every row header column title.

Sorting using Scheduler API

JavaScript Scheduler Component Row Sorting - Row Sorting API

You can also sort the Scheduler row manually using the rows.sort() method. The rows.sort() method accepts an object with the following structure:

{
  field: "capacity",
  order: "asc"
}

We will add three buttons to our project that will sort the rows using the specified rule:

HTML

<button class="sort-button" data-field="name">Name</button>
<button class="sort-button" data-field="capacity">Capacity</button>
<button class="sort-button" data-field="distance" data-order="desc">Distance (Desc)</button>

JavaScript

const app = {
  addEventHandlers() {
    const buttons = document.querySelectorAll(".sort-button");
    buttons.forEach(button => {
      button.addEventListener("click", (ev) => {
        const sorting = {
          field: button.dataset.field,
          order: button.dataset.order || "asc"
        };
        scheduler.rows.sort(sorting);
      });
    });
  },
  // ...
};

Full Source Code

And here is the full source code of our Scheduler view with row sorting support:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8"/>
  <title>JavaScript Scheduler: Row Sorting Tutorial</title>

  <!-- DayPilot library -->
  <script src="js/daypilot/daypilot-all.min.js"></script>

  <link rel="stylesheet" type="text/css" href="css/toolbar.css" />
</head>
<body>
<div class="header">
  <h1><a href='https://code.daypilot.org/78411/javascript-scheduler-row-sorting-tutorial'>JavaScript Scheduler: Row Sorting Tutorial</a></h1>
  <div><a href="https://javascript.daypilot.org/">DayPilot for JavaScript</a> - HTML5 Calendar/Scheduling Components for JavaScript/Angular/React/Vue</div>
</div>


<div class="main">
  <div class="toolbar">
    Sort using API:
    <button class="sort-button" data-field="name">Name</button>
    <button class="sort-button" data-field="capacity">Capacity</button>
    <button class="sort-button" data-field="distance" data-order="desc">Distance &#9660;</button>
  </div>
  <div id="scheduler"></div>
  <div class="generated">Generated using <a href="https://builder.daypilot.org/">DayPilot UI Builder</a>.</div>
</div>

<script>
  const scheduler = new DayPilot.Scheduler("scheduler", {
    timeHeaders: [{groupBy: "Month"}, {groupBy: "Day",format: "d"}],
    scale: "Day",
    days: DayPilot.Date.today().daysInYear(),
    startDate: DayPilot.Date.today().firstDayOfYear(),
    rowHeaderColumns: [
      { name: "Name", width: 150, display: "name", sort: "name"},
      { name: "Capacity", display: "capacity", sort: "capacity"},
      { name: "Distance", display: "distance", sort: "distance"}
    ],
    onTimeRangeSelected: async (args) =>{
      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
      });
    },
  });
  scheduler.init();

  const app = {
    addEventHandlers() {
      const buttons = document.querySelectorAll(".sort-button");
      buttons.forEach(button => {
        button.addEventListener("click", (ev) => {
          const sorting = {
            field: button.dataset.field,
            order: button.dataset.order || "asc"
          };
          scheduler.rows.sort(sorting);
        });
      });
    },
    loadData() {
      const resources = [
        {id: "R1", name: "Storage Location #1", capacity: 40, distance: 100},
        {id: "R2", name: "Storage Location #2", capacity: 15, distance: 20},
        {id: "R3", name: "Storage Location #3", capacity: 5, distance: 20},
        {id: "R4", name: "Storage Location #4", capacity: 20, distance: 100},
        {id: "R5", name: "Storage Location #5", capacity: 20, distance: 200},
        {id: "R6", name: "Storage Location #6", capacity: 25, distance: 300}
      ];
      scheduler.update({resources});
    },
    init() {
      this.addEventHandlers();
      this.loadData();
    }
  };
  app.init();
</script>

</body>
</html>