Using CSS in Qlik Sense

Whether you are building extentions in Qlik Sense or including Qlik Sense objects in a mashup you very likely will need some CSS. There are even some things you can really only do with CSS. And since Qlik released Themes support there is also the possibility of adding your own stylesheets to the Qlik Sense client with or without other changes, like extensions or mashups.

But stylesheets are different. They are not like javascript, with a defined API with methods, parameters and return values. Instead they have their own logic, with CSS selectors, specificity and interdependance. When something breaks, you won’t get error messages but things will just look wrong, not be visible at all or stop working. And very little is documented.

The qv-object rule

When you build an extension you can include CSS rules. To make it possible for you to add rules that affect only your extension Qlik Sense will add a CSS class with the format ‘qv-object-[extension]’ to the HTML element your extension is rendered in. So if your extensions qext filename is xxxx.qext, it will add the CSS class ‘qv-object-xxxx’. The idea is that you should prefix all your CSS rules with qv-object-xxx, somthing like this:

.qv-object-xxx .qv-object-content {
  overflow: auto;
}

.qv-object-xxxx ul {
  list-style: none;
}

.qv-object-xxxx .important {
  color: red;
}

If you don’t do this, your styling might affect other content in the page. Sometimes that is what you want, but for normal extensions you should not do that. You might break other stuff in the page and create bugs that are hard to find.

Extension HTML structure

The structure your extension is rendered into looks like this(this is a simplified structure):

<article class="qv-object qv-object-xxx">
  <div class="qv-inner-object">
    <header class="qv-object-header">
      <h1 class="qv-object-title">
        <h2 class="qv-object-subtitle"></h2>
      </h1>
    </header>
  </div>
  <div class="qv-object-content-container">
    <div class="qv-object-content">
      Your extension renders here
    </div>
  </div>
</article>

This means that:

  • a .qv-object-xxxx rule will affect the whole box the extension is rendered in, including title
  • a .qv-object-xxxx .qv-object-content rule will affect just the extension body, not titles etc
  • a .qv-object-xxxx .qv-object-title will affect the title and possibly the subtitle
  • hoover buttons are outside of the qv-object-xxxx element and cannot be styled based on the qv-object-xxxx class

Note that this structure is as far as I know undocumented. It has been pretty stable so far, but still might change in the future. The same structure is used for both built-in and extensions, might be good to know if you are building a mashup.

Loading your CSS

To make your CSS rules work you need to load them in the browser. They should be loaded once, even if your extension is used several times in the same page/sheet. The best place to do this is at the very start of your code, at the beginning of the callback function in the define call.

There are two ways of loading the CSS. First you could add a link to the CSS file to the HTML page. An easy way to do this is with the requirejs css plugin, like this:

