I implemented the sparkline using the Power BI Developer Tools. Let’s start its
implementation with the IVisual interface. Remember that IVisual has three key methods:
init(), update(), and destroy().
Implementing the init() method
Power BI calls the init() method to give a chance to the visual to initialize itself. Figure 13.14 shows its implementation.
Figure 13.14 The init() method initializes the visual.
First, the code creates an instance of the SelectionManager which the host uses to communicate to the visual user interactions, such as clicking the graph. The sparkline doesn’t do anything with user selection events but it’s possible to extend it, such as to
navigate to another page or highlight a line segment. Line 167 initializes the D3.js
framework with the DOM element the visual owns, which is passed to the init() method as a property of the VisualInitOptions parameter. Line 169 creates a svg HTML element and classes it as “sparkline”. It’s a good practice to create another element instead of using the root in the case when you might need to draw more elements in future. The code also sizes the svg element so that it occupies the entire viewport.
Figure 13.15 The update() method draws the graph.
Implementing the update() method
The update() method is where the actual work of drawing the graph happens (see Figure 13.15). Line 178 removes the existing graph so that redrawing the sparkline doesn’t overlay what’s already plotted on the canvas and to avoid drawing new lines when the
visual is resized.
When the host calls the update() method, it passes the data as a dataView object. For example, if you add the CalendarQuarter field to the Category area and SalesAmount field to the Value area, the host will aggregate SalesAmount by quarter and pass the
corresponding data representation and the metadata describing the columns under the options.DataView object. The definition of the DataView object is documented at
https://github.com/Microsoft/PowerBI-visuals/wiki/DataView-Introduction. In our case, the DataView object might look like the example shown in Figure 13.16.
Figure 13.16 When the host calls the update() method it passes a DataView object with the actual data.
Since the sparkline visual supports only one field in the Category area, there is only element in the DataView.categorical.categories array. The values property returns the actual category values, such as Q1 2015. The identity property returns system-generated unique identifiers for each category value. The DataView.categorical.values property contains the values of the field added to the Value area. Because the sparkline visual supports only one field in the Value area, the values array has only one element.
Working directly with the DataView object is impractical. This is why line 182 calls a converter object, which converts the DataView object into a custom object for working with the data in a more suitable format. Using a converter is a recommended pattern since it allows you to organize the data just as you are to draw it, which makes your code
focused on the task at hand and not on manipulating the data. For example, in our case the converter.data property on line 193 returns the data points as a JavaScript array.
The D3.js code starts at line 195. First, the code calibrates the X axis to plot the number of data points. Conveniently, D3.js supports quantitative scaling and the
d3.scale.linear.domain property scales the X axis to fit the data points. Next, the code calibrates the Y axis to fit the values given the minimum and maximum data point values.
Lines 199-211 plot the line. One cautionary note here is that the zero coordinate of Y axis starts at the top of the viewport. Therefore, line 210 inverts the data point Y coordinate.
Line 214 draws the line using the user-specified line width and color.
Animating the graph
If the user turns on the Animate setting, the line constantly redraws itself using a configurable delay and redrawing speed. The code that animates the graph is shown in Figure 13.17. Line 233 checks if the animation effect is turned on. If so, it uses the
JavaScript setInterval() function to call periodically the redrawWithAnimation() function.
D3.js and SVG make the task of animating the graph easy. Line 221 calls the SVG getTotalLength() function to calculates the length of the graph. The stroke-dasharray attribute lets you specify the length of the rendered part of the line. The stroke-dashoffset attribute lets you change where the dasharray behavior starts. Then the SVG transition() function is used to animate the path.
Figure 13.17 The graph supports animated line redrawing by calling repeatedly the redrawWithAnimation function.
The last line of the update() method (line 239) enables the tooltip support that allows the user to hover on top of the viewport and see the data point values in a tooltip. It calls the converter.viewModel.toolTip property which enumerates and concatenates the data point values.
var tooltipString: string = ””;
var formatter = valueFormatter.create({ format:“0”, value: 0});
for (var i=0; i < values.length; i++) {tooltipString += formatter.format(values[i]) + ” “} // beautify tooltip values toolTipInfo: [{
displayName: dataViewCategorical.categories[0].source.displayName, value: values.join(“, “) }]
Implementing the destroy() method
Remember that the host calls the destroy() method to give the visual a chance to release any resources that might result in memory leaks. Our implementation releases the D3.js
graph elements. It also releases the timer variable that holds a reference to timer identifier when the animation effect is used.
public destroy(): void { this.svg = null;
this.root = null;
this.timer = nulll;}