1 2 3 Previous Next

Qlik Design Blog

37 Posts authored by: Francis Kabinoff
Francis Kabinoff

Introducing qdt-lui

Posted by Francis Kabinoff Apr 13, 2018

If you follow our blog posts regularly, you'll know we've been working on a library called qdt-components which includes a bunch of custom components we're using in our Qlik-powered apps. We've been using Bootstrap 4 with reactstrap, which is a collection of React components that use Bootstrap 4. The thing is that made Bootstrap 4 a dependency, and many of its styles are global, and that was a problem. We also wanted to be able to use Qlik's own Leonardo UI as a general style guide, but we needed the functionality of things like Bootstraps dropdowns. So we've been working on a new library called qdt-lui, which is a collection of React components that use Leonardo UI,  Just today we finally finished replacing all of the reactstrap components in qdt-components with qdt-lui components! There's still a bit of styling to touch up on because of this transition, but for the most part it's looking and working great.

 

So the general idea is very similar to reactstrap, but instead of creating React components that use Bootstrap for styles, we use Leonardo UI. So far, we have 6 components, and they are - LuiButton, LuiDropdown, LuiIcon, LuiList, LuiListItem, and LuiSearch. If you're familiar with Leonardo UI, you may be aware that Leonardo UI doesn't have a dropdown component at all, it's actually a select component that just reskins an actual html select element. But for our use cases, select elements are generally not very useful, so we borrowed the styles from the Leonardo UI select component and built something very similar to a bootstrap dropdown.

 

The LuiSearch component uses the Leonardo UI search component styles, but makes it a controlled react component which accepts as props a value and a clear method, so you can control the value from the parent component, and we can call the passed down clear method to clear the value in the parent component when you click on the clear symbol that's in the component. Again, this pattern is very similar to reactstrap, and what we've been using as a reference when building these components.

 

So check it out here - https://github.com/qlik-demo-team/qdt-lui and hopefully you find it useful. You'll be seeing it used a lot from us now in most of our projects on the demo team, and we'll have a lot of examples soon. Documentation is non-existent at the moment, but checking the source of each component and taking a look at the prop-types it should be pretty clear for now, but documentation will be coming. It's still marked as alpha, but we'll be moving it to beta soon. Let us know what you guys think, if you find this useful, and any features that you would like to see. Oh, and any pull requests submitted would have us spinning in our chairs excited, so if you're so inclined to contribute, that would be very, very cool.

As many of you know, custom themes shipped with Qlik Sense February 2018, and we covered how to start creating custom themes which you can check out here -How to create a custom theme in Qlik Sense. But did you know that Qlik Sense February 2018 also shipped with a new Theme API under the Capability APIs collection?

 

What can you do with the Theme API?

The Theme API can basically be used for two purposes, to get and to apply themes. You can view the documentation here - Theme API. Getting a theme is useful for applying styles to extensions, widgets, and mashups that are identical to the theme style. You can also apply themes to both Qlik Sense apps and independently of the theme set in the Qlik Sense app to native Qlik Sense objects created or embedded using the Visualization API (or app.getObject, if you're still using that).

 

You can get a theme using one of two different methods, the `get` method and the `getApplied` method. The difference is that the `get` method you call on the `qlik` object and pass a theme id to get any theme in your extensions, while the `getApplied` method you call on an instance of an `app` and it simply returns the current theme applied to that app. The `getApplied` method is particularly useful for widgets and extensions, since it allows you to style your widgets and extensions in such a way that they will match the theme the user applies to their app.

 

Both the `get` method and the `getApplied` method returns a promise which resolves to a `QTheme` object. The `QTheme` object currently has two methods available on it, the `getStyles` method, and the `apply` method. The `getStyles` method can be used to get a particular style by passing it a few parameters. It's an easy way to find the style you are looking for in the `QTheme` object. The `apply` method is one of the ways in which you can apply a theme to all visualizations on the page.

 

You can also apply a theme using the `apply` method of the Theme API without first fetching a `QTheme` object. The `apply` method of the Theme API just requires that you pass the theme id. These apply methods are particularly helpful for applying a theme to mashups and other embedded visualizations. Below is a code sample and image of the mashup with the theme applied.

 

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


 qlik.theme.apply('high-contrast-theme');

 var app = qlik.openApp('Helpdesk Management.qvf', config);

 app.getObject('QV03','rJFbvG');
 app.getObject('QV02','xfvKMP');
 app.getObject('QV01','a5e0f12c-38f5-4da9-8f3f-0e4566b28398');
} );

mashupthemed.png

 

There's one final method to mention, and that is the `save` method. The `save` method allows you to programmatically set the theme in a Qlik Sense app. This differs from simply applying a theme, as it actually sets and saves the theme in the Qlik Sense app, and not just for the session like the apply methods do.

 

And that's the introduction to the Theme API.

banner_react.jpg

On the demo team we end up working with both the Viz API to create and embed native Qlik Sense charts in apps, as well as creating custom components powered by the Qlik Engine using enigma.js, and we do this in Angular, React, and plain html, as we try to demo all of the ways that Qlik can be used. So after my last blog post (Starter project for developing QAP-powered apps with React and enigma.js) we had an idea on the demo team: let's try to unify all of our different custom components and templates. So we've been hard at work accomplishing that.


You may have seen Yianni's last blog post, qdt-components - A Components Library that can be used in simple Html, Angular 5 and React, introducing this effort. The goal is to have the qdt-components library include custom components powered by Qlik Engine, as well as a way to create and embed native Qlik Sense charts. Right now, the qdt-components library contains a few components, but we're not totally ready to start talking about all of them yet, so we're just sticking to the component that allows you to create and embed native Qlik Sense charts by interfacing with the Viz API.


The big addition since Yianni's post is that we've added a React template, so now we have an Angular 5 template, a plain html template, and a React template, all which load the qdt-components, which is pretty cool. This means that the components only need to be built once, but can be used in a project with almost any frontend stack. I say "almost" because in order to use the component that interfaces with the Viz API there's no getting around loading Angular 1.5 and RequireJS into the global namespace, which means they still have the chance to create conflicts. But you can choose not to use the component that interfaces with the Viz API if you don't have a need for native Qlik charts, and it will avoid loading all of the Capability API stuff, but if you choose to use it, you don't have to worry about writing any of the boilerplate you usually do when connecting to the Capability API.


Documentation, examples, and custom components are coming (and oh yea, we plan on using https://picassojs.com/), but that's the update for now. You can check out the qdt-components library and the templates here - Qlik Demo Team · GitHub. And demos of each of the 3 templates below

 

Angular 5 with qdt-components

React with qdt-components

Simple html with qdt-components


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.

Filter Blog

By date:
By tag: