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

Register now to learn Fabric in free live sessions led by the best Microsoft experts. From Apr 16 to May 9, in English and Spanish.

Reply
aristogeiton
Frequent Visitor

Cannot successfully implement color picker on visual using table mapping

am using the table type dataViewMapping.

 

I'm trying to store and retrieve colors, however colors don't save successfully.

 

I'm creating my selection Ids using the code from #77:

 

private static getSelectionIds(dataView: DataView, host: IVisualHost): powerbi.visuals.ISelectionId[] {
            return dataView.table.identity.map((identity: DataViewScopeIdentity) => {
                const categoryColumn: DataViewCategoryColumn = {
                    source: dataView.table.columns[0],
                    values: null,
                    identity: [identity]
                };
        
                return host.createSelectionIdBuilder()
                    .withCategory(categoryColumn, 0)
                    .createSelectionId();
            });

Then I populate the default colors:

 

                this.GANTTEntries = options.dataViews[0].table.rows.map((v, i, a) => 	
                                    [
                                        v[columnNames['stage']],												//Stage name
                                        a.map(i => i[columnNames['stage']]).indexOf(v[columnNames['stage']]),	//Stage surrogate index
                                    ])
                        .filter((v, i) => v[1] === i)														     //Only keep stages groups (based on index)
                        .map((v, i) =>  {
                                            let defaultColor: Fill = {
                                                solid: {
                                                    color: this.host.colorPalette.getColor(options.dataViews[0].table.rows[i][2].toString()).value
                                                }
                                            }
                                            
                                            return {
                                                        id: i,
                                                        content: v[0],
                                                        color: getCategoricalObjectValue<Fill>(options.dataViews[0].categorical.categories[0], i, 'colorSelector', 'fill', defaultColor).solid.color,
                                                        selectionId:    selectionIds[i]
                                                    }
                                        });

Finally I implement my enumerate method:

 

       public enumerateObjectInstances(options: EnumerateVisualObjectInstancesOptions): VisualObjectInstance[] | VisualObjectInstanceEnumerationObject {

            let objectName = options.objectName;
            let objectEnumeration: VisualObjectInstance[] = [];

            switch (objectName) {

                case 'colorSelector':

                    for (let GANTTElement of this.GANTTEntries) {

                        //debugger;

                        objectEnumeration.push({
                            objectName: objectName,
                            displayName: GANTTElement.content.toString(),
                            properties: {
                                fill: {
                                    solid: {
                                        color: GANTTElement.color
                                    }
                                }
                            },
                            selector: GANTTElement.selectionId.getSelector()
                        });
                    }
                    
                break;
            }

            return(objectEnumeration);
        }

Color selections immediately revert to the default. The immediate reason is that there is no stored value (objects), but I am not sure how this all plumbs up and there are no examples that I can find other than the post for #77, which does not seem to work. I can't find the object in the dataview anywhere, and getCategoricalObjectValue returns the default value in all cases (as it can't either). This has taken longer than to implement the rest of the visual. Why is this so difficult?

2 ACCEPTED SOLUTIONS
v-viig
Community Champion
Community Champion

Please this code snippet below to fix the issues with color picker:

 

        private static getSelectionIds(
            dataView: DataView,
            host: IVisualHost
        😞 powerbi.visuals.ISelectionId[] {
            const queryName: string = dataView.table.columns[0].queryName;

            return dataView.table.identity.map((identity: DataViewScopeIdentity) => {
                const categoryColumn: DataViewCategoryColumn = {
                    source: {
                        queryName,
                        displayName: null
                    },
                    values: null,
                    identity: [identity]
                };

                return host.createSelectionIdBuilder()
                    .withCategory(categoryColumn, 0)
                    .withMeasure(queryName)
                    .createSelectionId();
            });
        }

Ignat Vilesov,

Software Engineer

 

Microsoft Power BI Custom Visuals

pbicvsupport@microsoft.com

View solution in original post

Alright thank you !

 

For those with the same situation, I find a workaround.

 

You should use

d3.scale.ordinal().domain(data.map(d => d.dimension).range(color_library)

With color_library an array of color your users can pick ( I take 10  for my chart but you can use 3 or 20...).

 

 

Not the best solution, I have to admit but it works for now until I find something else 🙂

View solution in original post

23 REPLIES 23
v-viig
Community Champion
Community Champion

Hello @aristogeiton

 

The issue is in this code:

getCategoricalObjectValue<Fill>(options.dataViews[0].categorical.categories[0], i, 'colorSelector', 'fill', defaultColor).solid.color,

 

  1. The color for table mapping is kept in options.dataViews[0].table.rows[rowIndex].objects[0] instead of options.dataViews[0].categorical.categories[0].
  2. You should modify getCategoricalObjectValue function to handle options.dataViews[0].table.rows[rowIndex].objects[0properly

 

Please let me know if you have any further questions.

 

Ignat Vilesov,

Software Engineer

 

Microsoft Power BI Custom Visuals

pbicvsupport@microsoft.com

  

 

Thanks for your response Igor. This still doesn't work. Is getSelectionIds() defined correctly? rows[index].objects is undefined in all cases.

The getSelectionIds looks ok.

The rows[index].objects is undefiend if you haven't changed any settings of formatting panel.

 

Could you pleas share the whole source code as a zip?

Just want to debug it deeper.

 

Ignat Vilesov,

Software Engineer

 

Microsoft Power BI Custom Visuals

pbicvsupport@microsoft.com

Hi Igant,

 

I sent this through last week; I hope you received it.

 

Could the problem be that I am running getSelectionIds for each VisualUpdateType = 2? I get a type 2 update whenever I change one of the selections. Is running getSelectionIds() at that time overwriting my selections?

I didn't receive the code. Could you please share tocde by using One Drive?

 

Ignat Vilesov,

Software Engineer

 

Microsoft Power BI Custom Visuals

pbicvsupport@microsoft.com

v-viig
Community Champion
Community Champion

Please this code snippet below to fix the issues with color picker:

 

        private static getSelectionIds(
            dataView: DataView,
            host: IVisualHost
        😞 powerbi.visuals.ISelectionId[] {
            const queryName: string = dataView.table.columns[0].queryName;

            return dataView.table.identity.map((identity: DataViewScopeIdentity) => {
                const categoryColumn: DataViewCategoryColumn = {
                    source: {
                        queryName,
                        displayName: null
                    },
                    values: null,
                    identity: [identity]
                };

                return host.createSelectionIdBuilder()
                    .withCategory(categoryColumn, 0)
                    .withMeasure(queryName)
                    .createSelectionId();
            });
        }

Ignat Vilesov,

Software Engineer

 

Microsoft Power BI Custom Visuals

pbicvsupport@microsoft.com

 I know this is an old post, but I wanted to post a modification to this (I'm using the SDK 2.1.0).

 

In cases where my table was empty, I was seeing an exception in my visual.  I traced it and found that dataView.table.identity can be undefined, which would lead to an exception in this method.  A simple fix is to perform a check for undefined.

 

        private getSelectionIds(dataView: DataView, host: IVisualHost): ISelectionId[] {
            
            if (dataView.table.identity === undefined){
                return [];
            }

            const queryName: string = dataView.table.columns[0].queryName;

            return dataView.table.identity.map((identity: data.DataRepetitionSelector) => {
                const categoryColumn: DataViewCategoryColumn = {
                    source: {
                        queryName,
                        displayName: null
                    },
                    values: null,
                    identity: [identity]
                };

                return host.createSelectionIdBuilder()
                    .withCategory(categoryColumn, 0)
                    .withMeasure(queryName)
                    .createSelectionId();
            });
        }

Can you share whole source code for deeper debugging?

 

Ignat Vilesov,

Software Engineer

 

Microsoft Power BI Custom Visuals

pbicvsupport@microsoft.com

Excellent! It now works! Thanks very much for this!

@v-viig @aristogeiton

 

I am having same issue color picker does not save in custom visual stack bar.

 

what I have to do? do you have any example of custom visual.

 

Please help me.

 

Thanks,

Anand

@v-viig @aristogeiton

 

Below is the code snipet from my actual visual file

 

let colorPalette: IColorPalette = hst.colorPalette;
            if (this.ColumnValues.length > 0 ) 
            {
                for(var i=0; i < this.ColumnValues.length; i++) 
                {
                    let defaultColor: Fill = {
                        solid: {
                            color: colorPalette.getColor(this.ColumnValues[i] + '').value
                        }
                    };
                    var objn  = { FieldName: this.ColumnValues[i] };
                    
                    objn["Color"] = Visual.getCategoricalObjectValue<Fill>(this.ColumnValues[i], i, 'colorSelector', 'fill', defaultColor).solid.color,
                    objn["LegendLabel"] = this.ColumnValues[i];
                    objn["selectionId"] = Visual.getSelectionIds(element.dataViews[0],hst)
                    this.stackOriginalFields.push(objn);
                }
            }
			
	*ColumnValues = Two Different Value of Catehory

enumerate Instance

 

public enumerateObjectInstances(options: EnumerateVisualObjectInstancesOptions): VisualObjectInstanceEnumeration {
            let objectName = options.objectName;
            let objectEnumeration: VisualObjectInstance[] = [];                
            var c = this.stackOriginalFields;
            
			 switch(objectName) {
			 case 'colorSelector':
                    for (let barDataPoint of c) {
                        
                        objectEnumeration.push({
                            objectName: objectName,
                            displayName: barDataPoint.LegendLabel ,
                            properties: {
                                fill: {
                                    solid: {
                                        Color: barDataPoint.Color
                                    }
                                }
                            },
                            //selector: barDataPoint.LegendLabel
							//selector:barDataPoint.selectionId.getSelector() //Its not working						
                            selector:barDataPoint.selectionId
                        });
                    }
                    break;
			 };        
            
            return objectEnumeration;
	}

Get Value

			public static getCategoricalObjectValue<T>(category: DataViewCategoryColumn, index: number, objectName: string, propertyName: string, defaultValue: T): T {
            let categoryObjects = category.objects;
            if (categoryObjects) {
                
                let categoryObject: DataViewObject = categoryObjects[index];
                if (categoryObject) {                    
                    let object = categoryObject[objectName];
                    if (object) {
                        console.log("object"+object)
                        let property: T = <T>object[propertyName];
                        if (property !== undefined) {
                            return property;
                        }
                    }
                }
            }
            return defaultValue;
        }

        private static getSelectionIds(
            dataView: DataView,
            host: IVisualHost
        😞 powerbi.visuals.ISelectionId[] {
            const queryName: string = dataView.table.columns[0].queryName;

            return dataView.table.identity.map((identity: DataViewScopeIdentity) => {
                const categoryColumn: DataViewCategoryColumn = {
                    source: {
                        queryName,
                        displayName: null
                    },
                    values: null,
                    identity: [identity]
                };

                return host.createSelectionIdBuilder()
                    .withCategory(categoryColumn, 0)
                    .withMeasure(queryName)
                    .createSelectionId();
            });
        }

 

*In Capabilities.json file

dataViewMappings": [
      {
        "table": {
          "rows": {
            "select": [
              { "for": { "in": "Columnvalues" }}
            ]
          }
        }
      }
    ]

 

 

Thanks In Advance

 

@anandsoftweb Please take a look at this topic.

 

Ignat Vilesov,

Software Engineer

 

Microsoft Power BI Custom Visuals

pbicvsupport@microsoft.com

Hi @v-viig

 

I'm quite in the same situation as @aristogeiton, I'm using a Table DataViewMapping and I don't know how to retrieve the object when I modify the color in Power BI.

I have already implemented this option in an other custom char with a Categorical DataViewMapping but with the table, it seems different.

I'll try to explain my issue.

 

I have a Table DataViewMapping 

 

"dataViewMappings": [
        {
            "table": {
                "rows": {
                    "select": [
                        {"for": {
                            "in": "Details"
                        }},
                        {"for": {
                            "in": "Yaxis"
                        }},
                        {"for": {
                            "in": "Xaxis"
                        }},
                        {"for": {
                            "in": "Dim2"
                        }},
                        {"for": {
                            "in": "Dim1"
                        }}
                    ]}}
        }]

 To get my data from the dataview, i use this code :

                        dataView.table.rows.forEach((row: DataViewTableRow) => {
                            const columns = this.getColumnGroupByRole(dataView.metadata.columns, row);
                            viewModel.DataPoints.push({
                                Details: <string>columns["Details"] ? <string>columns["Details"] : "Null",
                                Yaxis: <string>columns["Yaxis"] ? <string>columns["Yaxis"] : "Null",
                                Xaxis: <string>columns["Xaxis"] ? <string>columns["Xaxis"] : "Null",
                                Dim1: <string>columns["Dim1"] ? <string>columns["Dim1"] : "Null",
                                Dim2: <string>columns["Dim2"] ? <string>columns["Dim2"] : "Null",
                                color: this.host.colorPalette.getColor(<string>columns["Dim1"]).value,
                                identity: this.getSelectionIds(dataView, this.host)
                                });
                            });

As you can see, my color is define by the value of my "Dim1" column. So, in my enumerateObjectInstances I wrote this :

public enumerateObjectInstances(options: EnumerateVisualObjectInstancesOptions): VisualObjectInstanceEnumeration {
            let objectName = options.objectName;
            let objectEnumeration: VisualObjectInstance[] = [];

            switch (objectName) {
                case "dataColors":
                this.uniqueColorRetrieve(this.finaldata).forEach(d => {
                        objectEnumeration.push({
                            objectName: objectName,
                            displayName: d.name,
                            properties : {
                                fill: d.color
                            },
                            selector: null
                        });
                    });
                break;
            }
            return objectEnumeration;
        }

With "FinalData", the data I use for my SVGs and "uniqueColorRetrieve", a function returning a list of {name,color} pair without duplicates. 

 

I have the right number of color in my parameters in PBI, but when I pick an other color, it goes back to the default color.

I know that my problem is somewhere between my color definition and my color object but I can't find where....

 

I saw the situation of @aristogeiton, but without the full code, I don't fully understand what it does.

 

Do you perhaps know what I do wrong ? 

Thanks in advance !

 

v-viig
Community Champion
Community Champion

@FR5702 To parse color properly you should specify a selector for each item of finalData.

 

this.uniqueColorRetrieve(this.finaldata).forEach(d => {
                        objectEnumeration.push({
                            objectName: objectName,
                            displayName: d.name,
                            properties : {
                                fill: d.color
                            },
                            selector: d.identity.getSelector(),
                        });
                    });

 

You should also parse color for row by using something like this:

 

row.objects[0] // It will contain properties. Please use console.log to figure out its structure.

Ignat Vilesov,

Software Engineer

 

Microsoft Power BI Custom Visuals

pbicvsupport@microsoft.com

Hi @v-viig,

 

Thanks for your tips, I have a better understanding of how it works.

 

But I still have a problem...

 

To recap, if it's confusing for you, here what I'm doing:

 

1) I take all my datapoints (with their color define by  

color: this.host.colorPalette.getColor(<string>columns["Dim1"]).value,

and identity define by

this.getSelectionIds(dataView, this.host)

with

private  getSelectionIds( dataView: DataView, host: IVisualHost): powerbi.visuals.ISelectionId[] {
            const queryName: string = dataView.table.columns[0].queryName;

            return dataView.table.identity.map((identity: DataViewScopeIdentity) => {
                const categoryColumn: DataViewCategoryColumn = {
                    source: {
                        queryName,
                        displayName: null
                    },
                    values: null,
                    identity: [identity]
                };

                return host.createSelectionIdBuilder()
                    .withCategory(categoryColumn, 0)
                    .withMeasure(queryName)
                    .createSelectionId();
            });
        }

)

 

2) I group them by my four dimensions = I create X groups

3) I represent them on my chart = 1 point per group

 

The result is this one :

Capture1.PNG

 

And I want to be able to change the color of my groups depending on 1 Dimension ( Dim1).

Capture1.PNG

As it needs the identity to bound my object with my data, I use the identity from my datapoints.

But how can I use all the identities of the datapoints having the same Dim1 value ? Because if I use an identity from my dataPoints, it'll not have any effect as the other dataPoints of his group won't be bound to my object and therefore won't change.

 

I tried to define an identity for each of my groups with :

this.host.createSelectionIdBuilder().withMeasure(list[i].Dim1).createSelectionId()

but it didn't change anything.

 

And when I change my Datapoints' identites by these group identities, my chart broke while my data are fine (I checked in my console).

 

My question is : How can I change my DataPoints' identities to group identities without breaking my chart ? Or is there an other solution ?

 

Thank you for your help, I really need it 🙂

v-viig
Community Champion
Community Champion

To support such grouping you should start using categorical data mapping instead of table mapping.

Categorical mapping supports "group by" option.

 

Ignat Vilesov,

Software Engineer

 

Microsoft Power BI Custom Visuals

pbicvsupport@microsoft.com

 

Thanks @v-viig I get the logic but I don't know how it can be apply to my case as I don't have any value data (all my data are text format).

 

How can I go from

"table": {
                "rows": {
                    "select": [
                        {"for": {
                            "in": "Details"
                        }},
                        {"for": {
                            "in": "Yaxis"
                        }},
                        {"for": {
                            "in": "Xaxis"
                        }},
                        {"for": {
                            "in": "Dim2"
                        }},
                        {"for": {
                            "in": "Dim1"
                        }}
                    ]
                },
                "conditions": [{
                    "Details":{
                        "max": 1
                    },
                    "Yaxis":{
                        "max": 1
                    },
                    "Xaxis":{
                        "max": 1
                    },
                    "Dim1":{
                        "max": 1
                    },
                    "Dim2":{
                        "max": 1
                    }
                }]
            }

 to something similar to the examples 2 or 3

 

v-viig
Community Champion
Community Champion

Don't have numbers? If so, there's no way to impelement desired behavior w/o using numbers.

 

Ignat Vilesov,

Software Engineer

 

Microsoft Power BI Custom Visuals

pbicvsupport@microsoft.com

Yes, I transform my data from text to numeric in my code, but what I get from my DataSource are all text data.

 

There no way to have this behaviour without numbers directly in my DataSource ? Or do I have to make all the transformation when I create my ViewModel/DataPoints in order to enable my color picker ?

 

 

v-viig
Community Champion
Community Champion

Correct. You should have numeric data column in a data set. Such column will allow grouping that required to implement desired behavior.

 

Ignat Vilesov,

Software Engineer

 

Microsoft Power BI Custom Visuals

pbicvsupport@microsoft.com

Helpful resources

Announcements
Microsoft Fabric Learn Together

Microsoft Fabric Learn Together

Covering the world! 9:00-10:30 AM Sydney, 4:00-5:30 PM CET (Paris/Berlin), 7:00-8:30 PM Mexico City

PBI_APRIL_CAROUSEL1

Power BI Monthly Update - April 2024

Check out the April 2024 Power BI update to learn about new features.

April Fabric Community Update

Fabric Community Update - April 2024

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