1 2 3 Previous Next

Qlik Design Blog

34 Posts authored by: Francis Kabinoff

I've been doing most of my projects lately with React and enigma.js, and developing some general patterns along the way that I've been working into a starter project that I can use as a starting point. I'd like to share that with you today, and potentially start a discussion around the design decisions I've made thus far.

 

The starter project can be found at GitHub - fkabinoff/qlik-react-starter: Starter project for rapidly developing QAP-powered apps. It's still very much in development, but there's a decent framework and dev environment in place, as well as a few useful components.

 

Overview

The project uses enigma.js to interface with the Qlik engine, along with React, webpack, Babel, ESLint, Sass, and a few other goodies. Really, a pretty standard React app with enigma.js added. The qDoc, which is the name the enigma.js docs use for the connection to the Qlik app is created in a plain Javascript module and simply imported to any file that needs to use it.

 

I use react-hot-loader, which allows you to change react components without refreshing the page or losing state, and I really like it. It has it's problems, most noticeably to me that it doesn't play very nicely with higher order components, but the ability to change my React components and not have to reload the page or lose state helps me develop faster.

 

I'm using the latest Babel preset, which enables es2015, es2016, and es2017, and Babel plugins which enable decorators, spread syntax, and class properties. I can't believe we actually wrote Javascript before this stuff.

 

I use the very popular airbnb config for ESLint. It helps keep the code healthy and uniform.

 

I make use of reactstrap, a library of Bootstrap 4 components for React, and SASS for customizing and mixing in Bootstrap styles, as well as creating a few utility classes. I prefer using reactstrap over some of the more feature-rich React UI libraries, because I often find it creates more work trying to make the components of those UI libraries, such as Material UI, play nicely with how I want a component in a Qlik-powered app to function since in a Qlik-powered app the Qlik engine acts as a state machine, and many of the UI libraries with more features expect the state to be managed and changed by the client. Reactstrap gives me what I need, without being too opinionated about it.

 

I use the "render prop" pattern in place of higher order components. The render prop pattern is simply passing a component as a prop to another component for that component to render it. It basically works like a higher order component does, but to me it's a bit simpler, a little more flexible, and doesn't mess with react-hot-reload or PropTypes checking.

 

Notably, I don't use a state manager like Redux or MobX, and this may be my most contentious decision. To me, the use of a state manager in most instances is redundant when building a Qlik-powered app. Qlik already manages my state, and shares it between components. I hardly, if ever, have state that needs to be managed outside of Qlik in my Qlik-powered apps, and using a state manager forces me to do extra work to keep the client state and Qlik state in sync. I don't like it.

 

It would be very interesting to hear what you think about these design decisions, and the decisions that you've made with your own Qlik projects.

 

Components

Now, let's take a look at the actual components. There are currently 4 Qlik-specific components, but I expect to add more as I continue developing this. I tried to design the Qlik-specific components to expect props which are identical to values defined in Qlik docs, and pass down values directly returned by the Qlik engine, in order to standardize as much as possible. Though in some instances, I broke that rule. For instance, since the QlikObject wrapper, which I'll talk about in a moment, is really only designed to work with an object that fetches one page, it didnt make sense to pass down the qDataPages returned by the Qlik generic object getData method. Instead, I simply pass down the object at the first index of qDataPages.

 

QlikObject.jsx

The QlikObject component implements the render prop pattern, and contains the logic to create and manage a Qlik generic object and its state. It passes down the Qlik generic object information to the component defined in the Component prop, which is responsible for rendering something.


QlikVirtualScroll.jsx

The QlikVirtualScroll component implements the render prop pattern, and paginates data on vertical scroll so only data in view gets rendered. It passes down a subet of the qMatrix to the component defined in the Component prop, which is responsible for rendering something. It is used by the QlikFilter and QlikTable components.


QlikFilter.jsx

The QlikFilter component renders a dropdown that acts as a Qlik Sense filter. It's meant to be used as the `Component` prop of a `QlikObject` component a type of `qListObject`. This component doesn't need to be passed any props other than the props passed to it by `QlikObject`.


QlikTable.jsx

