Wrap-up

Developing the Mastering QS bar chart alongside the code examples step by step was a very quick introduction into how to build compelling visualizations on top of D3.js. Now that the skeleton is set up, you can proceed to use your imagination on any other requirements you can think of that you are missing in native Qlik Sense, or by implementing those odd use cases that your users have. What is important is to keep the structure of the code clean to ensure you can always quickly add new features without too much hassle. Given the rich selection of open source examples of D3 available on the web, being able to implement a visualization that reads from the Qlik engine, visualizes the results, and responds to user clicks is a very powerful skillset to have.

While this is only an introduction, I strongly encourage you to keep deepening your knowledge in this area, as you will be able to provide remarkable value to your users.

To summarize, please see next the complete runnable JavaScript code:

//MasteringQSBarchart.js
define(['d3', './properties', 'css!./MasteringQSBarChart.css'],
function(d3, properties) {
return {
initialProperties : {
qHyperCubeDef : {
qDimensions : [],
qMeasures : [],
qInitialDataFetch : [{
qWidth : 2,
qHeight : 5000
}]
}
},
definition: properties,
paint: function(){},
resize: function($element, layout){
//Makes sure the chart is re-initialized when the object is //resized.
this.$scope.init(this.$scope.element, layout)
this.$scope.render(this.$scope.element, layout)
},
template: '<div></div>',
controller: ['$scope', '$element', function (scope, $element) {
scope.prep = function(scope, $element){
console.log("PREP");
//Prepares the SVG structure of the d3 visualization.
//Initializes the chart once prepared
scope.svg = d3.select($element.children()[0])
.append('svg')
scope.svg.append('g')
.attr('class', 'canvas')
scope.svg.append('g')
.attr('class', 'x-axis')
scope.svg.append('g')
.attr('class', 'y-axis')
scope.x = d3.scale.ordinal()
scope.y = d3.scale.linear().nice();
scope.xAxis = d3.svg.axis()
scope.yAxis = d3.svg.axis();
scope.init(scope.element, scope.layout)
};
scope.init = function (element, layout){
console.log("INIT");
//Initializes the visualization, determines size of the chart
//as well as its axis, ranges and domains
//Renders the charts once initialized
scope.margin = {top: 30, right: 30, bottom: 30, left: 50}
scope.width = $element.width() - scope.margin.right - scope.margin.left
scope.height = $element.height() - scope.margin.top - scope.margin.bottom
scope.svg
.style('width', $element.width())
.style('height', $element.height())

scope.svg.select('.canvas')
.attr('transform', 'translate('+scope.margin.left+',5)')
scope.svg.selectAll('.x-axis')
.attr('transform', 'translate('+scope.margin.left+','+parseInt(scope.height+5)+')')
scope.x.rangeRoundBands([0, scope.width], 0.1);
scope.svg.selectAll('.y-axis')
.attr('transform', 'translate('+scope.margin.left+',5)')
scope.y.range([scope.height, 0]);
scope.bar_color = layout.barColor.color;
};
scope.getData = function(layout){
console.log("GETDATA");
//Takes the returned dataset and visualizes the bars
//Data prep
scope.data = layout.qHyperCube.qDataPages[0].qMatrix.map(function(d, i, arr){
return {
Bar: d[0].qText,
BarID: d[0].qElemNumber,
Value: d[1].qNum,
}
})
scope.bars = layout.qHyperCube.qDataPages[0].qMatrix.map(function(d, i, arr){
return d[0].qText
})
//x-Axis values
scope.x.domain(scope.bars)
//y-Axis domain
scope.y.domain([
0,
d3.max(scope.data, function(d){
return d.Value;
})
])
}
scope.render = function(element, layout){
console.log("RENDER");
//Bar Width
scope.bar_width = scope.width/scope.bars.length*0.8;
//Visualize Bars
var bars = scope.svg.select('.canvas')
.selectAll('rect')
.data(scope.data)
//Enter
bars
.enter()
.append("rect")
.transition()
.duration(300)
.attr("class", "bar")
.attr("elementid", function(d){
return d.BarID
})
.attr('fill', scope.bar_color)
.attr('width', scope.bar_width)
.attr('x', function(d){
return scope.x(d.Bar)
})
.attr('y', function(d){
return scope.y(d.Value);
})
.attr('height', function(d){
return scope.height-scope.y(d.Value)
})
//Update
scope.svg.selectAll('rect')
.on("click", function(d, e){
scope.selection(d.BarID)
})
.attr("elementid", function(d){
return d.BarID
})
.attr('fill', scope.bar_color)
.attr('width', scope.bar_width)
.attr('x', function(d){
return scope.x(d.Bar)
})
.transition()
.duration(300)
.attr('y', function(d){
return scope.y(d.Value);
})
.attr('height', function(d){
return scope.height-scope.y(d.Value)
})
//Exit
bars
.exit()
.remove();

//Add x-axis
scope.svg.selectAll('.x-axis')
.transition(300)
.call(
scope.xAxis
.scale(scope.x)
.orient('buttom')
.ticks(20)
)
//Add y-axis
scope.svg.selectAll('.y-axis')
.transition(300)
.call(
scope.yAxis
.scale(scope.y)
.orient('left')
.ticks(20)
)
}
scope.selection = function(elementid){
if (elementid >= 0) { // null is not selectabel (element -2)
this.backendApi.selectValues(0, [elementid], false); //Multi-select
//self.selectValues(0, [vElementId], false);
}

}
scope.prep(scope, $element);
scope.getData(scope.layout);
scope.render(scope.element, scope.layout)

scope.backendApi.model.Validated.bind(function(a, b){
//Listens for click events or other data
//model changes to re-render the chart with the updated data.
scope.getData(scope.layout);
scope.render(scope.element, scope.layout)
})
}]
}
});
Qlik is looking to improve the Extension API and with their February 2018 update have released a new method (similar to paint()) called updateData(). This permits paint() to finally becomes a stateless function, possibly removing the necessity to implement D3.js visualization using the AngularJS controller.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.37.196