Filtering Data Using DateRangepicker with Paging and Preset Intervals

Quite often programmers face the necessity to filter large datasets by various periods of time whether it is a decade, year or a custom period. A vivid example here would probably be Webix Inventory App where we implemented such a solution with the help of the DateRangePicker and Richselect components. So in this tutorial we are going to build a control that will help you to filter multi-page data by certain date intervals.

This is what you are going to build:

View code >>

So let’s do it step by step.

Building the interface

The control is a combination of other controls, namely:

  • DateRangePicker for date selection
  • Richselect with predefined time intervals
  • arrows to switch the intervals back and forth

As far as several controls are engaged you need a container to nest them in. Webix Toolbar seems to fit perfectly. So let’s define a toolbar and populate it with the controls. The date picker should be at least 220px in width. For sake of convenience specify the initial date range with the help of the value property. To hide all the buttons in the popup calendar add the suggest property to the config and inside its body field disable buttons and icons as it is shown below:

{
  view:"toolbar", css:"webix_dark", paddingX:12,
    cols:[
      // ...
      {
        view:"daterangepicker", id:"dates", width:220,
        value:{
          start:webix.Date.monthStart(new Date()),
          end:webix.Date.monthStart(webix.Date.add(new Date(), 1, "month"))
        },
        suggest:{
          view:"daterangesuggest",
          body:{
            button:false, icons:false // hiding buttons
          }
        }
      },
      // ...
    ],
  // config
}

On either side of the date picker add an arrow to be able to page back and forth through intervals.

{
  view:"toolbar", css:"webix_dark", paddingX:12,
    cols:[
      { view:"icon", icon:"wxi-angle-left", id:"prev"},
      {/*date picker configuration*/},
      { view:"icon", icon:"wxi-angle-right", id:"next"},
      // ...
    ]
}

You also need a list with predefined date intervals. In the demo they are a year, month, 12 months, and a custom interval. While with the first three periods the values are pretty clear the last one needs the $empty property to reset the current value. So let’s create Richselect and pass the options to it.

{
  view:"toolbar", css:"webix_dark", paddingX:12,
    cols:[
      // arrows and date picker
      {
        view:"richselect", width:120, value:"month",
        id:"dateMode", placeholder:"Custom",
        options:[
          { $empty:true, id:"$empty", value:"Custom" },
          { id:"month", value:"Month" },
          { id:"year", value:"Year" },
          { id:"12", value:"12 months" }
        ],
      }
    ]
}

By this point you should have a ready-made interface.

Code of this step >>

Implementing logic

The interface is ready, so it’s the right time to define the necessary behaviour for the controls.

Tuning Richselect

You have created a list of predefined options and now let’s breathe life into them. Richselect will set a date interval of DateRangePicker (a month, a year, or last 12 months). Let’s handle the onChange event to track changes in the select box and set dates for the date picker accordingly:

{
  view:"richselect",  
    // config
    on:{
      onChange: function(value){
        setRange(value);
      }
    }
}

function setRange(value){
  if (value){
    let dates = {};
    switch (value){
      case "month":
      case "year":
        dates.start = webix.Date.monthStart(new Date());
        dates.end = webix.Date.monthStart(webix.Date.add(new Date(), 1, value));
        break;
      case "12":
        dates.start = webix.Date.monthStart(webix.Date.add(new Date(), -11, "month"));
        dates.end = webix.Date.monthStart(webix.Date.add(new Date(), 1, "month"));
    }
  }
}

The setRange function takes the ID of the selected option as a parameter and switches the value of the date picker according to that ID.

Code of this step >>

So the two components are bound and changing value in the select box will change the one of the date picker .

Setting custom mode

In the demo it is the date picker that deals with custom intervals. So when the user selects a date range that does not coincide with any of the predefined ones, you should reset Richselect value. Let’s add the onChange event listener that triggers every time the user selects a date range.

const ranges = [
// number of days in ms
  31622400000, // 366
  31536000000, // 365
  2678400000, // 31
  2592000000, // 30
  2505600000, // 29
  2419200000 // 28
];

