Forum Discussion
Sliding legend
The source code I linked for the violin plot uses 2.5, which will also work for 2.3 if you're using that. You'll need to downgrade chartutils as well as the latest version has been updated to support v3 of the SDK.
I can re-write the above but will need to do it tomorrow as I'm away from my desk until then and will need to reinstall 2.x to confirm the solution for you.
Hi Anonymous,
I've re-done the above in 2.3 for you. The process is closer to the install guide but there are still some issues in getting it to work. Here's the run-down:
Make sure that if you're running pbiviz start, then you've stopped it prior to installation as you'll get inconsistent results after package installation.
Package Installation
I had to downgrade until I could find one that will work. 1.7.0 will run, but colour seemed to stop working after 1.5, so run the following command to import:
npm i [email protected]
The installed packages have a dependency on d3, which it does install, but make sure that the typings are installed for it:
npm i @types/[email protected] --save-dev
To confirm, after instantiating a new visual and installing packages, my package.json looks like this, so you could just copy/paste and then run npm i to sync:
{
"name": "visual",
"dependencies": {
"powerbi-visuals-utils-chartutils": "^1.5.1",
"powerbi-visuals-utils-dataviewutils": "1.2.0"
},
"devDependencies": {
"@types/d3": "^3.5.42"
}
}tsconfig.json
Add the following to the files key in tsconfig.json before settings.ts:
"node_modules/powerbi-visuals-utils-formattingutils/lib/index.d.ts",
"node_modules/powerbi-visuals-utils-interactivityutils/lib/index.d.ts",
"node_modules/powerbi-visuals-utils-svgutils/lib/index.d.ts",
"node_modules/powerbi-visuals-utils-typeutils/lib/index.d.ts",
"node_modules/powerbi-visuals-utils-chartutils/lib/index.d.ts",The files key looks like this in mine:
"files": [
".api/v2.5.0/PowerBI-visuals.d.ts",
"node_modules/powerbi-visuals-utils-dataviewutils/lib/index.d.ts",
"node_modules/powerbi-visuals-utils-formattingutils/lib/index.d.ts",
"node_modules/powerbi-visuals-utils-interactivityutils/lib/index.d.ts",
"node_modules/powerbi-visuals-utils-svgutils/lib/index.d.ts",
"node_modules/powerbi-visuals-utils-typeutils/lib/index.d.ts",
"node_modules/powerbi-visuals-utils-chartutils/lib/index.d.ts",
"src/settings.ts",
"src/visual.ts"
]pbiviz.json
Add the following to the externalJS key in pbiviz.json:
"node_modules/d3/d3.min.js",
"node_modules/powerbi-visuals-utils-typeutils/lib/index.js",
"node_modules/powerbi-visuals-utils-svgutils/lib/index.js",
"node_modules/powerbi-visuals-utils-formattingutils/lib/index.js",
"node_modules/powerbi-visuals-utils-interactivityutils/lib/index.js",
"node_modules/powerbi-visuals-utils-chartutils/lib/index.js"The externalJS key looks like this in mine:
"externalJS": [
"node_modules/powerbi-visuals-utils-dataviewutils/lib/index.js",
"node_modules/d3/d3.min.js",
"node_modules/powerbi-visuals-utils-typeutils/lib/index.js",
"node_modules/powerbi-visuals-utils-svgutils/lib/index.js",
"node_modules/powerbi-visuals-utils-formattingutils/lib/index.js",
"node_modules/powerbi-visuals-utils-interactivityutils/lib/index.js",
"node_modules/powerbi-visuals-utils-chartutils/lib/index.js"
]visual.less
It shouldn't matter for this scenario, but to mirror the installation guide, make sure the following are present at the top of visual.less:
@import (less) "node_modules/powerbi-visuals-utils-interactivityutils/lib/index.css"; @import (less) "node_modules/powerbi-visuals-utils-chartutils/lib/index.css";
visual.ts
The solution in the previous post is largely unchanged, except for:
- The way dependencies are imported, e.g.:
import createLegend = powerbi.extensibility.utils.chart.legend.createLegend; import ILegend = powerbi.extensibility.utils.chart.legend.ILegend; import Legend = powerbi.extensibility.utils.chart.legend; import LegendData = powerbi.extensibility.utils.chart.legend.LegendData; import LegendIcon = powerbi.extensibility.utils.chart.legend.LegendIcon; import LegendPosition = powerbi.extensibility.utils.chart.legend.LegendPosition; import LegendDataPoint = powerbi.extensibility.utils.chart.legend.LegendDataPoint; - LegendDataPoint doesn't use markerShape as a property and this is icon in this version, so double-check this part of the code if you're amending your local version (the compiler will error if not correct), e.g.:
{ label: <string>c, color: this.host.colorPalette.getColor(<string>c).value, identity: this.host.createSelectionIdBuilder() .withCategory(categoryRole, i) .createSelectionId(), selected: false, icon: LegendIcon.Circle }
Again just to be sure, here's the full visual.ts:
/** powerbi.extensibility.utils.chart.legend */
import createLegend = powerbi.extensibility.utils.chart.legend.createLegend;
import ILegend = powerbi.extensibility.utils.chart.legend.ILegend;
import Legend = powerbi.extensibility.utils.chart.legend;
import LegendData = powerbi.extensibility.utils.chart.legend.LegendData;
import LegendIcon = powerbi.extensibility.utils.chart.legend.LegendIcon;
import LegendPosition = powerbi.extensibility.utils.chart.legend.LegendPosition;
import LegendDataPoint = powerbi.extensibility.utils.chart.legend.LegendDataPoint;
module powerbi.extensibility.visual {
"use strict";
export class Visual implements IVisual {
private target: HTMLElement;
private settings: VisualSettings;
private legend: ILegend;
private host: IVisualHost;
constructor(options: VisualConstructorOptions) {
console.log('Visual constructor', options);
this.target = options.element;
/** Assign visual host */
this.host = options.host;
/** Reserve element for legend */
this.legend = createLegend(
this.target,
false,
null,
true, /* scrollable */
LegendPosition.Top
);
}
public update(options: VisualUpdateOptions) {
this.settings = Visual.parseSettings(options && options.dataViews && options.dataViews[0]);
console.log('Visual update', options);
/** THIS ASSUMES THAT YOUR DATA VIEW WORKS, SO THERE'S NO ERROR HANDLING. YOU WILL AT LEAST
* NEED A VALUE IN THE CATEGORY DATA ROLE TO CONTINUE */
let categoryRole = options.dataViews[0].categorical.categories[0];
/** We're now going to create an array of LegendDataPoint objects to feed into our drawLegend
* method below.
* We're arbitrarily assigning a colour from the host's palette (theme) as it's required. You
* might want to sub this out with your own from your view model */
let myLegendDataPoints: LegendDataPoint[] = categoryRole.values.map(
(c, i) => (
{
label: <string>c,
color: this.host.colorPalette.getColor(<string>c).value,
identity: this.host.createSelectionIdBuilder()
.withCategory(categoryRole, i)
.createSelectionId(),
selected: false,
icon: LegendIcon.Circle
}
)
);
/** This actually draws the legend; note that although font settings aren't required, it will look
* 'wrong' if they're not supplied */
this.legend.drawLegend(
{
title: 'Test Legend',
fontSize: 10,
dataPoints: myLegendDataPoints
},
options.viewport
);
}
private static parseSettings(dataView: DataView): VisualSettings {
return VisualSettings.parse(dataView) as VisualSettings;
}
/**
* This function gets called for each of the objects defined in the capabilities files and allows you to select which of the
* objects and properties you want to expose to the users in the property pane.
*
*/
public enumerateObjectInstances(options: EnumerateVisualObjectInstancesOptions): VisualObjectInstance[] | VisualObjectInstanceEnumerationObject {
return VisualSettings.enumerateObjectInstances(this.settings || VisualSettings.getDefault(), options);
}
}
}At this point you can run pbiviz start and this will work as for the v3 example above.
Good luck!
Daniel
- Anonymous7 years agoNot applicable
dm-p Daniel,
Thanks for detailed answer! It was very helpful.
I have some questions regarding it. I added it to my line chart, which has categorical dataview mapping, and can I pass to legend grouping of categorical data view? I tried using this:
let categoryRole = options.dataViews[0].categorical.values.grouped();
But it gave me error.
Also, I don't want to use selection id, if I leave it empty, I get error.
And finally, even though scrollable parameter is true, I can't scroll:
When I click on arrow, it doesn't work.
Regards,
Yerkhan
- dm-p7 years agoSuper User
Hi Anonymous,
I mentioned in my earlier post that you will need to have a selection ID for each entry, otherwise the legend utility will not work. Unfortunately for your case you cannot provide a null value here so will need to provide something valid. It's highly likely that this is causing your legend to break.
Daniel
- Anonymous7 years agoNot applicable
Hi dm-p ,
Got it regarding selection id. How does regular line chart works? It has date as axis, category as grouping for data, and legend is built based on category. I wanted to recreate it, but with cumulative values.
Regards,
Yerkhan
- dm-p7 years agoSuper User
Hi Anonymous,
I'm not entirely sure I follow - you can manage cumulative measures with the core line chart and DAX. Is there a specific feature you're trying to manage in your custom visual?
Thanks,
Daniel
- Anonymous6 years agoNot applicable
dm-p ,
I can't use DAX since I'm using live connection to OLAP cube. I wanted to recreate line chart, but make cumulative calculations inside of visual. However, by legend doesn't work, as I mentioned before.
Regards,
Yerkhan
- Anonymous6 years agoNot applicable
Also, when I click on arrow in scrollable legend in bar chart, text color of legend changes. By default it was black. I changed it to white using visual.less. When I scroll legend, it changes again to black.
How it can be changed?
Regards,
Yerkhan
- dm-p6 years agoSuper User
From the looks of things, there is something that happens with re-drawing the nav buttons when clicked that seems to ignore any styling or changes applied. I made some attempts in one of my visuals to do something similar in the past (to change the shape of the icons), but found that it was more trouble that it was worth and abandoned my efforts.
If you want to do this in a maintainable way, then I think you'll need to create an idea with the custom visuals team to add support. You can refer to this post in the Custom Visuals Development Discussion Forum for more details.
Regarding your other question, if you can't write DAX in-model, I would suggest requesting the owners of the OLAP cube add in such a measure for you - these tools are designed for such functionality and it is minimal work for them to manage and support. If the OLAP cube is SSAS, then SSAS Tabular uses DAX for its measures; SSAS Multidimensional can achieve the same via MDX. Solving in the semantic model is going to be the way to go.
Not to discourage learning how to develop custom visuals at all - I would personally love to have more people to collaborate with and learn from - but this is a very heavy approach to solving a problem that can be resolved upstream and is going to be several of hours' work from my side to document how such a solution could conceivably work (and much more from yours to develop). I'm a big subscriber to working smarter rather than harder. As it's outside the scope of the original question, I think it probably warrants its own thread if you specifically want to go through this.
However, if I were to do this, then I'd start by working with the standard categorical data mapping that comes with a new visual project, and set my view model hierarchy as measure[] > category[] > value[]. Once I've mapped my distinct measures and categories, as I iterate through the value array, I'd add to the previous value so that each subsequent value in the value array for each measure/category is a cumulative total.
- Anonymous6 years agoNot applicable
dm-p ,
Firstly, I tried adding cumulative value to OLAP cube, however, it didn't result in required field.
Therefore, I started building custom visual. I managed to overcome issue with legend text color and changing position of legend.
view model hierarchy as measure[] > category[] > value[]
Could explain this a bit more?
Regards,
Yerkhan
- Anonymous6 years agoNot applicable
dm-p ,
Hi again,
How can I make sliding legend for visual with table dataview mapping?
I got error at identity, using getSelectionIds.
Regards,
Yerkhan