Features

See also the basic JavaScript Resource Calendar Tutorial that explains the setup and configuration of the component.

This project was generated using Resource Calendar UI Builder (online configurator).

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.

Filter Implementation

The JavaScript resource calendar component filtering API consists of two parts:

The column filtering API is available since build 2018.3.3342.

Filtering Columns by Text

javascript resource calendar column filtering by text

Let's say we want to filter the columns by text in the column name.

To apply a column filter, we need to call columns.filter() method and pass the criteria as a parameter:

dp.columns.filter("room");

This call will invoke onColumnFilter event for each of the columns. We need to add an event handler that will use the supplied criteria to determine whether the column will be visible or not:

dp.onColumnFilter = function(args) {
  var criteria = args.filter.toUpperCase(); 
  var columnName = args.column.name.toUpperCase();
  if (columnName.indexOf(criteria) === -1) {
    args.visible = false;
  }
};

The args.filter property holds the "room" string that we used as an argument when calling columns.filter().

We will normalize the text using toUpperCase() method:

var criteria = args.filter.toUpperCase();

The args.column property holds an object with column details. We can access the column name as args.column.name. We will normalize it as well - comparing two normalized strings will make the filter case insensitive:

var columnName = args.column.name.toUpperCase();

If you need case-sensitive search, just use the original string in both cases.

The args.visible property determines whether the column will be visible (by default it is set to true). We can hide columns that don't meet the rules by setting args.visible to false. In this case, we hide the column if the column name doesn't contain the filter string:

if (columnName.indexOf(criteria) === -1) {
  args.visible = false;
}

Clearing the Filter

We can clear the filter by calling columns.filter() without an argument:

dp.columns.filter();

This will display all columns, onColumnFilter event handler will not be called at all.

Filtering Columns by Category

javascript resource calendar column filtering by category

The same approach can be used to filter columns by category:

dp.columns.filter("People");

