Qlik Community

Ask a Question

Qlik Design Blog

All about product and Qlik solutions: scripting, data modeling, visual design, extensions, best practices, etc.

Dipankar_Mazumdar

In the past few posts, I have discussed the modern, lightweight framework from Qlik, Nebula.js, and its usage in developing various Qlik Sense objects such as — creating a new visualization chart or building Embedded analytics solutions like Mashups. Nebula.js is a collection of product and framework agnostic JavaScript libraries and APIs that helps developers easily integrate out-of-the-box capabilities on top of the Qlik Associative Engine. So, let’s assume you have an already existing extension developed using the Extension API, and you would like to migrate this extension to the Nebula.js framework. How would you do this?

The focus of this post is to understand what resources are required by a Developer to effectively migrate an existing extension developed using Extension API to the Nebula.js framework and its potential benefits.

To demonstrate the overall migration process, I will take an existing visualization extension that I have developed in the past using the Extension API. The extension is a Scatter-Pie plot that allows us to visualize each scatter plot’s bubble as a pie-chart to understand the Sales-Profit correlation for the three categories state-wise as shown below:

Dipankar_Mazumdar_0-1620871570667.pngLet us try to recreate the exact same visualization by leveraging the novel Nebula.js framework.

Traditional Extension API-based visualizations need JavaScript code (.js), a metadata file (.qext), and stylesheets (.css). The logic of the extension is controlled using the code in the JavaScript file and so that is the entry point for your development activity. In Nebula.js, we try to segregate the various modules of the code to align it to a modular architecture, thus providing greater flexibility to the developers. Let’s deep dive into the steps required to migrate our current extension.

Step 1: Create a Nebula project structure.

The first step is to get all the files required as part of developing the extension. We can use the Nebula.js CLI like below and structure the project.

 

npx @nebula.js/cli create hello --picasso none

 

Dipankar_Mazumdar_2-1620871769611.png

Executing the above command, gives us the three required files.

 

Step 2: Starting the local development server.

One of the advantages of using the Nebula.js framework is that it comes with a local development server that allows developers to see their output as they code. To start the server, execute the following command.

 

cd hello
npm run start

 

Dipankar_Mazumdar_4-1620871831541.png

Now, we will need to connect to Qlik’s Associative Engine using the WebSocket protocol. Once we establish a connection, we will be presented with the list of apps to test our visualization in the Developer UI.

 

Step 3: Configuring the data structure.

Next, we need to configure the data structure and define our hypercube as shown in the code snippet below. This is similar to what we have in our current extension (Extension API). However, all this information is saved under one single JavaScript file in the older approach.

 

const properties = {
showTitles: true,
qHyperCubeDef: {
qInitialDataFetch: [{ qWidth: 6, qHeight: 100 }],
},
definition: {
type: "items",
component: "accordion",
items: {
dimensions: {
uses: "dimensions",
min: 1,
max: 6,
},
measures: {
uses: "measures",
min: 2,
max: 2,
},
sorting: {
uses: "sorting",
},
settings: {
uses: "settings",
},
},
},
};
export default properties;

 

The only thing that we do differently here in Nebula is to add the /qHyperCubeDef  as a data target in data.js like this:

 

export default {
  targets: [{
  	path: '/qHyperCubeDef'
  }],
};

 

 

 

Step 4: Import packages.

Nebula.js is built on the concept of custom hooks. If you do not know what hooks are, this tutorial will give you a high-level idea of how to leverage them in terms of Nebula. As per the Getting started with Nebula extension tutorial, we will need to import two essential packages to access the Qlik Sense object’s layout and render the data. They are useLayout and useEffect . Also, since our visualization is built using D3.js, we will need to install D3.js in our NodeJS environment and import the package for D3 using the commands below.

  • Install D3 by running npm install d3 --save
  • Import D3 to index.js by adding import * as d3 from d3

Now, let us understand what we did differently here in the Nebula.js framework as compared to the Extension API.

Dipankar_Mazumdar_5-1620872278049.png

Dipankar_Mazumdar_6-1620872294270.png

As we can see from the above comparative analysis, the primary difference is that the Extension API uses RequireJS to load resources asynchronously and has a jQuery wrapper around the HTML element. In the Nebula.js framework, we eliminate all these dependencies, thereby making it framework agnostic and faster.

 

Step 5: Code Logic.