{
  view: “daterangepicker”,
    on:{
      onChange:function(nv){
        // if the period does not relate to any period from "modes", set custom
        // do not do anything until the user selects the end date!
        if (nv.start && nv.end){
      // range in ms
          const range = webix.Date.datePart(nv.end) - webix.Date.datePart(nv.start);
          const r = ranges.indexOf(range);
          if (r === -1){
            $$("dateMode").setValue("");
          }            
          // other ranges
      this.getPopup().hide();
        }
      }
    }
  // config
}

As far as the user can pick a month/year/etc. manually уou should check whether the selected value matches any of the predefined time intervals stored in the array called “ranges” (all the values are in ms). If it’s the case, change the value of the select box value to the matched one (year, month, etc) if not – the value of the select box is reset.

Code of this step >>

Paging with arrows

Now let’s implement paging where the arrows will switch DateRangepicker value. You can add a common function that will be called upon a click on either arrow.

{
  // inside the toolbar config
  [
    { view:"icon", icon:"wxi-angle-left", id:"prev", click: function(){
      changeRange(-1);
    }},
  // …
  { view:"icon", icon:"wxi-angle-right", id:"next", click: function(){
     changeRange(1);
  }},
 ]

This function receives a parameter (1 or -1) and steps forth or back correspondingly.
First, you need to disable the arrows until both start and end date are selected in the date picker. Apart from that copy the date to avoid changing the original object.

function changeRange(sign){
  const mode = $$("dateMode").getValue();
  const orDate = $$("dates").getValue();

  // do not start until the user selects the end date!
  if (orDate.start && orDate.end){
    let dates = {
      start:webix.Date.copy(orDate.start),
      end:webix.Date.copy(orDate.end)
    };
  // further logic
  }
}

Now you need to calculate the date according to the current Richselect option (or lack thereof) and the argument passed to the changeRange function. If no option is selected (the “Custom” option is selected) then the step will be based on the current date picker value. Basically, you need to shift the start and end date by this step. So it makes sense to use the add method of the webix.Date module that serves this purpose.

function changeRange(sign){
  if (orDate.start && orDate.end){
    // …

    // checks if selected range matches one of the predefined options
    if (mode){
      switch (mode){
        case "month":
        case "year":
          dates.start = webix.Date.add(dates.start, sign*1, mode);
          dates.end = webix.Date.add(dates.end, sign*1, mode);
          break;
        case "12":
          dates.start = webix.Date.monthStart(webix.Date.add(dates.start, sign*12, "month"));
          dates.end = webix.Date.monthStart(webix.Date.add(dates.end, sign*12, "month"));
      }
    } else {
      const period = webix.Date.datePart(dates.end) - webix.Date.datePart(dates.start);
      const days = period / (1000 * 60 * 60 * 24);
      dates.start = webix.Date.add(dates.start, sign*days, "day");
      dates.end = webix.Date.add(dates.end, sign*days, "day");
    }

  }
}

Try selecting a range:

View code >>

That’s it! The control is fully armed and you can check it out in action e.g. for filtering some data.

Using the control to filter data

The resulting control can be used to filter data by date intervals e.g. to look for specific chunks of data. We are going to filter the Datatable by comparing the start field of the data items with the time interval from the date picker.

{
  view: “daterangepicker”,
    on:{
      onChange:function(nv){
        // if the period does not relate to any period from "modes", set custom
        // do not do anything until the user selects the end date!
        if (nv.start && nv.end){
          // ....

          // use values for datatable
          $$("data").filter(function(obj){
            return obj.start >= nv.start && obj.start < nv.end;
          });
        }
      }
   }
}

View code >>

To sum it all up

We combined Daterange, Richselect, and icons into a small but useful tool that will help to pick date intervals as well as step back and forth through them. Originally this control was implemented in the Inventory App (right time to check it out if you haven’t yet). The resulting tool can be used for filtering large datasets by certain date intervals.

Have you ever faced such a task? How would you solve it? Share your thoughts with us in the comments section below.