Don't miss your chance to take the Fabric Data Engineer (DP-600) exam for FREE! Find out how by attending the DP-600 session on April 23rd (pacific time), live or on-demand.
Learn moreNext up in the FabCon + SQLCon recap series: The roadmap for Microsoft SQL and Maximizing Developer experiences in Fabric. All sessions are available on-demand after the live show. Register now
Hello All,
I am developing custom visual for a clustered column chart and I am facing issue while implementing the color palette.
I have added the color palette successfully but cannot use the color value for the series.
I can get it to work withCategory, but I need to set the color per the measure. I am having trouble implementing
host.createSelectionIdBuilder().withMeasure()
Once I select a color from palette it returns to the default.
Below is my code snipet for updating the view model and for rendering the Power BI properties.
//set up viewmovdel
//queries power bi to gather data that user is using for the visual
//power bi makes VisualUpdateOptions available with every call to the update method
private getViewModel(options: VisualUpdateOptions): ViewModel {
//option.dataViews stores power bi's query data based on shape (e.g. tabular, matrix, tree, etc.)
//configured in capabilities.json dataViewMappings.categorical
//https://microsoft.github.io/PowerBI-visuals/docs/concepts/dataviewmappings/
let dv = options.dataViews;
//empty viewModel
//The empty viewModel will be returned in the case where there is no data to show
let viewModel: ViewModel = {
dataPoints: []
, maxValue: 0
, minValue: 0
, highlights: false
};
//Check to make sure that all data that is needed is present before attempting to render the visual
if (!dv
|| !dv[0]
|| !dv[0].categorical
|| !dv[0].categorical.categories
|| !dv[0].categorical.categories[0].source
|| !dv[0].categorical.values
|| !dv[0].metadata
)
return viewModel;
//Only accepting one dataview
// The type that we are using is the categorical dataview
let view = dv[0].categorical;
//because we are accepting multiple measure types, iterate through each
for (let i = 0, len = view.values.length; i < len; i++) {
//We are only allowing for one series, so use index of 0 for categories
let categories = view.categories[0];
let values = view.values[i];
let highlights = values.highlights;
let objects = categories.objects;
let metadata = dv[0].metadata;
//metadata values will be used to populate the tooltip
let categoryColumnName = metadata.columns.filter(c => c.roles["category"])[0].displayName;
let measureColumnName = metadata.columns.filter(c => c.roles["measure"])[i].displayName;
//iterate over all category-value pairs by index
for (let j = 0, len = Math.max(categories.values.length, values.values.length); j < len; j++) {
//push datapoints into the view
viewModel.dataPoints.push({
category: <string>categories.values[j],
measure: <string>values.source.displayName,
value: <number>values.values[j],
//Since this property is data-bound, the property value is stored in the objects array
//if objects array exists and the color for
color: objects && objects[j] && dataViewObjects.getFillColor(objects[i], {
objectName: "dataColors"
, propertyName: "fill"
}, null) || this.host.colorPalette.getColor(<string>categories.values[i]).value,
//a new SelectionIdBuilder is needed everytime a new selection id is needed
identity: this.host.createSelectionIdBuilder()
//slice over category column at position i
.withCategory(categories, j)
.createSelectionId(),
measureIdentity: this.host.createSelectionIdBuilder()
.withMeasure(values.source.queryName)
.createSelectionId(),
//checks to see if the highlights array is defined
// then checks to see if any value exists in the same position of the corresponding
// values array, not the metadata array
highlighted: highlights ? highlights[j] ? true : false : false,
//set up the base tooltip for each datapoint
tooltips: [{
displayName: categoryColumnName,
value: <string>categories.values[j]
}, {
displayName: measureColumnName,
//if the value doesn't have a decimal, set value to 0 decimal places, otherwise set it 2 decimal places
value: ((<number>values.values[j]) == Math.floor(<number>values.values[j])) ? (<number>values.values[j]).toFixed(0) : (<number>values.values[j]).toFixed(2)
}]
});
}
}
//populate the single-valued datapoints
viewModel.maxValue = d3.max(viewModel.dataPoints, d => d.value);
viewModel.minValue = d3.min(viewModel.dataPoints, d => d.value);
//boolean to see whether or not there are any highlights
viewModel.highlights = viewModel.dataPoints.filter(d => d.highlighted).length > 0;
return viewModel;
}
//called by power bi everytime it needs to render the properties pane
//for updating a property/property value in the properties pane
//Driven by the object object in capabilities.json
//called one time per property group in object object any time anything is updated in power bi
//* Note - this takes place outside of the update loop
public enumerateObjectInstances(options: EnumerateVisualObjectInstancesOptions): VisualObjectInstanceEnumeration {
let propertyGroupName = options.objectName;
let properties: VisualObjectInstance[] = [];
switch (propertyGroupName) {
case "xAxis":
properties.push({
objectName: propertyGroupName,
properties: {
show: this.settings.axis.x.show.value
},
selector: null
});
break;
case "yAxis":
properties.push({
objectName: propertyGroupName,
properties: {
show: this.settings.axis.y.show.value
, invert: this.settings.axis.y.invert.value
, minimumDomain: this.settings.axis.y.minimumDomain.value
, integers: this.settings.axis.y.integers.value
, gridlines: this.settings.axis.y.gridlines.show.value
},
selector: null
});
break;
case "dataColors":
{
if (this.viewModel) {
//list out all data point properties that are needed
let allColorDatPoints = [];
this.viewModel.dataPoints.forEach(dp => {
allColorDatPoints.push({measureIdentity: dp.measureIdentity, measure: dp.measure, color: dp.color})
})
//get unique list of needed data point properties
let colorDatPoints = [];
for(let i=0; i<allColorDatPoints.length;i++){
let exists = false;
for(let j=0; j<colorDatPoints.length; j++){
if(//allColorDatPoints[i].measureIdentity == colorDatPoints[j].measureIdentity
//&&
allColorDatPoints[i].measure == colorDatPoints[j].measure
&& allColorDatPoints[i].color == colorDatPoints[j].color
) {
exists = true;
}
}
(!exists) ? colorDatPoints.push(allColorDatPoints[i]) : null
}
for (let cdp of colorDatPoints) {
properties.push({
objectName: cdp.measure
, displayName: cdp.measure
, properties: {
fill: cdp.color
},
selector: cdp.measureIdentity.getSelector()
});
}
}
break;
}
}
return properties;
}
}
}
Solved! Go to Solution.
With measures, I've only got it to work by using the queryName property from the measure DataViewMetadataColumn. If you use this rather than getSelector() against a SelectionId it should push it into the data view correctly.
If done right, you should see an objects key underneath your column in the metadata, e.g.:
You should be able to make out the queryName property from this screenshot too.
This should then get persisted into your object enumeration, e.g.:
Hopefully this is enough to get you moving; if not, you might need to share some code so we can have a look at specifics.
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,
Did you manage to resolve this issue? I am having the same problem and can't figure out how to read the measure colors
Thanks
Nishant
With measures, I've only got it to work by using the queryName property from the measure DataViewMetadataColumn. If you use this rather than getSelector() against a SelectionId it should push it into the data view correctly.
If done right, you should see an objects key underneath your column in the metadata, e.g.:
You should be able to make out the queryName property from this screenshot too.
This should then get persisted into your object enumeration, e.g.:
Hopefully this is enough to get you moving; if not, you might need to share some code so we can have a look at specifics.
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)
If you have recently started exploring Fabric, we'd love to hear how it's going. Your feedback can help with product improvements.
A new Power BI DataViz World Championship is coming this June! Don't miss out on submitting your entry.
Experience the highlights from FabCon & SQLCon, available live and on-demand starting April 14th.
| User | Count |
|---|---|
| 1 | |
| 1 | |
| 1 | |
| 1 | |
| 1 |
| User | Count |
|---|---|
| 8 | |
| 8 | |
| 4 | |
| 3 | |
| 2 |