Skip to main content
cancel
Showing results for 
Search instead for 
Did you mean: 

Earn the coveted Fabric Analytics Engineer certification. 100% off your exam for a limited time only!

Reply
MawashiKid
Resolver II
Resolver II

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 //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,

pbivizStart.png

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:
DimpleMock.png
 

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
Trellis.png

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 1
v-chuncz-msft
Community Support
Community Support

@MawashiKid,

 

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.

Community Support Team _ Sam Zha
If this post helps, then please consider Accept it as the solution to help the other members find it more quickly.

Helpful resources

Announcements
April AMA free

Microsoft Fabric AMA Livestream

Join us Tuesday, April 09, 9:00 – 10:00 AM PST for a live, expert-led Q&A session on all things Microsoft Fabric!

March Fabric Community Update

Fabric Community Update - March 2024

Find out what's new and trending in the Fabric Community.