define( [ "css!./style.css"], function () {
/* requirejs will add a link to the document, nothing more is needed */

You could do this yourself too, but there is really no reason to do this, and you need to be careful and handle all cases, like virtual proxies and when your extension is used in a mashup not served by Qlik Sense.

The other possibility is to add the contents of your stylesheet to a style element in the document header. That requires a bit more code:

define( ['jquery', 'text!./style.css'], function ($, cssContent ) {
    $( '<style>' ).html(cssContent).appendTo( 'head' );

You can also do it without using jQuery, with standard javascript, but that means a few more lines.

The first method, adding a link to the stylesheet, has the advantage that references to external resources, like images and fonts, works, provided that you include those files in your extension too. But if you are running the extension in a mashup on an external (not Qlik Sense) server you might run into problems, since there are security restrictions on loading CSS from other servers.

That’s why I generally use the second method, adding the stylesheet text to the document. Images referenced in the stylesheet then needs to be Base64-encoded and included in the stylesheet itself. That works well for small images, if they are not too many. But if you have many images, or need to load for example font files, you should probably go for the link method.

Who am I? Getting the user id in a Qlik Sense Extension or mashup

Sometimes you need to know who the user is in your Qlik Sense extension or mashup. Perhaps you want to help the user filling in some data, display notifications to the user or something else that I can’t even think of.

getAuthenticatedUser call

There is no property containing user id (at least not someone that I know of), you’ll need to call an API one way or the other. The relevant call in the API is getAuthenticatedUser(), available on the global object.

In an extension, you should use the global property available on the app, so the call would be app.global.getAuthenticatedUser(). In a mashup it depends.. If you need to do this before the app is opened, you could use qlik.getGlobal(config) to get the global object. But be aware that this will mean another open web socket. If you can wait, it’s better to use app.global, since that means you will reuse the existing web socket.

Like most API calls, getAuthenticatedUser() is asynchronous, so you need to call it like this:

qlik.currApp(this).global.getAuthenticatedUser().then(function(reply){
    console.log('user',reply.qReturn);
});

If you run this in Qlik Sense Desktop, you should get the string ‘Personal\Me’. But in Qlik Sense Enterprise it’s a bit more complicated. Ehen I run it I get: ‘UserDirectory=UPPER88QS; UserId=erik’. So this is a string, which contains two key-value pairs. You’ll need to parse this, to grab the UserId, or if you really want both values (perhaps you have more than one UserDirectory? Perhaps this is different between production – test – development?

Using OSUser()

But there is another method. Youd could use the Qlik Script system function OSUser(). In a mashup this is probably not a great idea, since you would need to either create a generic object containing OSUser(), or call the Evaluate method, not even included in Capabilities API, only in Enigma.

But in an extension the framework always creates a generic object for you and gets the layout. So you could add ‘OSUser’ to your initialProperties, and get the result in your layout, something like this:

initialProperties: {
   user: {
      qStringExpression: '=OSUser()'
   }
},

In most cases you would of course have other stuff in your properties too, and they can easily be combined. Don’t show this in your property panel, if you don’t want your users to be able to change it.

This will mean that you in layout.user will have the user the same way as when you called getAuthenticatedUser(), you will probably need to parse it, but you don’t have to handle the fact that is it asynchronous.

A warning

This is all client side. That means it’s not really safe, you should not use this to restrict user access – it’s OK to use this to hide operations from the user that they should not be allowed to use, but you need to verify the user serverside too, in a safe way, that can not be hacked. But that’s another topic…

Pros and cons of Qlik Sense mashups

Recently I got the question on if I would recommend using Qlik Sense mashups. That got me thinking, of course the question is not so simple: it all depends. As with most things there are pros and cons…

Why would you use Qlik Sense mashups?

There are several reasons for using Qlik Sense mashups. A very common one is that you want to apply your companys profile and have a look-and-feel more like that you have for other systems. When I started building mashups for customers Qlik Sense hade no support for theming, so if you wanted to change things like fonts you needed to do that in a mashup. Now you can do this in a theme, but I honestly don’t know how much custom themes are used today.

Another common reason is that you want navigation to work another way than it does in Qlik Sense. Perhaps put sheets in a hierarchical structure or have views targeted to different user categories.

You might also mobile support to work differently than it does in standard Qlik Sense. In a mashup you can leverage open source CSS framework like Bootstrap or Bulma which gives you possibilities you do not have in the built-in Qlik Sense client.

Another reason is to simplify the user interface. In a mashup you can focus on the workflow that’s most important for your users (usually analyse data) and skip features like edit mode, storytelling, data load and modelling. This could potentially let your users get started faster and reduce the need for training, a big cost in a large dashboard projects.

You might also want to add features not in the standard Qlik Sense client. Examples could be commenting, writeback, sharing, exports not covered in the standard client, integration with other systems. Features like alternate state and default (startup) bookmark used to fall in this category, but are now included in the standard clent.

Problems with using Qlik Sense mashups

You should realize that mashups require HTML, CSS and javascript knowledge, a skillset most Qlik developers don’t have. You need to make sure you have both web developer skills and Qlik skills in your organization, whether you have that inhouse or work with a partner. And even though there are lots of web developers out there, very few of them know how to work with the Qlik APIs.

And the javascript environment is changing fast. While Qlik script has changed little in the 12 years I have worked with Qlik, the javascript environment has changed a lot. The language has a lot of new features, and frameworks, build tools and libraries has changed even more, so that the environment used in many mashups, with angularjs 1.x, today is pretty old and completely different frameworks (React etc) dominate, at least in new projects.

The tools included with Qlik Sense, like dev-hub, the Mashup Editor and the templates included makes it easy to create a mashup with only drag-and-drop and really no programming, but for production level projects it really is not enough. You would want stuff like multi-page support (including only loading Qlik Sense objects when they are needed), version handling and a dev environment where you can use npm packages, build tools etc.

Some advice on mashup development

So, you have decided that your use case is best solved with mashups. But how should you go about developing it? Here are some advice based on my experience with building mashups.

Recognize that you need web developer skills. The mashups itself is largely a web developer project. You need experience on web development, and on troubleshooting and debugging web apps. Someone with deep knowledge of HTML, CSS, Javascript and Qlik APIs is needed.

Web developer skills is not all. For users the most important part of your mashup is the data. For that you still need Qlik developer skills. In many cases you would have two kind of developers, those building charts and those working on the mashup itself.

Use version control. I used to say that version control is what differentiate professional development from private hacks. Today this is no longer true, even many hobby projects use version control. Set it up early in your project, commit changes often, make suire you know exactly what code runs in production.

Separate Qlik Object definitions from the javascript code. Try to avoid having Qlik Sense object definitions, field names etc in the actual code. Separate object id’s etc into separate configuration files or use tags in the Qlik Sense apps. This improves reusability of your mashup code and simplifies things a lot.

Use a CSS framework. A CSS framework like Bootstrap or Bulma helps you a lot with making your mashup responsive and good-looking. You might want to modify it by overriding the styles with your profile colors, fonts etc, but add those later.

Files in Qlik Sense extensions

Qlik Sense allows you to add extensions to your installation. In a server environment you do this by importing a zip file, containing one or several extensions. In Qlik Sense Desktop you can unzip (the same) zip file to a directory under Documents/Qlik/Sense/Extensions.

The qext file

The qext file is what defines the extension. It’s a JSON file, it has to be valid JSON, but very few properties are mandatory. The qext file defines the extension root, Qlik Sense will make files the directory where the qext file and all directories under it available for HTTP calls to the server. So if you add an extension with the file xxxx.qext, you can fetch the file from the server with:

HTTP GET /extensions/xxxx/xxxx.qext

And all other files in the extension can also be fetched the same way:

HTTP GET /extensions/xxxx/xxxx.js

Sub-directories can also be used:

HTTP GET /extensions/xxxx/lib/jquery.js

Not that the directory name on disk is not relevant. On Qlik Sense Desktop you can build your own directory structure. Extensions in Dashbord bundle and Visualization bundle are kept in their own sub-directory, which doesn’t affect the URL used to load them. But if you do build a structure for your extensions, be careful that you do not keep duplicates of the same extensions. If you do you will not know which copy is actually loaded.

Multiple qext files

You can also have multiple qext files in the same zip file, which will mean that several extensions, and their files, are made available. Qlik Sense Enterprise will not allow you to have more than one qext file in the same directory, you can do this in Qlik Sense Desktop, but it is confusing, so you probably shouldn’t.

Multiple qext files is useful when you want to use the same files in more than one extension, like some library, CSS files etc. You can then put common files in one directory and add a qext file in it to make it available for all extensions. By putting them in the same zip file you make sure that they are updated at the same time as the extensions using the library. Another effect is that if the user is using two different extensions that use the same library, files will only be downloaded once. If every extension has it’s own copy of a library, it will be downloaded multiple times.

For Qlik Sense Enterprise you have no control over how the extension files are stored. It is not hard to find the files on disk, but the repository is responsible for storing them and serving to the client.

The extension type

One of the fields you always should have in the qext file is the type. In a visualization extension, that you should be able to use in the Qlik Sense client, it should be set to ‘visualization’:

"type": "visualization",

If you set it to something else, the Qlik Sense client will not load it and your users will not be able to create visualizations using it.

For mashups it is usually set to ‘mashup’:

"type": "mashup",    

This is not absolutely necessary, your mashup will still work even if you set the type to something else, but if you want to manage it and possibly edit the file in Qlik Sense dev-hub you need to set the type to ‘mashup’. That also means that if you want to hide your mashup in dev-hub, set the type to something else (perhaps ‘webapp’ ?).

There are also a few other types used by Qlik Sense: ‘visualization-template’ and ‘mashup-template’. These types are used for the templates used in dev-hub when you create a new visualization or mashup.

If you plan to build several mashups creating a good mashup template with the look-and-feel you want might be a good choice.

The wbfolder.wbl file

One of the most misunderstood file in extensions is the wbfolder.wbl file. It is simply a list of files in the extension, with one line for every file, terminated by semicolon:

com-qliktech-horizlist.js;
com-qliktech-horizlist.qext;
horizlist.css;

The wbfolder.wbl file is not mandatory. It is not needed for your extension for work, if you do have one it does not need to contain all files. It is only used by the dev-hub editors. Without it you cannot open or duplicate the extension in dev-hub.

If you do have it, only the files listed in wbfolder.wbl will be available in the editors. Also only the files in wbfolder.wbl will be copied if you make a copy of the extension or mashup. The other files will stil be available over HTTP, Qlik Sense will serve them.

A scenario I have seen used, is that a web developer buiilds a mashup, but then users (super-users ??) can modify files in it, like HTML, CSS or configuration files. In that case you can add a wbfolder.wbl to the extension containing just the files users should be able modify, but not other stuff, like libraries, possibly some JavaScript etc.

Qlik Sense Global object

Sometimes when you are working with the Qlik Sense APIs you need access to information that is not connected to the Qlik Sense document you are working with, but rather to the QIX Engine running.

You find the apis for this in the global object in the Capabilities APIs, or in the Global class in the Engine API. While the Capabilities APIs only include very few methods, the full list of calls in the Engine API is much longer.

Finding the Global object

While there is a method in the Capabilities API to get the global object, in most cases you should not use it. In fact if you are building an extension you never should. Instead you should use the global property on the app.

That is because the getGlobal() method will open a new web socket to the engine, while app.global property will use the web socket used for the app. There is not reason to open another web socket to the server when you already have one, and in an extension there always is one.

The only situation getGlobal() should be used is when you do not have an open web socket, like in a mashup before the app is opened. And in that case you might be better off with a REST call.

Chrome developer tools in Qlik Sense extension development (part 2)

As I wrote in Part 1 Chrome Developer tools is perhaps the most important tool for a Qlik Sense extension developer. Some important points in part 1:

  • you should turn caching off in the network tab, and keep developer tools open to make sure files are not cached
  • Qlik Sense client catches program errors in your extension, so if you need to see them temporarily check the box ‘Pause on caught exceptions’. You can’t keep this box checked all the time though.

In this post we will continue with some more useful stuff.

Checking web socket traffic

As you probably know communication with the Qlik Engine is over web sockets. You find the web socket in the Network tab, just click on WS and you will find it. Click on the websocket and then on messages, and you will see the communcation, something like this:

Lots of information, lots of transactions. Some things you should know:

  • messages from client to server has a dark background, messages from server to client a white background
  • all messages are in JSON format
  • if you click on a message it will be displayed in a format that’s easier to read in the area below
  • you can filter messages by typing in text in the field where it says ‘Enter regex..’

Connect request and reply

What you see in the web socket log is simply a list of all messages sent and received, in chronological order. Since commands are processed in paralell, the reply for a specific request might not at all be anywhere near the request. Request and reply are connected with the ‘id’ attribute (that’s a feature in the json rpc protocol, which Qlik Sense is built on). This means we can filter on the id attribute to find the reply for a specific request. If you try typing in “id”:1, in the filter box you will get request #1 and it’s reply:

Handling handles

So now we know how to find a request and it’s reply, but to really understand what’s happening we also need to understand handles.

Handles is a Qlik Sense specific concept, not part of Json RPC but an addition to it, made by Qlik. The QIX protocol is object-oriented, meaning that all commands are sent to an object. When you connect to Qlik Sense engine, the only handle available is the global handle, -1.

The first thing you do in most cases is call OpenDoc to open your Qlik Sense app (or Doc, as its called in the QIX documentation). If that succeeds you will get a handle (called qHandle) back in the reply. You can them send Doc commands to this handle (almost always handle 1). If you type in the filter “handle”:1, you will see all app commands used:

As you can see there is a lot of them. Most of them fetches or creates objects in the app, with GetObject or CreateSessionObject. You do not see the replies, only the requests, since the replies do not contain the handle. But if we instead filter by the id of one of the GetObject calls, (in my case id 13) we can see what happens:

As you can see in the reply there is a new qHandle in the reply, #5. So now we can filter on “handle”:5, and see what commands were sent to this object. If you want to know what handle is used for your extension, there is as far as I know no way to get that information from standard Qlik, but you can use my Chrome Extension Add Sense.

Find commands that invalidates object

Handles are used not only to refer to objects when you send commands, but also for invalidation. When you change selections (make a new selection, clear selection, apply bookmark etc) QIX engine tells you what objects are no longer invalid. It does this with the ‘change’ array (another addition to JSON rpc) which contains handles for the invalidated objects. So if I for example want to find commands that invalidated opbject 13, I can filter on “change”:\[.*13.*\] (note: a regex, since the handle can be at any position in the array):

We only get the replies for commands that invalidated the object, to get the actual command you need to use the id field to find the actual command and handle.

Guide to Qlik Sense ApplyPatches

One of the more powerful calls in the QIX Api is the ApplyPatches call. It allows you to dynamically add, change or remove basically any property of a generic object. With it, you can allow users to dynamically change visualizations: reorder data, change dimensions and measures, add limitation etc etc. But using it in your implementation can be tricky.

Parameters

The ApplyPatches call only has two parameters (plus the handle, identifying which object we are patching:

NameDescriptionMandatoryType
qPatchesArray of patches.YesArray of NxPatch
qSoftPatchPatches are not savedNoBoolean

So it’s an arry and a boolean (true/false) flag. Let’s start with the flag. Setting the qSoftPatch flag to true does mean that the patched property is not saved, but it also has some other consequences:

  • you can change objects that you otherwise cannot, like published objects, or objects in apps you cannot change
  • the changes will only affect you, other users will not see them, they might very well apply a completely different patch to the same object
  • the changes will only affect the current session. If you disconnect and connect again, they will be lost and you will start over with the original version of the object

So if your goal is to allow users to dynamically change visualizations you would want qSoftPatch to be true. While the default value false works OK in Qlik Sense Desktop, it’s generally not something you would want to use in a server environment.

The array of patches

The array is a bit more difficult. It’s an array so you can change several properties with one call. For example if you change dimension or measure you should consider changing the title too, and probably do that in the same call.  Every entry(almost) in the array has three parameters:

NameDescriptionType
qOpOperation to perform. Add, remove or replace.String
qPathPath to the property.String
qValueThe value of the propertyString

The first parameter, qOp, is an easy one. In most cases you want to change a property, use ‘replace’. Occasionaly you want to add a property, use ‘add’. Rarely you want to remove a property, in that case use ‘remove’. 

The second parameter, qPath, is the path to the property, using / between the property names (not . as in javascript). Remember:

  • you’re patching properties, not the layout, so it’s /qHyperCubeDef/… not qHyperCube
  • for arrays you need to include the position in the array, and numbering starts with zero, so first dimension is /qHyperCubeDef/qDimensions/0/ etc
  • property structures can be complicated, speciallly the hypercube..
  • you can use my Chrome extension Add Sense to create the path argument. Show properties for the objects, switch to patch mode, copy the path

The second parameter, qPath, is the path to the property, using / between the property names (not . as in javascript). Remember:

  • you’re patching properties, not the layout, so it’s /qHyperCubeDef/… not qHyperCube
  • for arrays you need to include the position in the array, and numbering starts with zero, so first dimension is /qHyperCubeDef/qDimensions/0/ etc
  • property structures can be complicated, speciallly the hypercube..
  • you can use my Chrome extension Add Sense to create the path argument. Show properties for the objects, switch to patch mode

Some examples

A common case is when you want your users to be able to change the ordering of for example a table dynamically. Ordering of a hypercube is determined by the qInterColumnSortOrder property, an array with the column numbers in the order they should be used for sorting. If you want say the third column (which has number 2, since numbering starts with 0) you move the number 2 to the beginnging of the array and call applyPatches:

model.applyPatches( [{
  qPath: '/qHyperCubeDef/qInterColumnSortOrder',
  qOp: 'replace',
  qValue: JSON.stringify(sortorder)
}], true );

You can find a more complete example in the mashup I showed at Qonnections back in 2015.

Another example is when you want to reverse the ordering of a hypercube. You do this by toggling the boolean value of qReverseSort in the dimension or measure where you want to change the order. For the first dimension this will be:

model.applyPatches( [{
  qPath: '/qHyperCubeDef/qDimensions/0/qDef/qReverseSort',
  qOp: 'replace',
  qValue: JSON.stringify( !reversesort )
}], true );

Qlik nebula.js – first web app

A while ago I did my first test project with Qlik’s new open source library nebula.js (it’s available here). That time I tested the visualization part, corresponding to extensions in the current product (but hopefully used also for visualizations delivered by Qlik). Now it’s time to try it from the other side, building a web app with these new kind of visualizations.

Terminology first: the term mostly used in Qlik Sense is mashup. That’s because the current javascript API was originally designed for a mashup scenario, where you wanted to add Qlik Sense objects to an existing web page. But what it’s mostly used for today is building web apps, that is pages that include only Qlik Sense objects. And that is what I intend to do in this small test project.

Setting up

Nebula.js includes a small example app. You find it here. It’s just a few files, since it actually downloads the libraries (enigmajs, nebulajs) over the internet. So if we just copy those files to a local directory we have got a starting point.

The example uses a docker installation and a session app, but I want to use my Qlik Sense Desktop and the Consumer Sales app. To fix that I revised (and simplified) the connect.js file so it looks like this:

window.connect = function connect(host, id) {
	return fetch('https://unpkg.com/enigma.js/schemas/3.2.json').
	then(response => response.json()).
	then(schema => window.enigma.create({
		schema,
		url: `ws://${host}/app/${id}`,
	}).open().then(qix => qix.openDoc(id)));
};

And in the index.js file add host and app:

connect('localhost:4848','Consumer Sales.qvf').then((app) => {

and we have got something that’s working. Start Qlik Sense desktop, make sure your test project is served (I use serve), and open the file in the browser. You’ll se a selection toolbar, and an area that displays the simple supernova that’s included:

If you wonder how come my selection toolbar shows selections, that’s because I opened the same app with the same url(use http://localhost:4848/sense/app/Consumer%20Sales.qvf ) and made a selection. Session sharing does the rest.

Load nova-table

OK, so now we have got a page using nebulajs and connecting to an app, and the selction toolbar works. But how do we use supernovas like the one I made in my first test drive? Well first we need to make it available in our project. Just copy the files from the dist directory in nova-table and put it in your project. I have used a sub-directory called novas, since there is actually nothing super about nova-table. You can then load it in index.html:

  <script src="./novas/nova-table.js"></script>
  <script src="connect.js"></script>
  <script src="index.js"></script>

It needs to be before index.js for this to work.

Unlike the current Qlik Sense javascript libraries, nebulajs is not bundled with a module management library like requirejs. Instead it will see if there is a module manager available and use it if there is. If not it will simply add the module as a global function. So we can access it simply as window[‘nova-table’]. In a real project we would not really want everything defined globally, but this is just a quick test.

To make a solution that can be extended with more visualization, we create a novas object, where we can put all our visualizations, and then grab the right one using the type, with the sn from the initial example as the default:

const novas = {
 'nova-table': window['nova-table']()
};
const sn = {
 component: {
  mounted(element) {
   element.textContent = 'Hello';
  },
 },
};

const nebbie = window.nucleus(app, {
 load: (type, config) => config.Promise.resolve(novas[type.name] || sn),
});

Add a table

So now we have got the nova-table module loaded, let’s create one and add to the page. We’ll make a session object, with the definition in the javascript file, so first we change the HTML file to:

  <div class="content">
    <div class="toolbar"></div>
    <div class="object" id="nova01"></div>
  </div>

And in the javascript file we use the create method to create a nova-table object, with properties that contain a hypercube and inject it into the HTML element ‘nova01’:

nebbie.create({
 type: 'nova-table',
}, {
 element: document.getElementById('nova01'),
 properties: {
  qHyperCubeDef: {
   qDimensions: [{
    qDef: {
     qFieldDefs: ['Customer']
    }
   }],
   qMeasures: [{
    qDef: {
     qDef: 'Sum([Sales Amount])'
    }
   }],
   qInitialDataFetch: [{
    qWidth: 5,
    qHeight: 1000
   }]
  }
 }
});

And we get something like this:

A very basic page with a working app connection and displaying some data from our app using the visualization we developed earlier. The project is available here.

Checking performance with Add Sense Chrome extension

One of the uses for the Add Sense Chrome extension is to check performance in your Qlik Sense app or mashup. To help you with this the info boxes show you a few numbers, collected as you use the extension. When you select to show the boxes, it will start collecting statistics, and you will se something like this:

Initial display with no values calculated

In this post I am using one of Qliks demo apps (you find it here, try it yourself!), so I don’t really expect to find any performance problems.

The Chrome extension will find all both objects defined in the app and session objects, but only ones that are shown on the page. For all such objects it will start collecting statistics:

  • number of recalculations made
  • latest calculation time in milliseconds
  • maximum calculation time

In the top left corner you will see the handle, that’s a number that the QIX Engine assigns to objects as they are opened. If you want to see the actual messages sent for a specific object in the web socket log you need this number. Since the handles are assigned in sequence, you will also get a grip of how many objects you are using in your solution.

Now to try this lets make some selection. Open the selection tool and click on show in the extension menu (yes the extension works in the selection tool too).Make some selections and you will see something like this (excuse me, it’s in swedish):

Selection tool with the Chrome extension enabled

Confirm your selections and close the selection tool. When you come back to the sheet probably only the app infobox will be visible. When Qlik Sense removes the objects from the page, the infoboxes disappear too. But the extension will continue to collect statistics, so click on show again in the extension menu, and you will se something like this:

Calculations have been made

Note that objects have only been recalculated once. That’s because the client only recalculate objects that are visible on the screen. The text object on the left should not be recalculated at all, since it was not affected by our selections.

So that’s what you need to do in a real situation:

  1. enable timing for objects on the screen with the show command
  2. make your selections, the extension wil register recalculations
  3. if objects have been off screen, reenable the infoboxes with the show command
  4. analyze the figures. Look for long calculation times or too many recalculations

Once you have started the timing, the only way to stop it is by refreshing the page, using the show command will not stop the monitoring.