Printing Qlik Sense extensions

From time to time I get questions from Qlik customers and partners with some problem regarding Qlik Sense extensions or mashups. Usually this means a few hours of troubleshooting and hopefully fixing something, fun and interesting. Could be extensions that break when Qlik Sense is upgraded, performance issues or some feature that doesn’t work.

A recurring problem is with printing extension objects. Many customers don’t care whether this works, they don’t use NPrinting or it’s not relevant for their extensions, but some do. And if they have problems, it can be a challenge to solve them if you have never done it before.

How does it work

Qlik Sense uses snapshots for printing. That means what’s printed is not what you see on your screen, but a snapshot of the data in the extension, that is then re-rendered by the rendering engine and eventually printed. There is no live connection to the Qlik Sense engine available during this re-rendering, which means that everything that needs a live connection is not available. That’s why you should always base your rendering only on the layout.

So, a first step is to verify that the extension does not do any calls that require an engine connection in the rendering part (they can be used in the property panel, and in many cases are to fill dropdown lists with content etc). This might require some refactoring to solve and so might take some time, but in most cases this is not the problem.

Waiting for the extension to be ready

A more common problem is that the printing is made before the extension is ready. This might mean that printing works sometimes, but not all of the time. It might also mean that the printout is partial, showing the extension when parts of it are rendered, but not all of it etc.

To solve this you need to return a Promise and make sure it is not resolved until your rendering is complete, something like this:

paint: function ($element, layout) {
  // do your rendering
  return new qlik.Promise(function (resolve, reject) {
    // when rendering is complete, call resolve
    resolve();
    // optionally if rendering fails, call reject 
    reject();
  });
} //end of print function

The Promise supplied by Qlik now follows the standard javascript API (though it looks like it isn’t the standard) unlike in previuos releases where it was essentially angular $p service. I’m afraid that means the code I’ve published earlier in this blog no longer works, the code above is how it should be.

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.