The ultimate Fabric, Power BI, SQL, and AI community-led learning event. Save €200 with code FABCOMM.
Get registeredCompete to become Power BI Data Viz World Champion! First round ends August 18th. Get started.
Hi,
I tried to get that from Chart Utils :
https://github.com/Microsoft/powerbi-visuals-utils-chartutils/blob/master/docs/api/legend.md
and I followed this to install the librarie:
But without any changes to my code and despite it compiles, the Visual crashes at rendering :
Uncaught SyntaxError: Cannot use import statement outside a module
at Object.i [as injectJsCode] (customVisualsHost.bundle.min.js:19)
at r (customVisualsHost.bundle.min.js:21)
at i.loadWithoutResourcePackage (customVisualsHost.bundle.min.js:21)
at i.executeMessage (customVisualsHost.bundle.min.js:21)
at i.onMessageReceived (customVisualsHost.bundle.min.js:21)
at customVisualsHost.bundle.min.js:21
at e.invokeHandler (customVisualsHost.bundle.min.js:20)
at e.dispatchMessage (customVisualsHost.bundle.min.js:20)
at e.onMessageReceived (customVisualsHost.bundle.min.js:20)
at windowMessageHandler (customVisualsHost.bundle.min.js:20)
Is there another library to draw a legend ? I do not need sophisticated one.
Thank you in advance.
Best Regards.
Hi guys,
Thanks for your contribution! @augustindelaf @jppp
The legends have been developed through the above code. Now, how can we handle the overflow of legend items or how can we add buttons that works like standard power bi legend?
Also need to add the title.
Please help me on this.
Thank You!
Hi @augustindelaf,
Looks like that documentation is still out of date and uses the older import methods. I worked this out a while ago and the correct steps are:
npm install powerbi-visuals-utils-chartutils --save
@import (less) "node_modules/powerbi-visuals-utils-interactivityutils/lib/index.css";
@import (less) "node_modules/powerbi-visuals-utils-chartutils/lib/index.css";
Hopefully this gives you some guidance on how to proceed. Good luck!
Daniel
Proud to be a Super User!
On how to ask a technical question, if you really want an answer (courtesy of SQLBI)
Hi Daniel,
Thank you very much, your answer helped a lot to understand how it works. 😉
I have subsidiary questions.
1) I do not see where to set the inputdata in your renderLegend function (for example, I expected an object parameter in the drawLegend method) to have the correct behavior :
For each dot forming the scatter plot of my visual (all dots having a different shape), I need to display in the legend the identifier of the data with the correct shape used and the correct color.
Legend data is usually a parameter field in the panel but I don't need it because the scatter plot data I have is the information I need.
2) I have no idea what format is your Constants in the dependancies section (
import { VisualConstants } from '../constants'
) you use to manage the dimensions of your legend box.
3) In case I have to use the fields panel after all, is it have I to use datarole + capabilities.json to do it ?
Conclusion :
I wish not to use data field in the panel to get data for legend because it wil be redundant with the data used by the visual itself but rather the data object I built (in my update.ts) as an array of objects having all informations needed.
Please feel free to tell me if my wish is not a best practice.
Thank you in advance.
Best regards.
Hi @augustindelaf,
Lately I am always creating legend myself: tried the legend of the chartutils, but found it to complicated/to little control.
How I do it:
1. part of the method to convert the dataView data to your internal data structure store the legend values seperatly
2. in the init method add an SVG g container/placeholder and store in in a private variable
this.legend = this.svg.append("g").classed("legend", true);
3. use d3 functionallity to create the legend based on the data structure of step 1 and allign all the legend labels not to overlap each other, see the .each(function () {}) part
this.legend
.selectAll(".legendItem")
.data(legendNodes)
.join(enter => enter
.append("g")
.classed("legendItem", true)
.each(function (d) {
select(this)
.append("circle")
.classed("legendMarker", true)
.attr("r", 5)
.style("fill", d.data.color)
select(this)
.append("text")
.classed("legendText", true)
.attr("x", 7.5)
.attr("y", 4)
.style("alignment-baseline", "baseline")
.text(d.data.label.value)
})
.each(function () {
let size = (<SVGGElement>this).getBoundingClientRect();
select(this)
.attr("transform", `translate(${moveX} 0)`)
moveX += size.width + 5;
}),
update => update
.each(function (d) {
select(this)
.selectAll(".legendMarker")
.style("fill", d.data.color)
select(this)
.selectAll(".legendText")
.text(d.data.label.value)
})
.each(function (d) {
let size = (<SVGGElement>this).getBoundingClientRect();
select(this)
.attr("transform", `translate(${moveX} 0)')
moveX += size.width + 5;
}),
exit => exit
.remove()
);
4. Positioning of the legend you can do via the legend container including the visibility if needed
Feel free to see if this is working for you.
-JP
Hi @jppp ,
Thank you very much for your answer.
I tried your code but sadly, nothing displays yet : I am reviewing if I made no mistake.
To help me for my reviewing, I have some questions :
1) You use the keyword "select" in your function, This keyword is unknown in my code. What does it refer to ? I assumed "selectAll".
2) There is the variable moveX. I assume it is a number but where to initialize it ?
3) For your 2) I have no Init method, I assume the init of the legend to be in the constructor. Am I correct ?
Once again thank you for your time.
Best regards.
Hi @augustindelaf ,
Sorry missed to provide some extra information:
1. I am using d3-selection and you can import it via
import { select, Selection } from "d3-selection";
2. And just before the settings the legend variable set the moveX variable as:
let moveX = 0;
3. Sorry I mean indeed the contructor instead of the init.
-JP
Hi @jppp,
Thank you.
The legend appeared on first rendering but disapeared on updating.
I will try to relocate the init of the Legend to understand why. 🙂
Thank you, your contribution saved me 😉
Best Regards.