The QlikTable component renders a table. It's meant to be used as the `Component` prop of a `QlikObject` component with a type of `qHyperCube`. This component requires a prop `columnWidths` to be passed to it, defining the widths of the columns of the table. This prop should be passed using the `componentProps` prop of the `QlikObject`.



Finishing up

Go to GitHub - fkabinoff/qlik-react-starter: Starter project for rapidly developing QAP-powered apps, download the zip, run npm install, and npm run dev, and you can check out the QlikFilter and QlikTable components in action, and check out the code. I'll be adding more components and styles as I'm still pretty actively developing it, so watch the repo if you're interested, and let me know what you think.

Wow! There are some really mind-blowing trends developing in BI and data analytics. Here are a few that have me the most excited, and my favorite examples of each from the past year.

 

Augmented Analytics

artificial-intelligence-2228610_640.jpg

Augmented intelligence incorporates machine learning to supplement and support human analysis. Qlik Sense is now capable of Advanced Analytics Integration through the use of server-side extensions, and Qlik Sense + Advanced Analytics = Augmented Intelligence. Go get started augmenting your own intelligence today, using Qlik Sense Advanced Analytics Toolbox.

 

speech-bubbles-303206_640.png

Conversational Analytics

As virtual assistants like Alexa and Google Assistant and chatbots in messaging services like Slack and Telegram become more ubiquitous, interest in conversational analytics continues to grow. Conversational analytics is the ability to provide a query in natural language, either spoken aloud or as text, and receive a response. The ability to simply ask a question and get an answer about your data is pretty cool. Ask some questions and get some answers by trying out QlikBotNode.

 

Immersive Analytics

thumbnail.png

Immersive analytics utilizes virtual and augmented reality to enable analytics in immersive environments. While widespread adoption of immersive analytics may be a little further off that either augmented intelligence or conversational analytics, the possibilities are truly exciting. Immerse yourself today and take a glimpse into the future with Qlik Healthcare Analytics AR.

I posted back in May about creating an app that allows a user to authenticate with Facebook, grabs the user's post data, generates a load script, and creates a Qlik Sense session app with enigma.js (When do you post to Facebook?). At the time, I still had to overcome a few hurdles to put it up live so you could check it out yourself. Since then, it's also become possible to create a Qlik Sense session app with the Capability APIs so that you can use the Visualization API with your on-the-fly Qlik Sense session apps, which I posted about in September (Creating apps on the fly).

 

The demo is now up live, and there's two versions of it, one using enigma.js, and one using the Capability APIs. They're pretty minimal when it comes to design yet, I plan on building them out a bit more still, but it's cool to just see this all working, authenticating a user with Facebook, grabbing their data, generating a load script, creating a Qlik Sense session app, and visualizing the data. It's easy to see how something like this could be applied to a business use case where users may have very user-specific data. Check the demos out below!


Capability APIs version - Qlik Sense Session App Demo - Capability APIs

enigma.js version - Qlik Sense Session App Demo  - enigma.js


One of the most useful features of Qlik Sense for me when developing a front-end is how easy it is to paginate data. Any front-end developer knows that updating thousands of dom elements is not exactly ideal, because dom updates are slow. But Qlik Sense makes it so easy to avoid ever having to do that.


For instance, take a look at the location dropdown for the Solar Eclipse demo at https://webapps.qlik.com/solar-eclipse/index.html. There’s over 29,000 rows there! But, here’s the trick – there’s only ever 10 rows in the dom at any time, and when a user scrolls what’s actually happening is the dropdown is receiving new data from Qlik Sense.


To paginate data with Qlik Sense is pretty simple. With enigma.js you’ll just create your object, either a hypercube or a list object, like usual, but you don’t need to pass it any qInitialDataFetch attribute. Then anytime you need some data, you just call a method to get data. Which method you call depends on your object. For a straight hypercube, you’d just call getHyperCubeData method like below -

 