In this case, we need to specify a category for each of the columns. One of the options is to add an extra field to the column object (just make sure that the property name doesn't conflict with the built-in properties see DayPilot.Calendar.columns.list):

dp.columns.list = [
  {name: "Rooms", children: [
      {name: "Room 1", id: "R1", category: "Rooms"},
      {name: "Room 2", id: "R2", category: "Rooms"},
      {name: "Room 3", id: "R3", category: "Rooms"},
      {name: "Room 4", id: "R4", category: "Rooms"},
      {name: "Room 5", id: "R5", category: "Rooms"},
      {name: "Room 6", id: "R6", category: "Rooms"},
      {name: "Room 7", id: "R7", category: "Rooms"},
      {name: "Room 8", id: "R8", category: "Rooms"}
    ]},
  {name: "People", children: [
      {name: "Martin", id: "P1", category: "People"},
      {name: "Lisa", id: "P2", category: "People"},
      {name: "Diego", id: "P3", category: "People"},
      {name: "Lucy", id: "P4", category: "People"}
    ]}
];

The filtering event handler needs to compare the selected category with the column data. The original column object is available as args.column.data - we can access our custom category property as args.column.data.category:

dp.onColumnFilter = function(args) {
  var criteria = args.filter; 
  var columnCategory = args.column.data.category;
  if (criteria != "All" && criteria != columnCategory) {
    args.visible = false;
  }
};

Complex Filter

javascript resource calendar column filtering complex

Now we can combine both filters. We need to change the columns.filter() parameter from a simple string to an object that holds both filter condition:

var filter = {
  name: "a",
  category: "People"
};

dp.columns.filter(filter);

The filter implementation only marks the column as visible if both conditions are met:

dp.onColumnFilter = function(args) {
  var name = args.filter.name;
  var category = args.filter.category;

  var nameMatches = false;
  var categoryMatches = false;

  // no text specified
  if (!name) {
    nameMatches = true;
  }
  // filter text matches the column name, case insensitive
  else if (args.column.name.toUpperCase().indexOf(name.toUpperCase()) > -1) {
    nameMatches = true;
  }

  // all categories
  if (category === "All") {
    categoryMatches = true;
  }
  // category matches the column category
  else if (args.column.data.category === category) {
    categoryMatches = true;
  }
  args.visible = nameMatches && categoryMatches;
};

Highlighting the Filter Text

javascript resource calendar column filtering highlight

In the last step, we will highlight the filter text in the column headers. This can be done easily using onBeforeHeaderRender event handler. This event handler is fired for every column header during resource calendar update - we just need to add the highlight to the header HTML.

dp.onBeforeHeaderRender = function(args) {
  if (filter.name) {
    // case-insensitive search and replace
    var escaped = filter.name.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
    var search = new RegExp("(" + escaped + ")", 'ig');
    args.header.html = args.header.name.replace(search, "<span style='background-color: #e69138; color: #fff;'>$1</span>")
  }
};

Full Source Code

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8"/>
  <title>JavaScript Resource Calendar: Column Filtering</title>

  <style type="text/css">
    /* ... */
  </style>

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

</head>
<body>
<div class="header">
  <h1><a href='https://code.daypilot.org/25430/javascript-resource-calendar-column-filtering'>JavaScript Resource Calendar: Column Filtering</a></h1>
  <div><a href="https://javascript.daypilot.org/">DayPilot for JavaScript</a> - HTML5 Calendar/Scheduling Components for JavaScript/Angular/React</div>
</div>

<div class="main">

  <div class="filter-section">
    Filter columns
    by name: <input id="filter" />
    category: <select id="category"><option>All</option><option>People</option><option>Rooms</option></select>
    <a href="#" id="clear">&#x2716; Clear</a>
  </div>

  <div id="dp"></div>

</div>

<script>
  var dp = new DayPilot.Calendar("dp", {

    viewType: "Resources",
    headerLevels: 2,

    headerHeight: 30,
    cellHeight: 30,
    hourWidth: 60,

    showCurrentTimeMode: "Full",
    showCurrentTime: false,

    onColumnFilter: function(args) {
      var name = args.filter.name;
      var category = args.filter.category;

      var nameMatches = false;
      var categoryMatches = false;

      // no text specified
      if (!name) {
        nameMatches = true;
      }
      // filter text matches the column name, case insensitive
      else if (args.column.name.toUpperCase().indexOf(name.toUpperCase()) > -1) {
        nameMatches = true;
      }

      // all categories
      if (category === "All") {
        categoryMatches = true;
      }
      // category matches the column category
      else if (args.column.data.category === category) {
        categoryMatches = true;
      }
      args.visible = nameMatches && categoryMatches;
    },
    onBeforeHeaderRender: function(args) {
      if (filter.name) {
        // case-insensitive search and replace
        var escaped = filter.name.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
        var search = new RegExp("(" + escaped + ")", 'ig');
        args.header.html = args.header.name.replace(search, "<span style='background-color: #e69138; color: #fff;'>$1</span>")
      }
    },

    eventDeleteHandling: "Update",
    onTimeRangeSelected: function (args) {
      DayPilot.Modal.prompt("Create a new event:", "Event 1").then(function(modal) {
        var dp = args.control;
        dp.clearSelection();
        if (!modal.result) { return; }
        dp.events.add(new DayPilot.Event({
          start: args.start,
          end: args.end,
          id: DayPilot.guid(),
          text: modal.result,
          resource: args.resource
        }));
      });
    }
  });
  dp.events.list = [
    {
      id: "1",
      start: DayPilot.Date.today().addHours(10),
      end: DayPilot.Date.today().addHours(12),
      text: "Event 1",
      resource: "R1"
    }
  ];
  dp.columns.list = [
    {name: "Rooms", children: [
        {name: "Room 1", id: "R1", category: "Rooms"},
        {name: "Room 2", id: "R2", category: "Rooms"},
        {name: "Room 3", id: "R3", category: "Rooms"},
        {name: "Room 4", id: "R4", category: "Rooms"},
        {name: "Room 5", id: "R5", category: "Rooms"},
        {name: "Room 6", id: "R6", category: "Rooms"},
        {name: "Room 7", id: "R7", category: "Rooms"},
        {name: "Room 8", id: "R8", category: "Rooms"}
      ]},
    {name: "People", children: [
        {name: "Martin", id: "P1", category: "People"},
        {name: "Lisa", id: "P2", category: "People"},
        {name: "Diego", id: "P3", category: "People"},
        {name: "Lucy", id: "P4", category: "People"}
      ]}
  ];
  dp.init();

</script>

<script>
  var filter = {
    name: null,
    category: "All"
  };

  var elements = {
    filter: document.querySelector("#filter"),
    category: document.querySelector("#category"),
    clear: document.querySelector("#clear")
  };

  elements.filter.addEventListener("keyup", function() {
    filter.name = this.value;
    dp.columns.filter(filter);
  });

  elements.category.addEventListener("change", function() {
    filter.category = this.value;
    dp.columns.filter(filter);
  });

  elements.clear.addEventListener("click", function(ev) {
    ev.preventDefault();

    elements.filter.value = "";
    elements.category.value = "All";

    filter.name = null;
    filter.category = "All";

    dp.columns.filter(null);
  });

</script>

</body>
</html>