Qlik Sense Extension: An Inline Date Range Picker



When you want to have a “Date Range Picker” in your Qlik Sense report, it is almost always recommended to use the extension qlik-date-picker which has been added in to qlik-dashboard-bundle.

However, in certain situations, this extension doesn’t work perfectly to meet customers’ requirements. For example, a customer wants to have a Qlik Sense Mashup, where the date range picker is one of the major filters on the page so that it should always be visible. Specifically, rather than letting users click the button for popping up the calendar view many times (suppose the purpose of the report is to frequently check the measures with different date ranges), it should be displayed inline that it is always there for selecting different date ranges.

Unfortunately, this is almost impossible with the current qlik-date-picker in the bundle. In fact, it is a customized version of a popular JavaScript library daterangepicker.js. I’ve visited its GitHub repo. A lot of developers were asking to add inline display features in the issue trackers but it looks like the author is not quite interested in doing so.

Therefore, I have to “hack” into the included JS library to make such changes. After a few rounds, I found that it is not worth to do so for several reasons:

  • The JS file included with this Qlik Sense Extension is fully customised to adopt Qlik Sense, and the whole structure was changed and completely different from its original JS library.
  • The JS file was minified, and this makes it more difficult to be modified because of the poor readability.
  • The library was never designed to be able to display an inline calendar. So, it’s not a problem can be solved in a few lines of code.
  • The show and hide events were highly customised, which will potentially cause problems even if it is modified to display inline. For example, if the picker is used in a model view or a popup view on a webpage, the nested callback functions are really hard for debugging and maintenance.

Based on all the above requirements and existing limitations, I decided to develop a standalone Qlik Sense Extension to achieve this function. let’s call it qlik-date-picker-inline.

Create Qlik Sense Extension

Go to dev-hub of the Qlik Sense desktop or server, click “Extension editor” on the left navigation or “Create new extension” on the main view.

Image for post

Input the name of the extension, and select “Basic Visualization template”. Then, click “Create & edit”

Image for post

It is your choice to use whichever IDE tools you would prefer, or just use the web editor in dev-hub. Personally, I prefer Microsoft VS code. So, I will open the extension folder from my VS code.

Image for post

However, it is important to bear in mind that adding referenced filed (JS, CSS and etc.) directly via IDE tools or copy/move to the extension fold will NOT work! When we add the required dependencies such as the library JS file, we have to create the file in Qlik Sense Extension Editor, and then copy the code paste into the created file.

Here, the library we choose called “Lightpick” has 3 files to be included. A JS file, a CSS file and its own dependency “moment.js”. The screenshot below shows an example of adding lightpicker.js, and the procedures are exactly the same for the other two files.

Image for post
Image for post

For the source code of the library to be copy-paste, please go to the original GitHub repo.

Adopt the JS Library with Qlik Sense

Qlik Sense uses define and require module for including dependencies. We need to add the correct path of the library, and sometimes even modify the library itself if it needs its own dependency. In our case, it does need moment.js so that we need to modify it.

Firstly, let’s go to qlik-date-picker-inline.js to add the library files

