Earn the coveted Fabric Analytics Engineer certification. 100% off your exam for a limited time only!
Hi,
I stumbled on an article on LinkedIn where the author claimed he managed
to run a dimple.js trellis sample on Power BI Visuals.
https://www.linkedin.com/pulse/power-bi-fast-prototyping-dimpled3-custom-visual-martin-x
I decided to give it a try as I thought this would be a very good initiative to test external
libraries. One thing to keep in mind with dimple.js is it depends mainly on d3.js.
[d3.v4.. in this case] to work. So d3.js must appear first in directives hierarchy
otherwise dimple.js won't be able to run.
d3.v4.js: http://d3js.org/d3.v4.min.js //1st
'---> dimplejs: http://dimplejs.org/dist/dimple.v2.3.0.min.js //2nd
So I came up with this code based on information. I wish the author had provided
a complete working .
The whole code shows no error on pbiviz package compilation,
though when run in Power BI Desktop or testing on Power BI Online, nothing shows up... 😞
I'm using a similar csv mockup data sample:
I aslo checked visual's underlying data view and it seemed ok, so I presume something is obviously missing as dimple.js may not be recognized...
So here's what I came up with.
The application hierarchy
tsconfigj.son:
{ "compilerOptions": { "allowJs": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, "target": "ES5", "sourceMap": true, "out": "./.tmp/build/visual.js" }, "files": [ ".api/v1.5.0/PowerBI-visuals.d.ts", "typings/index.d.ts", "src/trellischart.d.ts", "src/trellischart.js", "src/visual.ts" ] }
pbiviz.json:
"externalJS": ["node_modules/d3v4/build/d3.js","extlib/dimple.v2.3.0.js"], <-------- "style": "style/visual.less", "capabilities": "capabilities.json", "dependencies": "dependencies.json"
capabilities.json:
{ "dataRoles": [ { "displayName": "Date", "name": "Date", "kind": "Grouping" },{ "displayName": "Month", "name": "Month", "kind": "Grouping" }, { "displayName": "Category", "name": "Category", "kind": "Grouping" }, { "displayName": "Value", "name": "Value", "kind": "Measure" } ], "dataViewMappings": [ { "conditions": [ { "Date": { "max": 1 }, "Month": { "max": 1 }, "Category": { "max": 1 }, "Value": { "max": 1 } } ] ,"categorical": { "categories": { "for": { "in": "Date" }, "dataReductionAlgorithm": { "top": {} } }, "values": { "select": [ { "bind": { "to": "Month" } }, { "bind": { "to": "Category" } }, { "bind": { "to": "Value" } } ] } } } ] }
src/tresllischart.d.ts:
declare var dimple: any; declare var trellichart: any;
src/trellischart.js:
/// <amd-dependency path='external/dimple.v2.3.0.js'> function loadTrellis(element, dataView, config) { console.log(config); var trellis = element; trellis.style('width', config.viewport.width) .style('height',config.viewport.height); // var x = convert(dataView); var data = convert(dataView);//loadData(); //load data from here //d3.tsv("/data/example_data.tsv", function (data) { // Get a unique list of dates var months = dimple.getUniqueValues(data.rows, data.date.Date); // Set the bounds for the charts var row = 0, col = 0, top = 25, left = 60, inMarg = 15, width = 115, height = 90, totalWidth = config.viewport.width*.9;//;parseFloat(trellis.attr("width")); // Pick the latest 12 dates months = months.slice(months.length - 12); // Draw a chart for each of the 12 dates months.forEach(function (month) { // Wrap to the row above if (left + ((col + 1) * (width + inMarg)) > totalWidth) { row += 1; col = 0; } // Filter for the month in the iteration var chartData = dimple.filterData(data.rows, data.date.Date, month); // Use d3 to draw a text label for the month trellis .append("text") .attr("x", left + (col * (width + inMarg)) + (width / 2)) .attr("y", top + (row * (height + inMarg)) + (height / 2) + 12) .style("font-family", "sans-serif") .style("text-anchor", "middle") .style("font-size", "28px") .style("opacity", 0.2) .text(chartData[0].Month.substring(0, 3)); // Create a chart at the correct point in the trellis var myChart = new dimple.chart(trellis, chartData); myChart.setBounds( left + (col * (width + inMarg)), top + (row * (height + inMarg)), width, height); // Add x and fix ordering so that all charts are the same var x = myChart.addCategoryAxis("x", data.axis.x); x.addOrderRule(data.axis.x); // Add y and fix scale so that all charts are the same var y = myChart.addMeasureAxis("y", data.axis.y); y.overrideMax = 1500; // Draw the bars. Passing null here would draw all bars with // the same color. Passing owner second colors by owner, which // is normally bad practice in a bar chart but works in a trellis. // Month is only passed here so that it shows in the tooltip. myChart.addSeries([data.date.Month, data.axis.x], dimple.plot.bar); // Draw the chart myChart.draw(); // Once drawn we can access the shapes // If this is not in the first column remove the y text if (col > 0) { y.shapes.selectAll("text").remove(); } // If this is not in the last row remove the x text if (row < 2) { x.shapes.selectAll("text").remove(); } // Remove the axis labels y.titleShape.remove(); x.titleShape.remove(); // Move to the next column col += 1; }, this); } function convert(dataView) { var DateCol = -1, MonthCol = -1, CategoryCol = -1, ValueCol = -1, ret = []; if (dataView && dataView.categorical && dataView.metadata && dataView.metadata.columns) { //&& dataView.categorical.categories var metadataColumns = dataView.metadata.columns; //step 1: 1)find correct column index in meta data for (var i = 0; i < metadataColumns.length; i++) { var col = metadataColumns[i]; if (col.roles) { if (col.roles['Date']) DateCol = i; else if (col.roles['Month']) MonthCol = i; else if (col.roles['Category']) CategoryCol = i; else if (col.roles['Value']) ValueCol = i; } } function getMonth(d) { function pad(s) { return (s < 10) ? '0' + s : s; } return [pad("1"), pad(d.getMonth() + 1), d.getFullYear()].join('/') } if (dataView && dataView.table) { //step 2: convert table row by row into correct columns of graph dataView.table.rows.forEach(function (item) { ret.push({"Date":getMonth(item[DateCol]),"Month":item[MonthCol], "Category":item[CategoryCol], "Value":item[ValueCol]}); }); } }//if return {axis:{ x: "Category", y: "Value"} , date:{ Date:"Date", Month:"Month"} , rows: ret }; }
src/visual.ts
// <amd-dependency path='external/dimple.v2.3.0.js' name='dimple'> /// <amd-dependency path='trellichart'> module powerbi.extensibility.visual { /// <reference types="d3" /> export class Visual implements IVisual { private svg: d3.Selection<SVGElement>; private ele: any; constructor(options: VisualConstructorOptions) { let ele = options.element; let svg = this.svg = d3.select(options.element) .append('svg').classed('trellis', true); } public update(options: VisualUpdateOptions) { let dataView = options.dataViews[0]; debugger; loadTrellis(this.svg, dataView, options); } } }
So if anyone has a solution to make this work, then be my guest...
Any help would be appreciated. Thanks.
I've done a quick test. You may add try...catch statement for troubleshooting. I'm sure you will be able to get it solved.
User | Count |
---|---|
15 | |
2 | |
1 | |
1 | |
1 |
User | Count |
---|---|
17 | |
11 | |
5 | |
4 | |
3 |