doc.createSessionObject({
  qInfo: {
    qType: "visualization"
  },
  qHyperCubeDef: {       
    qDimensions: [
      // qDimensions
    ],
    qMeasures: [
      // qMeasures
    ]
}).then((object) => {
  object.getHyperCubeData('/qHyperCubeDef', [
   // this will fetch 1 page of data, but you could request multiple pages by adding more qPages here
    {
      qTop: 0,
      qLeft: 1,
      qHeight: 2,
      qWidth: 10
    }
  ]).then((data) => {
    // do stuff with data
  });
});

 

If your hypercube is in pivot, stack, or tree mode, or if your object is a list object, you’ll have to use the corresponding method in place of getHyperCubeData, which you can find here https://help.qlik.com/en-US/sense-developer/September2017/Subsystems/EngineAPI/Content/Classes/GenericObjectClass/GenericObject-class.htm.

 

If you’re using the Capability APIs, while not technically supported, the same methods work with the object that is returned from the promise (not the layout returned in the callback).

Overview

I've seen a few questions regarding integrating Qlik Sense visualizations into existing webapps lately, and figured I'd lay out a few tips here that may be of help.

 

There's really two ways of integrating Qlik Sense visualizations into your webapp, by either using the Capability APIs or iFrames. I'll discuss the pros and cons of both.

 

Capability APIs

The Capability APIs include the Root API, the App API, and the Visualization API, among a few others. The Root API is used to connect to an app, and either the App API or the Visualization API can be used to then embed Qlik Sense objects onto the sheet. There's quite a few resources regarding the details of actually doing this available already, so I'll skip that, but if there's any questions surrounding it, let me know. What I do want to talk about is the pros and cons of using the Capability APIs vs iFrames.

 

The pros of the Capability APIs are that all you have to do to use them is load the assets that are conveniently served from the Qlik server, supply a config, require the qlik.js file, and you're set. You can quickly get up and running, and you're able to not only embed existing visualizations, but you can create visualizations on the fly using the Visualization API and patch them as you go. So, it's easy to get going, and to embed, create, or even update visualizations on the page.

 

The downside is that the assets needed for the Capability APIs include Require.js and Angular 1.5.8, and they're both globals that can be quite intrusive, and force you to redesign your existing app to play nicely with their residency in the global scope.

 

iFrames

And that's exactly the best thing about iFrames. Since an iFrame is totally encapsulated from its parent page, there's no issues with existing assets and assets needed to embed Qlik visualizations colliding. And for all the other features that the Capability APIs offer, you can simply use enigma.js, which can share the same session as the iFrame'd objects.

 

One of the things to watch out for when using iFrames is that Internet Explorer, by default, only allows a maximum of 6 concurrent connections. So that's a limitation if you need to support IE. Another thing that you won't be doing is creating any visualizations on the fly.

 

Hybrid

To help overcome the websocket number limit, or if you really need to create visualizations on the fly, but also want to use the iFrame approach, what you can do is instead of embedding visualizations using iFrames, you can create a mashup, and then embed that into your webapp in an iFrame. Using HTML5's window.postMessage API, you can enable communication between your app and the mashup in the iFrame, and you can also still use enigma.js in your webapp.

Some of you may remember that I recently posted about a similar topic here When do you post to Facebook?, where I talked about creating an app that allows users to authenticate with Facebook, grab their data with Facebook APIs, use halyard.js to generate a load script, and then create a session app. But when I did this just a few months ago, one of the things I really wished I could do was use the Visualization API so I could simply create charts on the fly using Qlik Sense, but it wasn't possible then because a session app could not be created from or associated with the Capability APIs.

 

However, new APIs were introduced with Qlik Sense June 2017 that makes it possible to create apps on the fly using the Capability APIs, so now it is possible to create an app, load data, and create visualizations, all on the fly! This is made possible by the addition of some new methods to the Capability APIs, including the qlik.sessionApp method, the qlik.sessionAppFromApp method, the setScript method, and the getScript method.


There's already some pretty decent documentation for doing this at Qlik Sense Help - Creating apps on the fly, and I recommend you check that out, but I want to make everyone is aware of this very cool new capability of Qlik Sense June 2017, and just briefly address some issues you may run into.


First thing first, which is something I ran into while trying to create an example for this blog post -- anonymous users cannot create session apps, and this does not seem to be able to be controlled by security rules. Unless I'm missing something, it's entirely impossible. So, no anonymous users.


Next, there's been a few questions about just how to go about loading data. The simple answer is you need to set a load script using the setScript method, and then use the doReload method to load the data. But where do you get the load script from in the first place? Well you can use the getScript method on an app that already exists, or you can just write a script, though, of course, you'll need to also create the data connections you'll need somehow too if they don't already exist. And for many instances that I can think of where I would want to create an app on the fly, I'd use halyard.js. You could also create a session app from an existing app, get the load script from that app, and do some kind of search and replace on the load script which would custom tailor the data for the current user. The point is, there's a lot of possibilities, and it's really up to you and your use case how you want to go about this.

 

With being able to create apps, load scripts, and visualizations all on the fly, there's really a ton of potential to do some awesome stuff here. It would cool to hear about how some of you are using these capabilities!

I've seen a few people asking recently if and how they can use webpack to build their mashup. The answer is yes, and I'm gonna discuss a few implementation details and provide some example code.

 

So to use the Qlik Capability APIs, you're probably already aware that you need to load the Qlik Capability API code, which includes loading a custom require.js file, and then requiring the qlik.js file through the loaded instance of require.js. The thing is, there's really no way to get around this, that's how the Qlik Capability APIs are loaded.

 

But you can still use webpack for all of your own project code, you just have to decide how you are going to load the custom require.js file and where you'll use the require.js instance to require qlik.js. This is how I've been doing it.

 

First, I load the Qlik custom require.js file in a script tag in the head of the document. Can you get fancier if you'd like with something like the script-loader, yea sure you can, but the goal here is to load that Qlik custom require.js file in a global context, and to me the simplest way to do it is to just include it in a script tag in the head of my html document.

 

Then, you'll need to set the require.js config and require the qlik.js file through require.js somehow. The trick here is that the require.js instance can be accessed with window.require. Also, since requiring files with require.js is asynchronous, and you'll almost certainly want to return some stuff when qlik.js is done loading, it's useful to use a promise here. This is what my module looks like for this in ES2015 -

 

webpackcapabilityconfig.png

 

You'll notice the config object which you should be used to for mashups, then how I'm using window.require.config to set the require.js config. Also, I explicitly set the path for 'qlik' because I find this helps avoid some errors, especially with regards to loading extensions. Then, I export a promise which resolves with the app from the openApp() method as the value. You can resolve this promise with the 'qlik' object, or multiple apps, or whatever your needs are, but for myself, most of the time, I'm just opening 1 app and I just resolve the app.

 

So in summary, if you want to use webpack with the Capability APIs then the Qlik custom require.js file will need to be loaded in a global context in some way, and then you'll be able to access the require.js instance on 'window.require' (but not just simply 'require' since webpack will use that keyword).

With the Qlik Sense June 2017 release comes server side extensions, which allow you to extend the Qlik built-in expression library with functionality from external calculation engines. We've released an open source server side extension that enables interaction with R, and if you use the R-Plugin, you know that you have to start Rserve.exe and SSEtoRserve.exe, then restart the Qlik Sense Engine Service. What I'm going to walk you through is how to setup Rserve.exe and SSEtoRserve.exe to run as services, and then make the Qlik Sense Engine Service depend on those services so that they start whenever the Qlik Sense Engine Service starts.

This post is for people who have already installed the open source R-Plugin and would like to run the plugin as a Windows service instead of starting it manually. If you haven't gotten started with the open source R-Plugin, and would like to, please check out this guide sse-r-plugin - get started.

 

The first thing we need is NSSM. Download and unzip, then open a command prompt with admin privileges and change directory to  <path-to-nssm>\nssm-{x}.{x}{x}\win64.

nssm.png

 

Now we need to install our 2 new services. In the command prompt, enter nssm install RserveService. An interface will pop up where we can define the service. Enter <directory>\Rserve.exe into "Path", and set "Startup directory" to the directory Rserve.exe is in. Then, go to the "Environment" tab, and enter PATH=%Path%;C:\R\R-3.4.1\bin\x64 (assuming you have R-3.4.1 installed in the C:\R directory, otherwise make adjustments as necessary). Now click the "Install service" button.

rservepath.pngrserveenv.png

 

Next, we need to install a service for SSEtoRserve.exe. So, again, at the command prompt enter nssm install SSEtoRserveService, and then enter <directory>\SSEtoRserve.exe into "Path", and set "Startup directory" to the directory SSEtoRserve.exe is in. Now click "Install service".

ssetorserve.png

 

Now that the two services are installed, we can set the Qlik Sense Engine Service to depend on them, so that any time the Qlik Sense Engine Service starts, these services will be available. Back at the command prompt enter nssm edit QlikSenseEngineService. Go to the "Dependencies" tab and add RserveService and SSEtoRserveService, each on their own new lines under the already existing QlikSenseRepositoryService.

engineservice.png

 

All of you have to do now is restart the Qlik Sense Engine Service and you should be good to go.

If you're creating a mashup that includes embedded Qlik Sense charts, there's a couple issues you may run into that you'll want to be aware of.

 

First, if your mashup scrolls, and you scroll down the page, you will notice that the tooltips on the Qlik Sense charts are not positioned correctly, like below:

2017-06-07 11_20_25-Marató Barcelona.png

The tooltips will get more and more displaced the further down the page you scroll. For the tooltips to be positioned correctly, you'll have to adjust how scrolling happens on your page a little bit.

 

The easiest option that you can implement with just some CSS is to set the html and body tags to a height of 100%, hide the overflow on the html tag, and add use overflow: auto on the body tag. That looks like this:

html, body {
height: 100%;
overflow: hidden;
}
body {
overflow: auto;
}









 

Now the tooltips will no longer be displaced when you scroll your mashup. However, there are times when it may be necessary to watch the scroll position of the page, or be able to set the scroll position of the page programmatically, and for some reason, when using the above method the scrollTop attribute of the body tag never actually updates, so there is no way to observe when the page scrolls or set the scroll position programmatically.

 

In instances like this, just a simple extra step will fix the issue. What you'll need to do is add a wrapper element that wraps the entire content of your mashup, and also set that to a height of 100%, and put the overflow on that element. That element's scrollTop will be set correctly, and you can observe or set it programmatically. So, it would be something like this:

<head>
<style>
  html, body, #page-content {
    height: 100%;
    overflow: hidden;
  }
  #page-content {
    overflow: auto;
  }