define( [ "qlik", "./lightpick", "./moment", "css!./lightpick.css"],
function (qlik, Lightpick, moment) {
...

Note that we need to ignore the .js extensions when include them using define. Also, we use css! prefix to tell Qlik Sense that this is a CSS file rather than a JS file.

Then, let’s go to lightpick.js to change its dependency reference.

Originally, the factory function looks like this

(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD. Make globaly available as well
define(['moment'], function (moment) {
return factory(moment);
});
}
...

define(['moment']... will not work in Qlik Sense. Let’s change it to define(['./moment'] which is a relative path. So, if you prefer to create a lib folder in your extension, then you can also change the relative path to ./lib/moment. But here I’ll simply put it in the root folder.

Test the inline date range picker

The author of this lightpick library prepared pretty good documentation about how to use this library. Please refer to it’s GitHub repo with many demos.

Let’s go to qlik-date-picker-inline.js, and put the following code in paint function.

paint: function ($element) {    console.log('in paint')    //add your rendering code here
$element.html( '<input type="hidden" id="datepicker"/>' );

var picker = new Lightpick({
inline: true,
singleDate: false,
numberOfMonths: 2,
field: document.getElementById('datepicker'),
onSelect: function(start, end){}
});
//needed for export
return qlik.Promise.resolve();
}

Here, we need an input tag for lightpick working. However, we don’t actually want Qlik Sense to show this input. So, we can set it type to hidden.

Also, we want to set inline to true in order to display the picker inline, as well as singleDate set to false to make sure it is picking a range rather than a single date. for the list of configurable items, please refer to the GitHub repo README.

Let’s add the extension to Qlik Sense dashboard

Image for post

It works!

Add Date Field to the Extension Widget

In order to make the date picker actually work with Qlik Sense, we need to add an input field so that users can fill the Date dimension in.

In the return block of the extension function, we need to define the initialProperties first. Here, we just define a qListObjectDef with qInitialDateFetch, because we only need one field. Also, we give 1e4 height to the list which literally allows about 110 years with consecutive days in the date dimension.

initialProperties: {
version: 1.0,
qListObjectDef: {
qInitialDataFetch: [{
qWidth: 1,
qHeight: 1e4
}]
}
},

Then, we define the items objectin definition object contains only one input field for the date field. definition objectin Qlik Sense Extension can be used for defining the accordion menu on the right side.

definition: {
type: "items",
component: "accordion",
items: {
dimension: {
type: "items",
label: "Date Field",
ref: "qListObjectDef",
min: 1,
max: 1,
items: {
dimension: {
type: "string",
expression: "optional",
expressionType: "dimension",
ref: "qListObjectDef.qDef.qFieldDefs.0",
label: "Input Date Field"
}
}
},
appearance: {
uses: "settings"
}
}
},

After this, we should be able to input the date field of the extension.

Image for post

If you wish, you can now try to output the layout object to see the fetched dates.

paint: function ($element, layout) {
console.log(layout)
....

Implement Select Date Feature

Now, we need to let Qlik Sense knows that the user has chosen a date range when the date range is actually selected on the picker. In the Lightpick definition, we need to implement the onSelect object.

var self = this
var allDates = layout.qListObject.qDataPages[0].qMatrix
onSelect: function(start, end){
if (start && end) {
// Convert start & end moment to date string
var startDateString = start.format('D/M/YYYY')
var endDateString = end.format('D/M/YYYY')

// Populate all the dates in between of the range
toBeSelected = []
allDates.forEach(qDate => {
if (checkDateInBetween(moment(qDate[0].qText, 'D/M/YYYY'), startDateString, endDateString)) {
toBeSelected.push(qDate[0].qElemNumber)
}
});
console.log(toBeSelected)
// Select the dates
if (toBeSelected.length > 0) {
self.backendApi.selectValues(0, toBeSelected, false)
}
}
}

Here, we firstly get all the dates from the layout object. Then, loop all the dates to test whether it falls in the selected range. If so, push it into thetoBeSelected array.

Now, if we select a range 1/1/2014 — 12/1/2014 the Qlik backend API should be updated and we can see the selection from the selection bar

Image for post

Implement “setDateRange” based on the existing selection

Now, we still have a problem. After we selected a date range, it doesn’t retain on the picker. Also, if we have already got a date range selected in Qlik Sense, it doesn’t reflect on the picker. This is because Qlik Sense will refresh the extension after every time the user made a change. So, we need to let the extension detect if there is a selected range and programmatically set it on the picker.

// Get current selection if there are any
var selectedDates = []
allDates.forEach(qDate => {
if (qDate[0].qState == "S") {
selectedDates.push(moment(qDate[0].qText, "D/M/YYYY"))
}
});

if (selectedDates.length > 0) {
selectedDates = selectedDates.sort((a,b) => a-b)
minSelectDate = selectedDates[0]
maxSelectDate = selectedDates[selectedDates.length - 1]
picker.setDateRange(minSelectDate, maxSelectDate, true)
picker.gotoDate(maxSelectDate.subtract(1, 'month'))
}

Here, it is important to set the 3rd parameter of setDateRange to false. This is preventing the setDateRange function triggers the onSelect event. If we don’t do this, the setDateRange will trigger onSelect event of the picker, which has backendApi.selectValues() in it, and this will cause the whole extension to be refreshed and the setDateRange will be called again.

Also, don’t forget to call the gotoDate function at the end to make sure that the picker will interactively go to the selected date. This is quite important if you have other widgets that can select dates.

OK! Now we’ve done this Qlik Sense Extention. The snippets of the code probably difficult to be linked together to have the whole picture. But don’t worry, you can find the full source code available on my GitHub repo:

Resources:

Lightpick: https://wakirin.github.io/Lightpick/

Moment.js: https://momentjs.com

Qlik Sense Developer Documentation: https://help.qlik.com/en-US/sense-developer/April2019

GitHub Repo: https://github.com/qiuyujx/qlik-date-picker-inline

This article was originally published on Level Up Coding by myself:
https://levelup.gitconnected.com/qlik-sense-extension-an-inline-date-range-picker-7cc688738379


Comments