Forum Discussion
Power BI Visuals -Dimple.js Trellis sample...
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 //2ndSo 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.
1 Reply
- v-chuncz-msftCommunity Support
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.