Qlik Sense / Qlik Analytics Platform Articles, Projects & Tutorials by Stefan Walther

The Angular Way: Basics

Introducing AngularJS

AngularJS

  • is a framework to build dynamic web applications
  • is a MVC or MV* framework
  • has a strong focus on data binding and extends HTML
  • plays nicely together with other JavaScript libraries like jQuery

Some of the key concepts of AngularJS:

  • Templating & Data Binding
  • Controllers
  • Directives
  • Services
  • Dependency Injection
  • Filters
  • Routing
  • Testability

AngularJS and Qlik Sense

The Qlik Sense client uses under the hood AngularJS, that's first good to know. When it comes to use AngularJS also to develop your visualization extensions it's important to understand that you cannot use all of the concepts of AngularJS out of the box but the most important ones.

Some background information

One of the core-concepts of AngularJS is that an AngularJS application gets bootstraped. So does also the Qlik Sense client and during that process an AngularJS module called qvangular get created.

This qvangular module takes - among other things - care of two main aspects:

  • that AngularJS and RequireJS plays nicely together
  • that visualization extensions can be loaded at runtime without bootstrapping the application

AngularJS concepts which CAN be used in Qlik Sense

  • Directives
  • Templates
  • Filters
  • Re-using AngularJS internal methods
  • Data-binding
  • The entire scope concept

AngularJS concepts which CANNOT be used in Qlik Sense

The following cannot be done when developing a Qlik Sense visualization extensions in Qlik Sense:

  • Loading and re-using entire AngularJS modules or factories
    (You will have to modify them to work as directives or services)
  • Using AngularJS constants
  • Bootstrapping the application

Let's get started

The biggest difference between using the "classic" paint- and the AngularJS-approach is that instead of paint you have to define a template + a controller in your main JavaScript file:

Basic AngularJS skeleton

define( [
    ],
    function (  ) {
        'use strict';

        return {
            definition: {},
            initialProperties: {},
            snapshot: {canTakeSnapshot: true},
            template: '<div qv-extension>This is my message: <b>{{msg}}</b></div>',
            controller: ['$scope', function ( $scope ) {
                $scope.msg = 'Hello AngularJS';
            }]
        };
    } );

Code explanation:

  • Note that the paint method is not used anymore!
  • template defines your AngularJS template which will play together with the controller
    • The template can be defined inline (as shown above or by using a separate file, as shown below)
  • qv-extension must be part of your template's root element
    (qv-extension is a AngularJS directive which takes care of several aspects that your visualization extension can use AngularJS)

Using external templates

By moving the template to a separate file, maintaining the HTML code becomes much easier:

HTML:
(file: template.ng.html, located in the same folder as your main script file)

<div qv-extension>
    This is my message: {{msg}}<br/>
    or<br/>
    This is my message: <span ng-bind="msg"></span>
</div>

JavaScript:

define( [
        'text!./template.ng.html'
    ],
    function ( ngTemplate ) {
        'use strict';

        return {
            definition: {},
            initialProperties: {},
            snapshot: {canTakeSnapshot: true},
            template: ngTemplate,
            controller: ['$scope', function ( $scope ) {
                $scope.msg = 'Hello AngularJS';
            }]
        };
    } );

Result:

Accessing the layout & properties

Working with properties doesn't change, define definition and initialProperties as described in earlier chapters. All the property values are automatically bound to the current scope under $scope.layout.

By just changing the HTML template we can retrieve some very basic information about the current object:

Property definition:
(properties.js)

define( [], function () {
    'use strict';

    var dimensions = {uses: "dimensions"};
    var measures = { uses: "measures" };
    var sorting = { uses: "sorting" };
    var addons = { uses: "addons" };
    var appearancePanel = { uses: "settings" };
    return {
        type: "items",
        component: "accordion",
        items: {
            dimensions: dimensions,
            measures: measures,
            sorting: sorting,
            appearance: appearancePanel
        }
    };
} );

Initial properties:
(initialproperties.js)

define( [], function () {
    'use strict';
    return {
        qHyperCubeDef: {
            qDimensions: [],
            qMeasures: [],
            qInitialDataFetch: [
                {
                    qWidth: 10,
                    qHeight: 50
                }
            ]
        }
    };
} );

HTML:
(template.ng.html)

<div qv-extension>
    <b ng-bind="myTitle" />
    
    <table border="1">
        <thead>
        <tr>
            <th ng-repeat="dim in layout.qHyperCube.qDimensionInfo" ng-bind="dim.qFallbackTitle" />
            <th ng-repeat="mea in layout.qHyperCube.qMeasureInfo" ng-bind="mea.qFallbackTitle" />
        </tr>
        </thead>
        <tbody>
        <tr ng-repeat="row in layout.qHyperCube.qDataPages[0].qMatrix">
            <td ng-repeat="col in row">
                {{col.qText}}
            </td>
        </tr>
        </tbody>
    </table>
</div>

Main script:

define( [
        './properties',
        './initialproperties',
        'text!./template.ng.html'
    ],
    function ( props, initProps, ngTemplate ) {
        'use strict';

        return {
            definition: props,
            initialProperties: initProps,
            snapshot: {canTakeSnapshot: true},
            template: ngTemplate,
            controller: ['$scope', function ( $scope ) {
                $scope.myTitle = 'This is my AngularJS table';
            }]
        };
    } );

Result:

Hint:

Add a console.log('layout', $scope.layout); into your controller and you'll see where you can find all the properties.

Support of AngularJS

It is important to mention in this context that Qlik support does certainly not cover AngularJS.

  • Qlik Sense 2.0.1 is using version 1.2.15 of AngularJS,
  • Qlik Sense 3.0 is using 1.5 of AngularJS

So either ensure that you are not using a lot of version specific functionality of AngularJS or follow AngularJS advices how to upgrade your code to a new version.

Using angular as a dependency you can easily check which version is used:

define( [
        'angular'
    ],
    function ( angular ) {

    return {
        ...
        controller: ['$scope', function ( $scope ) {
            $scope.angularVersion = angular.version;
        }]
    };
} );

Result:
AngularJS developers seem to be funny guys, have a look at the codeName of version 1.2.15 ;-)