Extensions are Qlik Sense components made with HTML, CSS and JavaScript, which are meant to be used inside QVFs or Mashups.

They serve a multitude of purposes, such as creating custom charts, buttons, tables, comments, and so on.

This short post will show you how to create an extension from scratch. It will be a custom line chart using the ECharts library.

The result will be the image below, and you can use it in your applications and create new features as you wish!

Needed tools:

– Qlik Sense Desktop
– A Text Editor (I use Visual Studio Code)
– ECharts Library (https://echarts.apache.org/)
– Basic HTML and JS knowledge
– Browser (we will access Qlik Sense via this address: http://localhost:4848/hub)

Building the basic file structure:

Once you’ve got your Qlik Sense Desktop up and running, we’ll create a file called ClusterLineChart inside the /Extensions/ folder. This is usually located under Documents/Qlik/Sense/Extensions/.

– ClusterLinechart.qext
– ClusterLinechart.js

The QEXT file is responsible for telling Qlik Sense what the above set of files represents. In this case, type will be “visualization”.

ClusterLinechart.qext File contents

{
 "name" : "Cluster Line Chart",
 "description" : "Minha primeira extensão!",
 "icon" : "extension",
 "type" : "visualization",
 "version": "0.1",
 "author": "Thomas Pessato"
}

And the basic Javascript file structure:

ClusterLinechart.js File contents

define( [
     'jquery'
 ],
 function ( $ , echarts) {
     'use strict';
     return {
         paint: function ( $element, layout ) {
             $element.append("teste");
             //deverá aparecer escrito ‘teste’ ao inserir a extensão
         }
     };
 } ); 

With this done, we can now access our extension via the “custom objects” tab inside a QVF.

Note 1: The $ element variable is the containing box in our extension. That is, anything we put inside this element will appear on the screen. This includes text or any html element.

Note 2: The paint method is executed every time an action occurs on the screen, be it a window resize, sheet object resize, an applied filter, etc. This method is responsible for ‘painting’ the object on the screen.

For testing purposes, we will include the text “teste” inside our extension. As we insert the extension into a sheet, the text “teste” should be visible:

Great! We now have an extension that we can include inside a QVF, and it already brings up information!

Now we must make sure all our dependencies are included in our extension. In this case, it means including the ECharts library.

 define( [
     'jquery',
     'https://cdnjs.cloudflare.com/ajax/libs/echarts/4.2.1/echarts.min.js'
 ],
 function ( $ , echarts) {
    //repare na variável echarts, logo depois de $.
    …
 } ); 

At this point we should be able to “print” the “echarts” variable within our extension and check it exists in the console (hit F12 in the browser). Note: For this to work, we must insert the extension inside a sheet.

In other words, we are now able to use the ECharts library and start building our visualizations!

We now need to define which properties / data we want the Sense Engine to return. This means the data, the title, subtitle and footnote options of the object, and so on. For this to work, we must declare a few properties within our JavaScript code, which we will account for within our “return” in the code snippet below.

define( [
     'jquery',
     'https://cdnjs.cloudflare.com/ajax/libs/echarts/4.2.1/echarts.min.js'
 ],
 function ( $, echarts ) {
     'use strict';
     return {
         initialProperties: {
             qHyperCubeDef : {
                 qDimensions : [],
                 qMeasures : [],
                 qInitialDataFetch : [{
                     qWidth : 2,
                     qHeight : 50
                 }]
             }
         },
         definition: {
             type: "items",
             component: "accordion",
             items: {
                 data: {
                     uses: "data",
                     dimensions: {
                         uses: "dimensions"
                     },
                     measures: {
                         uses: "measures"
                     }
                 },
                 sorting: {
                     uses: "sorting"
                 },
                 appearance: {
                     uses: "settings",
                 }
             }
         },
         paint: function ( $element, layout ) {
              …
         };
 } );

For more information on what each of the properties means: Building a property panel for your extension

Anyway, now we can get to the real work. With these properties, when we insert the extension into a sheet, the option to “Add dimension” and “Add measure” will appear, as it usually appears in Qlik’s native extensions.

When choosing the data that we want to include (a measure and a dimension), the Qlik engine will return the data and metadata within the variable called “layout”, which is within the “paint” function. Namely: function ($ element, layout) {…}.

paint: function ( $element, layout ) {
             var data = layout.qHyperCube.qDataPages[0].qMatrix;
             console.log(data);
         }

Adding this code snippet, then going through the JavaScript layout object, we will find the qMatrix property, which has all the data. We can check through the browser’s console what data is being returned. Note: In order to view the data as shown below, you must select a dimension and a measure for the extension.

What we need to do now is convert this console data into a friendlier view on the main browser.

In order to structure the data we got from the Qlik Engine, we know that position 0 of the array corresponds to the dimension, and position 1 to the measure. We will now separate this data into different strings.

var dimensions = layout.qHyperCube.qDataPages[0].qMatrix.map((item) => {       return item[0].qText;
 });
 var measures = layout.qHyperCube.qDataPages[0].qMatrix.map((item) => {
     return {
          value: item[1].qNum,
          itemStyle: {
              color: "red"
          }
     } 
 });

I used the map method to optimize the code. This code will come out as two arrays, one containing the dimension text (qText), and the second will contain 2 variables: value (qNum) and itemStyle. The value array will match the measurement value, and the itemStyle array will match pertinent row customizations. In this case, I just decided to put a “color” variable with the value “red”, which will be used to render the ECharts chart. We could input styles for the line, such as thickness, types of line, and so on.

Last but not least, we will now pass the data on to the graphic library and return the element to be displayed in our extension.

paint: function ( $element, layout ) {
       var dimensions = layout.qHyperCube.qDataPages[0].qMatrix.map((item) => {
       return item[0].qText;
       });
       var measures = layout.qHyperCube.qDataPages[0].qMatrix.map((item) => {
           return {
                value: item[1].qNum,
                itemStyle: {
                    color: "red"
                 }
          } 
       });
       var myChart = echarts.init($element[0]);
       //initializing the graph in the extension's box. Since $element is a jQuery element, we access via index 0.
       // Here we pass all the variables to the graph configuration object, according to the Echarts documentation. xAxis for dimensions, and then passing measurements on the so-called data "series"
       var option = {
                  legend: {
                      data:[layout.qHyperCube.qMeasureInfo[0].qFallbackTitle]
                  },
                  xAxis: {
                      data: dimensions // variable 'dimensions', declared at the beggining
                  }, 
                  yAxis: {},
                  series: [{
                      lineStyle: {
                          normal: {
                              color: "red",
                              width: 3,
                              type: "solid"
                          }
                      },
                      name: 'Sales',
                      type: 'line',
                      data: measures // variable'measure', declared at the beggining
                      }]
         }; // what defines this being a line chart is the prop called “type”
         myChart.setOption(option);
         myChart.resize(); //whenever an action is taken, the chart must be resized via resize(). This is an eCharts method.
}

For more customizations (there are MANY), you can check out the ECharts documentation and test your countless options (https://echarts.apache.org/en/option.html).

The end result, using Qlik’s own Consumer Sales.qvf and using the City dimension along with “Avg Sales”:

Notice that at this time the customization panel works, with its basic settings:

There are several additional customizations and implementations to be had, such as:

  • chart filters;
  • zoom options;
  • data export options;
  • color customizations;
  • adding reference lines;
  • entry point customizations;
  • tooltip with values;
  • and more.

Nevertheless these will be covered in subsequent posts. We hope this post was useful! For more details on custom extensions and graphics, contact us!