Using CoffeeScript to create QlikView extensions

I am not a Javascript fan. To be honest, I find it rather abusive. There are probably lots of theoretically sound reasons for designing the Javascript (=JS) syntax the way it is – but that does not make it more readable or easy to learn.

I am however a QlikView (=QV) fan. Or rather: QV is a very powerful data visualisation, exploration and discovery tool, but the main drawbacks is its somewhat dated visualisation options. In a world used to fancy angular.js based dynamic web sites and great looking HighCharts graphs, QV’s visualisation options aren’t quite up there.

QV however has a rather interesting “extension” mechanism. You can create object extensions that add new visualisations to QV, using JS to develop the extensions. Document extensions are used to modify the deeper levels of QV applications – very useful and cool (someone created a document extensions using the accelerometers in iPhones to enable new ways to interacts with QV applications – pretty cool!) but not the focus of this post.

So, we want to create QlikView object extensions. JS is the mandated language. Ouch. We can however use CoffeeScript to remove the bad parts of JS, making the code base smaller and more easy to read and maintain. CoffeeScript is very cool, lots of testimonies to it’s greatness out there (DropBox is using CoffeeScript these days, and have shared some experiences).

Note: You need to install node.js before CoffeeScript. I’ve tried this on both Windows 8.1 and OS X – compiling CoffeeScript works without problems on both platforms.

Tuns out this works quite well. Brian Munz of QlikTech has created a couple of very nice templates to make it easier to create extensions, I’ve taken the liberty of converting one of them to CoffeeScript, to show how easy it is to convert JS to CoffeeScript (and make my own future extension development easier).

The CoffeeScript code can also be found in my repo at GitHub.

The Javascript version first:

[code language=”text”]

var template_path = Qva.Remote + "?public=only&name=Extensions/template_simple/";
function extension_Init()
{
// Use QlikView’s method of loading other files needed by an extension. These files should be added to your extension .zip file (.qar)
if (typeof jQuery == ‘undefined’) {
Qva.LoadScript(template_path + ‘jquery.js’, extension_Done);
}
else {
extension_Done();
}
}

function extension_Done(){
//Add extension
Qva.AddExtension(‘template_simple’, function(){
//Load a CSS style sheet
Qva.LoadCSS(template_path + "style.css");
var _this = this;
//add a unique name to the extension in order to prevent conflicts with other extensions.
//basically, take the object ID and add it to a DIV
var divName = _this.Layout.ObjectId.replace("\", "_");
if(_this.Element.children.length == 0) {//if this div doesn’t already exist, create a unique div with the divName
var ui = document.createElement("div");
ui.setAttribute("id", divName);
_this.Element.appendChild(ui);
} else {
//if it does exist, empty the div so we can fill it again
$("#" + divName).empty();
}

//create a variable to put the html into
var html = "";
//set a variable to the dataset to make things easier
var td = _this.Data;
//loop through the data set and add the values to the html variable
for(var rowIx = 0; rowIx < td.Rows.length; rowIx++) {
//set the current row to a variable
var row = td.Rows[rowIx];
//get the value of the first item in the dataset row
var val1 = row[0].text;
//get the value of the second item in the dataset row
var m = row[1].text;
//add those values to the html variable
html += "value 1: " + val1 + " expression value: " + m + "<br />";
}
//insert the html from the html variable into the extension.
$("#" + divName).html(html);
});
}

//Initiate extension
extension_Init();
[/code]

Now the CoffeeScript version. A lot more readable, at least to me:

[code language=”text”]
template_path = Qva.Remote + "?public=only&name=Extensions/template_simple_coffeescript/"

extension_Init = ->
# Use QlikView’s method of loading other files needed by an extension. These files should be added to your extension .zip file (.qar)
if typeof jQuery == ‘undefined’
Qva.LoadScript(template_path + ‘jquery.js’, extension_Done)
else
extension_Done()

extension_Done = ->
# Add extension
Qva.AddExtension(‘template_simple_coffeescript’, ->
_this = this

# add a unique name to the extension in order to prevent conflicts with other extensions.
# basically, take the object ID and add it to a DIV
divName = _this.Layout.ObjectId.replace("\", "_")
if _this.Element.children.length == 0
# if this div doesn’t already exist, create a unique div with the divName
ui = document.createElement("div")
ui.setAttribute("id", divName)
_this.Element.appendChild(ui)
else
# if it does exist, empty the div so we can fill it again
$("#" + divName).empty()

# create a variable to put the html into
html = ""

# set a variable to the dataset to make things easier
td = _this.Data

# loop through the data set and add the values to the html variable
for rowIx in [0..(td.Rows.length-1)]

# set the current row to a variable
row = td.Rows[rowIx]

# get the value of the first item in the dataset row
val1 = row[0].text

# get the value of the second item in the dataset row
m = row[1].text

# add those values to the html variable
html += "value 1: " + val1 + " expression value: " + m + "<br />"

# insert the html from the html variable into the extension.
$("#" + divName).html(html)
)

# Initiate extension
@extension_Init()
[/code]

Note that you need to include the –bare option when compiling the CoffeeScript code:

[code language=”bash”]

coffee –bare –compile Script.coffee

[/code]

This will give us the following Javascript file, which is functionally equivalent to the first JS file above:

[code language=”text”]
// Generated by CoffeeScript 1.6.3
var extension_Done, extension_Init, template_path;

template_path = Qva.Remote + "?public=only&name=Extensions/template_simple_coffeescript/";

extension_Init = function() {
if (typeof jQuery === ‘undefined’) {
return Qva.LoadScript(template_path + ‘jquery.js’, extension_Done);
} else {
return extension_Done();
}
};

extension_Done = function() {
return Qva.AddExtension(‘template_simple_coffeescript’, function() {
var divName, html, m, row, rowIx, td, ui, val1, _i, _ref, _this;
_this = this;
divName = _this.Layout.ObjectId.replace("\", "_");
if (_this.Element.children.length === 0) {
ui = document.createElement("div");
ui.setAttribute("id", divName);
_this.Element.appendChild(ui);
} else {
$("#" + divName).empty();
}

html = "";
td = _this.Data;
for (rowIx = _i = 0, _ref = td.Rows.length – 1; 0 <= _ref ? _i <= _ref : _i >= _ref; rowIx = 0 <= _ref ? ++_i : –_i) {
row = td.Rows[rowIx];
val1 = row[0].text;
m = row[1].text;
html += "value 1: " + val1 + " expression value: " + m + "<br />";
}
return $("#" + divName).html(html);
});
};

this.extension_Init();
[/code]