Skip to main content
Announcements
Join us at Qlik Connect for 3 magical days of learning, networking,and inspiration! REGISTER TODAY and save!
Francis_Kabinoff
Former Employee
Former Employee

enigma.js is Qlik’s open source library for communicating with Qlik Sense backend services. I’m going to show you how to get started using enigma.js, as well as a couple general patterns I’ve been using.

 

I’m going to use ES2015, Babel, and webpack. These are not necessarily requirements for building an app with enigma.js, but I recommend them.

 

Setup

Create a new folder for your project. Run npm init or just create your own package.json file, however you prefer. Now run npm install babel-loader babel-core babel-preset-es2015 webpack --save-dev. This will install our development dependencies and save them to our package.json file. Next, run `npm install enigma.js --save`. This will install enigma.js, and save it to our package.json file.

 

For convenience, we’ll add webpack to the scripts property of our package.json file. This will let us run npm run webpack instead of having to supply the entire path to webpack. Open up the package.json file and add ”webpack”: “webpack” to the scripts property. Your package.json file should now look something like this:

{

  "name": "enigmapatterns",

  "version": "1.0.0",

  "description": "",

  "dependencies": {

    "enigma.js": "^1.0.1"

  },

  "devDependencies": {

    "babel-core": "^6.23.1",

    "babel-loader": "^6.3.1",

    "babel-preset-es2015": "^6.22.0",

    "webpack": "^2.2.1"

  },

  "scripts": {

    "webpack": "webpack"

  }

}

 

 

We’ll also need to create a webpack.config.js file, which should look like this:

module.exports = {

  entry: './main.js',

  output: {

    path: __dirname,

    filename: 'bundle.js'

  },

  devtool: 'source-map',

  module: {

    loaders: [

      {

        exclude: /(node_modules)/,

        loader: 'babel-loader',

        query: {

          presets: ['es2015']

        }

      }

    ]

  }

}

 

 

Finally, we’ll want to create a main.js file which will be our entry point, and an index.html file which loads the bundle.js file that our build will output.

 

Connecting to an app