Our most important goal is to develop the main functionality of the extension. Again, the whole idea here is to replicate the exact visualization developed using Extension API without investing additional time in rewriting the source code. The entry point for the extension’s code is the index.js file, and currently, it looks like below with all the necessary packages.

 

import { useLayout, useElement, useEffect } from "@nebula.js/stardust";
import properties from './object-properties';
import data from './data';
import * as d3 from "d3";

export default function supernova() {
  return {
    qae: {
      properties,
      data,
    },
    component() {
      const element = useElement();
      element.innerHTML = '<div>Hello!</div>'; // eslint-disable-line
    },
  };
}

 

Now, let’s take a look at our current extension’s JS code.

Dipankar_Mazumdar_7-1620872386942.png

To render the visualization with an HTML element, we take advantage of the paint($element, layout) method where $element is a jQuery wrapper containing the HTML element and layout presents the data and properties for the visualization. This method is called every time the visualization is rendered. So, do we have a similar approach in the Nebula.js framework? The answer is Yes!

If we go back to our index.js file, we notice a function supernova( ) that consists of the component( ) method, which is where all the rendering takes place. To render something, we need to access the DOM element the visualization is assigned to, and to do so, we need to use the useElement method. Also, as I mentioned in Step 4, to access the QS object’s layout and bind the data, we need to use the useLayout and useEffect methods. These three methods are all we need to migrate our current code to the newer framework successfully.

After copying the current code and aligning it to Nebula’s programming standard, this is what we get.

 

export default function supernova() {
  return {
    qae: {
      properties,
      data,
    },
    component() {
      const element = useElement();
      const layout = useLayout();
      const selections = useSelections();
      console.log(layout)
      //getting data array from QS object layout
      useEffect(() => {
        if (layout.qSelectionInfo.qInSelections) {
          return;
        }
        var qMatrix = layout.qHyperCube.qDataPages[0].qMatrix;

        var measureLabels = layout.qHyperCube.qMeasureInfo.map(function (d) {
          return d.qFallbackTitle;
        });

        //an array that invokes each row of qMatrix from layout:
        var data = qMatrix.map(function (d) {
          return {
            Dim1: d[0].qText,
            Dim2: d[1].qText,
            Dim3: d[2].qText,
            Dim4: d[3].qText,
          };
        });
        var width = 1000;
        var height = 400;

        var id = "container_" + layout.qInfo.qId;
      
          const elem_new = `<div id=${id}></div>`;
          element.innerHTML = elem_new;

          viz(data, measureLabels, width, height, id);
        
      }, [element, layout]);
    }
  }
}

 

 

 

We see that most of the code lines are similar to what we had in our Extension API. The only difference lies in the way we interact with the three methods here in Nebula.js. In the end, the viz( ) method is called within the component( ) function, and that is where our D3.js code for the visualization is. Again, this is similar to what we did in the Extension API. That is all we needed to do from a code perspective.

 

Step 6: Build & Deploy.

The good old extensions developed using Extension API had to be bundled (zipped) together with a .qext file, a JavaScript file, and any other dependency files. Nebula.js presents a modern way of building and preparing the extension to be deployed to the Qlik Sense environment.

Firstly, since Nebula runs in a NodeJS environment, we can easily bundle everything and distribute the visualization as an NPM package using:

npm run build

Secondly, to deploy the extension to a QS environment, we use the command below, which generates all files into the folder /hello-ext  that you can then use as an extension in QS.

npm run sense

Now that we know about the resources required to migrate our existing extensions to the Nebula.js framework, let’s recap the potential benefits of the new SDK.

  1. Nebula.js is modular and allows developers greater flexibility in programming and code maintenance.
  2. It comes with a local development server to visualize the output as we code.
  3. Modern framework with no dependency on module loaders such as RequireJS or wrappers like jQuery.
  4. Available as an NPM package and hence very easy to get started with.
  5. Faster build and deployment; visualizations can be distributed as NPM packages.
  6. Way forward — support, etc.

This brings an end to the post, and hopefully, this serves as an initial guide for developers and organizations planning to migrate their existing visualizations to the Nebula.js framework. For folks who want to get started with Nebula or build advanced visual representations using the SDK, here is a list of accumulated resources -

  1. Building a Hello world extension using Nebula
  2. Why should we stop using Capability APIs?
  3. Building an advanced visualization extension using Nebula.js and 3rd party libs such as D3.js
  4. Dealing with variables in a Mashup using Nebula.js & Enigma.js (alternative to Variable API)