</style>
</head>
<body>
<div id="page-content">
  <!-- All of your content -->
</div>
</body>









 

The other issue you may run into is the chart tooltip not be styled correctly, since it may be affected by the CSS in your mashup. The most common example I see of this is if using Bootstrap v4. Bootstrap v4 adds some negative margins to the .row class, and the tooltip also uses the row class, and it makes the text in the tooltip get cut off, like this:

2017-06-07 12_11_20-Marató Barcelona.png

To fix this, and any other styling issues you may have with the tooltips, it's helpful to be able to inspect it. A div with the class .qs-chart-tooltip will be appended near the end of the body. If you inspect the page and find that element, then expand it, it's first child element is a div that has display: none set, just uncheck that style and you'll be able to view the tooltip in it's last location. Then you can continue to expand those child elements and inspect the element, looking for any issues. In the case I show above, as I stated, the problem is with Bootstrap v4 and it's negative margins on the .row class. So the css below fixed the tooltip:

.qv-chart-tooltip-inner .row {
margin-left: 0;
margin-right: 0;
}


 

So, now you should be able to address these Qlik Sense chart tooltip issues I often see in mashups. If you have any questions, let me know!

Halyard.js is a new open source library that simplifies the Qlik Sense data load experience as it abstracts away the need to write a load script. Halyard.js includes a mixin for enigma.js for loading your halyard representation into the QIX engine. Using halyard.js and enigma.js, it’s now pretty trivial to generate a load script and create an app on the fly.

 

