Qlik Community

Qlik Design Blog

All about product and Qlik solutions: scripting, data modeling, visual design, extensions, best practices, etc.

Employee
Employee

Creating charts with enigma.js and Vega

If you don't already know, enigma.js is an open source library for communicating with Qlik Sense backend services. The QIX Service of enigma.js provides an API to communicate with a QIX Engine, giving you the ability to build applications powered by Qlik.

Vega calls itself a visualization grammar. It is a declarative language for creating visualizations. You just describe the appearance and behavior of the visualization you want to create, and Vega does the rest. And it can render with canvas, avoiding costly dom manipulations.

I'm going to demonstrate using enigma.js and Vega to create a simple bar chart. I'll be reusing the qApp.js module and qSessionObject class I introduced in Getting started with enigma.js, so check that out first if you haven't already.

Getting Setup

You can review Getting started with enigma.js‌, and follow the "Setup", "Connecting to an app", and "Creating Session Objects" sections. You'll also want to load jQuery and Vega (https://cdnjs.cloudflare.com/ajax/libs/vega/3.0.0-beta.25/vega.min.js).

Alternatively, you can download the getting-started.zip file below. The getting-started.zip file includes package.json and webpack.config.js files, the qApp.js and qSessionObject.js files, as well as an index.html file, and a main.js file. There's also a .qext file, in case you want to use Qlik Sense as your server for your static files.

For this demonstration, I'm using fields from the Helpdesk Management app, so make sure your qApp.js file is connecting to a copy of the Helpdesk Management app.

The Project

Now that you're setup, we can start the project. We'll do everything in main.js for this demonstration. So open up main.js. We'll need to import qSessionObject.js and create our session object.

import qSessionObject from "./qSessionObject";

let chartCube = new qSessionObject({

  qInfo: {

    qType: "visualization"

  },

  qHyperCubeDef: {

    qDimensions: [{

      qDef: {

        qFieldDefs: ["[Case Owner Group]"]

      },

      qNullSuppression: true

    }],

    qMeasures: [{

      qDef: {

        qDef: "Avg([Case Duration Time])"

      }

    }],

    qInitialDataFetch: [{

      qWidth: 2,

      qHeight: 1000

    }]

  }

});

Now that our qSessionObject has been created, let's define the Vega spec for our bar chart. The spec is roughly based on the spec from Vega's tutorial Vega: Let's Make A Bar Chart Tutorial‌, so feel free to check that out if you like. I simplified it a bit, and removed the data values so that we can stream the values in using data returned from enigma.js. It looks like this:

let barchartSpec = {

  "$schema": "https://vega.github.io/schema/vega/v3.0.json",

  "width": 400,

  "height": 200,

  "padding": 5,

  "data": [

    {

      "name": "table"

    }

  ],

  "scales": [

    {

      "name": "xscale",

      "type": "band",

      "domain": {"data": "table", "field": "category"},

      "range": "width"

    },

    {

      "name": "yscale",

      "domain": {"data": "table", "field": "amount"},

      "nice": true,

      "range": "height"

    }

  ],

  "axes": [

    {

      "orient": "bottom",

      "scale": "xscale",

      "encode": {

        "labels": {

          "update": {

            "angle": {"value": -50},

            "align": {"value": "right"},

            "baseline": {"value": "middle"},

            "radius": {"value": -2}

          }

        }

      }

    },

    {

      "orient": "left",

      "scale": "yscale"

    }

  ],

  "marks": [

    {

      "type": "rect",

      "from": {"data":"table"},

      "encode": {

        "enter": {

          "x": {"scale": "xscale", "field": "category", "offset": 1},

          "width": {"scale": "xscale", "band": 1, "offset": -1},

          "y": {"scale": "yscale", "field": "amount"},

          "y2": {"scale": "yscale", "value": 0}

        },

        "update": {

          "fill": {"value": "steelblue"}

        },

        "hover": {

          "fill": {"value": "red"}

        }

      }

    }

  ]

}

With our qSessionObject and our bar chart spec created, we can create the bar chart. After the dom is ready, we'll initialize the Vega view (Vega: View API‌), open the qSessionObject, get the layout of the qSessionObject, reformat the matrix to work with Vega, insert the values into our Vega view, setup updating the Vega view when the qSessionObject changes, and add an event listener to the Vega view to enable selections. It all looks like this:

$(() => {

  //initialize vega view

  let view = new vega.View(vega.parse(barchartSpec))

    .renderer('canvas')

    .initialize('#view')

    .hover();

  //open cube

  chartCube.open().then(() => {

    //get object layout and insert data into vega view

    chartCube.object.getLayout().then((layout) => {

      let values = layout.qHyperCube.qDataPages[0].qMatrix.map((row) => {

        return {"category": row[0].qText, "qElemNumber": row[0].qElemNumber, "amount": row[1].qNum}

      });

      view.insert('table', values).run();

    });

    //when object data changes, update data in vega view

    chartCube.object.on("changed", function() {

      chartCube.object.getLayout().then((layout) => {

        let values = layout.qHyperCube.qDataPages[0].qMatrix.map((row) => {

          return {"category": row[0].qText, "qElemNumber": row[0].qElemNumber, "amount": row[1].qNum}

        });

        view.remove('table', (d) => { return true; }).run();

        view.insert('table', values).run();

      });

    });

    //add event listener to make selections on hypercube when a bar is clicked

    view.addEventListener('click', function(event, item) {

      if(item){

        chartCube.object.selectHyperCubeValues("/qHyperCubeDef", 0, [item.datum.qElemNumber], true);

      }

    });

  });

});

And that's it. Don't forget npm run webpack, and check out the results. Here's what it should look like - Vega bar chart‌.

I've attached the full project in case you'd prefer to just download that and play around too.

2 Comments
robert_cazaciuc
New Contributor II

Hi Francis. Thank you so much for putting this together. I have downloaded the getting-started.zip folder and I noticed that the main.js is empty - perhaps something happened in the process of uploading the zipped file?


Also - I have one small problem - I compiled all of the files using webpack as per the instruction but when I try to open the webpage I get the following error, do you happen to know why?

ReferenceError: $ is not defined[Learn More]  bundle.js:254:1

And it is pointing to this:

$(function () {

//open cube 

chartCube.open().then(function () {

0 Likes
89 Views
Employee
Employee

main.js is empty in getting-started.zip so you could follow along with the blog post. If you want to download the completed project, download vega-barchart.zip.

And $ is not defined means that jQuery is not defined on your page. Though I do not know why you would be seeing that. jQuery should be loaded from line 8 of index.html

0 Likes
89 Views