The pattern I’ve been using to connect to the app is to create a qApp.js file, and export the promise to connect to the app. This way, anywhere I need to use the app I can simply import this file and do something like getApp.then((app) => { //code here }).

 

So create a file named qApp.js, which looks like this:

import enigma from 'enigma.js';

const qixSchema = require('./node_modules/enigma.js/schemas/qix/3.1/schema.json');

 

const config = {

  schema: qixSchema,

  session: {

    host: 'localhost',

    prefix: '',

    port: 4848,

    unsecure: true

  }

};

 

export default enigma.getService('qix', config).then((qix) => {

  return qix.global.openApp('Helpdesk Management.qvf').then((app) => {

    return app;

  });

});

 

 

Creating session objects

I like to have a class for creating session objects. That way I can have an open() and close() method for each session object. The open() method calls createSessionObject on the app and returns the promise if the object does not exist in the app, and simply returns otherwise, so that I can always check to see if an object is available, without inadvertently creating a duplicate object in the app. The close() method calls destroySessionObject on the app, passing along the object’s id.

 

So create a file named qSessionObject.js, which looks like this:

import getApp from "./qApp";

 

class qSessionObject {

 

  constructor(properties) {

    this.properties = properties;

    this.object = null;

  }

 

  open() {

    if (!this.object) {

      return getApp.then((app) => {

        return app.createSessionObject(this.properties).then((object) => {

          this.object = object;

        });

      });

    }

  }

 

  close() {

    if (this.object) {

      return getApp.then((app) => {

        return app.destroySessionObject(this.object.id).then(() => {

          this.object = null;

        });

      });

    }

  }

 

}

 

export default qSessionObject;

 

You can also put other methods in this class which may be helpful. For example, I recently built an app that had 2 alternate states, and when the states changed I needed to call applyPatch on each object and change the state of the object. Having a class for the session objects made this really easy.

 

Usage example

So now we have our project setup, a qApp.js file for connecting to the app, a qSessionObject.js file for creating session objects, let’s cover a simple use.

 

We’ll create 3 lists objects, and 3 filters in our UI with those list objects. For this, we’ll use the Filter class which you can find at the end of this post, along with the rest of the source code. You'll need the filter.js file from the zip below.

You’ll also find a .qext file in the source code. That’s so we can put this project in the Extensions folder of Qlik Sense Desktop to use it as a server, but you can serve this any way you’d like, just make sure that your config in qApp.js is pointing to your Qlik Sense server.

 

So first, let’s get our html ready. The Filter class relies on jQuery and Bootstrap 4 alpha v6, so we’ll load those. Also, we'll need to load our bundle.js and main.css files. Then we can add the html we’ll need to the body. Your index.html should look like this:

<!doctype html>

<html>

<head>

  <title>Enigma patterns</title>

  <meta charset="utf-8">

  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no">

  <!-- jQuery -->

  <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>

  <!-- Tether -->

  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/tether/1.4.0/css/tether.min.css">

  <script src="https://cdnjs.cloudflare.com/ajax/libs/tether/1.4.0/js/tether.min.js"></script>

  <!-- Bootstrap 4 -->

  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css">

  <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/js/bootstrap.min.js"></script>

  <!--Project code -->

  <script src="bundle.js"></script>

  <link rel="stylesheet" href="main.css">

</head>

<body>

  <div id="loading">Loading...</div>

  <div id="loaded" style="display: none">

    <div class="filters row">

      <div class="col-auto">

        <div class="filter" id="case-number-filter"></div>

      </div>

      <div class="col-auto">

        <div class="filter" id="subject-filter"></div>

      </div>

      <div class="col-auto">

        <div class="filter" id="case-owner-filter"></div>

      </div>

    </div>

  </div>

</body>

</html>

 

Grab the main.css file from the attached source code and save it in your project folder.

 

Now open main.js. We’ll import the qSessionObject class and the Filter class, create 3 list objects, create 3 filters, open the 3 list objects, and then initialize the filters. This is what that should look like:

import qSessionObject from "./qSessionObject";

import Filter from "./filter";

 

let caseNumberList = new qSessionObject({

  qInfo: {

    qType: "visualization"

  },

  qListObjectDef: {

    qDef: {

      qFieldDefs: ["[CaseNumber]"],

      qFieldLabels: ["Case Number"]

    },

    qAutoSortByState: {

      qDisplayNumberOfRows: 1

    },

    qShowAlternatives: true,

    qInitialDataFetch: [{

      qWidth: 1,

      qHeight: 1000

    }]

  }

});

let subjectList = new qSessionObject({

  qInfo: {

    qType: "visualization"

  },

  qListObjectDef: {

    qDef: {

      qFieldDefs: ["[Subject]"],

      qFieldLabels: ["Subject"]

    },

    qAutoSortByState: {

      qDisplayNumberOfRows: 1

    },

    qShowAlternatives: true,

    qInitialDataFetch: [{

      qWidth: 1,

      qHeight: 1000

    }]

  }

});

let caseOwnerList = new qSessionObject({

  qInfo: {

    qType: "visualization"

  },

  qListObjectDef: {

    qDef: {

      qFieldDefs: ["[Case Owner]"],

      qFieldLabels: ["Case Owner"]

    },

    qAutoSortByState: {

      qDisplayNumberOfRows: 1

    },

    qShowAlternatives: true,

    qInitialDataFetch: [{

      qWidth: 1,

      qHeight: 1000

    }]

  }

});

 

$(() => {

  let caseNumberFilter = new Filter(caseNumberList, "#case-number-filter");

  let subjectFilter = new Filter(subjectList, "#subject-filter");

  let caseOwnerFilter = new Filter(caseOwnerList, "#case-owner-filter");

 

  Promise.all([caseNumberList.open(), subjectList.open(), caseOwnerList.open()]).then(() => {

    $("#loading").hide();

    $("#loaded").show();

    caseNumberFilter.init();

    subjectFilter.init();

    caseOwnerFilter.init();

  });

});

 

 

Don’t forget to run npm run webpack to build your bundle.js file.

 

If you’ve followed along, you should have 3 dropdowns on your page now that look like this - Enigma patterns. You can also just download the source code below, put it in the Extensions folder of your Qlik Sense Desktop installation, run npm install and then npm run webpack, and check out the project that way.

15 Comments