I built an app that uses Facebook’s API to grab user’s posts, halyard.js to generate a load script, enigma.js to create a session app and objects, and then displays a few filters and a visualization just to test out the whole flow, and it was awesome. I made a video of the results below, check it out.



I want to put this app up live so everyone can try it out because it's so cool, but I’m waiting on Facebook approval. In the meantime, you can grab the source code below and try it out on your own machine.


There’s a few steps to do that. First, download the source code and run npm install. Then go to the qapp.js file and enter session info for your Qlik Sense server in the config variable on line 8. Next, you’ll have to go to https://developers.facebook.com/ and create an app. Once your Facebook app is created, grab the App ID and enter it in the fb.js file, on line 26.

For your Facebook App to be able to authorize users, you’ll need to add a platform and app domain in the “Settings” page of the Facebook app.

It’s up to you how to host this. I’ve included a .qext file if you just want to use Qlik Sense Desktop.

 

Once you’ve done that, run npm run webpack to build the project, and it should be good to give it a go!

Did you know that you can register extensions on the fly in mashups? That's right, you can register an extension in a mashup to use in that mashup, regardless of whether the extension is already loaded into your Qlik Sense environment. That means you can distribute your mashup with any extensions it uses as one package, and you have total control of the extension version your mashup is using.

 

Doing it is pretty straightforward. You just need to load the extension code into your mashup, then register it. It'll look something like this.

require(["js/qlik"], function (qlik) { //load qlik module

  require(["path-to-my-extension/my-extension.js"], function(myExtension) { //load extension entry point
   qlik.registerExtension( 'my-extension', myExtension ); //register extension
    //do stuff with extension
  });

});








Notice that I loaded the extension entry point after loading the qlik module. That's because many extensions use the qlik module, and if your extension loads the qlik module but you try to load your extension code before loading the qlik module in your mashup, you'll end up with errors. So better just to load the extension after the qlik module has been loaded in your mashup.

Once the extension has been registered you can do stuff with it, like use it with the Visualization API. An interesting use case is if you are loading objects that use an extension from an app into your mashup. The version of the extension you register with the mashup will override the extension loaded into your Qlik Sense environment, which can be really useful.

 

You can read more about it and see a few examples here Creating extensions on the fly.

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.

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.

If you haven't yet heard, Qlik recently released 3 open source software projects, now available for use and open to contributions!

 

enigma.js

enigma.js is a framework that communicates with Qlik Sense backend services. The Rest Service allows communication with Qlik REST APIs, while the Qix Service allows communication with the Qix Engine. enigma.js is now my preferred method for communicating with Qlik Sense backend services, and if you're a developer that works with javascript, I strongly suggest you check it out.

 

leonardo-ui

leonardo-ui is a UI component library that has a Qlik Sense look and feel. It works well with popular CSS frameworks such as Bootstrap and Foundation. Using leonardo-ui can speed up your UI development process, and keeps styles in sync with other Qlik Sense assets.

 

after-work.js

after-work.js is a unified testing framework capable of performing unit, component, integration, and end-to-end tests. It is the testing framework used by enigma.js and leonardo-ui.

 

Contributing

You can contribute to any of the Qlik open source projects. Each project has its own contribution guidelines, linked below.

Contributing to enigma.js

Contributing to leonardo-ui

Contributing to after-work.js

It's almost the end of 2016, so I figured I'd look back at some of the stuff the Qlik developer community has created this year. This list does not attempt to be a "best of 2016" list, there were so many great things created this year that I wouldn't even know where to begin ranking them. Rather, this is just a few highlights I selected. Hope you enjoy!

 

 

Qlik Playground

Qlik Playground lets you quickly test out all kinds of stuff with the Qlik Engine and APIs. It gives you a sandbox to play in, and also has some cool showcase projects you can play around in, as well as learning resources. It's very cool.

 

RxQAP

RxQAP is a reactive JS wrapper that currently supports the Qlik Engine API. Observables are super awesome, and while I haven't had a chance to build a project with this yet, it's very high on my to-do list.

 

Sense Search Components

Sense Search Components lets you easily embed search in a web app. And it works with the App API or the Engine API!

 

Lars Mashup Template

Full disclosure, I built this one. It's the template I've been using for most of the year to quickly spin up boilerplate for any App API projects.

 

qSocks snippets extension for VSCode

Adds snippets for qSocks to VSCode. If you're not using VSCode, you should. If you haven't tried qSocks, check it out. And if you're already using both, this project is for you.

 

Google Annotation Chart for Qlik Sense

Google annotation charts are sharp. This is an integration of google annotation charts for Qlik Sense.

 

Climber Selection bar

An extension that allows you to add selection capabilities to your app that look sharp and have some cool functionality.

 

Dynamic Table with Sparklines

This extension allows you to create tables that include sparklines. It looks really good.

 

sxBlockRain

This. Is. Necessary.

 



You can check out Qlik Branch for even more awesome Qlik developer community created projects, and check out the comments to see recommendations by others and add your own!

Filter Blog

By date:
By tag: