Forum Discussion

a-smiggle's avatar
a-smiggle
Regular Visitor
1 year ago

Power BI Custom Visual: Customize series mechanism example

As per this: https://powerbi.microsoft.com/en-us/blog/introducing-the-new-format-pane-preview/

Is there an example on how to get the customize series mechanism.

In my formatting pane I am getting the options succeffully in the dropdown and in the formattingSettings object. But they are not updating.

Return formattingSettings:

{
    "columnWidth": {
        "container": {
            "displayName": "Apply settings to",
            "containerItems": [
                {
                    "width": {
                        "name": "width",
                        "displayName": "Column Width",
                        "description": "Set the width of this column (pixels)",
                        "value": 120,
                        "options": {
                            "maxValue": {
                                "type": 1,
                                "value": 1000
                            },
                            "minValue": {
                                "type": 0,
                                "value": 20
                            }
                        },
                        "type": "NumUpDown"
                    },
                    "name": "row_0",
                    "displayName": "work_year",
                    "slices": [
                        {
                            "name": "width",
                            "displayName": "Column Width",
                            "description": "Set the width of this column (pixels)",
                            "value": 120,
                            "options": {
                                "maxValue": {
                                    "type": 1,
                                    "value": 1000
                                },
                                "minValue": {
                                    "type": 0,
                                    "value": 20
                                }
                            },
                            "type": "NumUpDown"
                        }
                    ]
                },
                {
                    "width": {
                        "name": "width",
                        "displayName": "Column Width",
                        "description": "Set the width of this column (pixels)",
                        "value": 120,
                        "options": {
                            "maxValue": {
                                "type": 1,
                                "value": 1000
                            },
                            "minValue": {
                                "type": 0,
                                "value": 20
                            }
                        },
                        "type": "NumUpDown"
                    },
                    "name": "row_1",
                    "displayName": "job_title",
                    "slices": [
                        {
                            "name": "width",
                            "displayName": "Column Width",
                            "description": "Set the width of this column (pixels)",
                            "value": 120,
                            "options": {
                                "maxValue": {
                                    "type": 1,
                                    "value": 1000
                                },
                                "minValue": {
                                    "type": 0,
                                    "value": 20
                                }
                            },
                            "type": "NumUpDown"
                        }
                    ]
                },
                {
                    "width": {
                        "name": "width",
                        "displayName": "Column Width",
                        "description": "Set the width of this column (pixels)",
                        "value": 120,
                        "options": {
                            "maxValue": {
                                "type": 1,
                                "value": 1000
                            },
                            "minValue": {
                                "type": 0,
                                "value": 20
                            }
                        },
                        "type": "NumUpDown"
                    },
                    "name": "value_0",
                    "displayName": "Sum of salary_in_usd",
                    "slices": [
                        {
                            "name": "width",
                            "displayName": "Column Width",
                            "description": "Set the width of this column (pixels)",
                            "value": 120,
                            "options": {
                                "maxValue": {
                                    "type": 1,
                                    "value": 1000
                                },
                                "minValue": {
                                    "type": 0,
                                    "value": 20
                                }
                            },
                            "type": "NumUpDown"
                        }
                    ]
                },
                {
                    "width": {
                        "name": "width",
                        "displayName": "Column Width",
                        "description": "Set the width of this column (pixels)",
                        "value": 120,
                        "options": {
                            "maxValue": {
                                "type": 1,
                                "value": 1000
                            },
                            "minValue": {
                                "type": 0,
                                "value": 20
                            }
                        },
                        "type": "NumUpDown"
                    },
                    "name": "value_1",
                    "displayName": "RR",
                    "slices": [
                        {
                            "name": "width",
                            "displayName": "Column Width",
                            "description": "Set the width of this column (pixels)",
                            "value": 120,
                            "options": {
                                "maxValue": {
                                    "type": 1,
                                    "value": 1000
                                },
                                "minValue": {
                                    "type": 0,
                                    "value": 20
                                }
                            },
                            "type": "NumUpDown"
                        }
                    ]
                }
            ]
        },
        "name": "columnWidth",
        "displayName": "Column Width"
    }
}



This is what I code have implemented:

- capabilties.json: 

object: {
"columnWidth": {
      "displayName": "Column Widths",
      "properties": {
        "width": {
          "displayName": "Column Width",
          "description": "Set Column Width.",
          "type": {
            "numeric": true
          }
        }
      }
    }
}

- Settings.ts:

import powerbi from "powerbi-visuals-api";
import { formattingSettings } from "powerbi-visuals-utils-formattingmodel";

// Column Width Card for per-column dropdown
export class ColumnWidthCardItem extends formattingSettings.SimpleCard {
    public width: formattingSettings.NumUpDown = new formattingSettings.NumUpDown({
        name: "width",
        displayName: "Column Width",
        description: "Set the width of this column (pixels)",
        value: 120,
        options: {
            maxValue: {
                type: powerbi.visuals.ValidatorType.Max,
                value: 1000
            },
            minValue: {
                type: powerbi.visuals.ValidatorType.Min,
                value: 20
            }
        }
    });
    constructor(name?: string, displayName?: string, widthValue?: number) {
        super();
        this.name = name || "";
        this.displayName = displayName || name || "";
        if (typeof widthValue === "number") {
            this.width.value = widthValue;
        }
        this.slices = [this.width];
    }
}

class ColumnWidthCardSetting extends formattingSettings.SimpleCard {

  
    public container: formattingSettings.Container = {
        displayName: "Apply settings to",
        containerItems:[new ColumnWidthCardItem()]
    };

    constructor() {
        super();
        this.name = "columnWidth";
        this.displayName = "Column Width";
    }
    
}



// Main Settings Model
/**
 * Main settings model for the Power BI visual formatting pane. Contains all formatting cards and option groups.
 */
export class VisualSettingsModel {
    columnWidth: ColumnWidthCardSetting;
    cards: Array<formattingSettings.SimpleCard | formattingSettings.CompositeCard>;
    constructor() {
      
        this.columnWidth = new ColumnWidthCardSetting();
        this.cards = [
            this.columnWidth
        ];
    }
}

-visual.ts:

this.formattingSettings = this.formattingSettingsService.populateFormattingSettingsModel(VisualSettingsModel, dataView);
      // Dynamically create per-column width cards for the formatting pane using formatted columns
      if (dataView && dataView.matrix) {
        const formatted = formatMatrixForFGGrid(dataView.matrix);
        console.log("Formatted: ", formatted)
        const formattedColumns = formatted.columns || [];
        const fieldWidthCardItems = formattedColumns.map((col: any) => {
          // Use col.index for name and col.title for displayName
          let widthValue = 120;
          if (this.formattingSettings?.columnWidth?.container?.containerItems) {
            const match = this.formattingSettings.columnWidth.container.containerItems.find((item: any) => item.name === col.index);
            if (match && match.width) {
              widthValue = match.width.value;
            }
          }
          return new ColumnWidthCardItem(col.index, col.title, widthValue);
        });
        console.log("fieldWidthCardItems: ", fieldWidthCardItems);
        // Set the containerItems array on the formattingSettings model
        if (this.formattingSettings?.columnWidth?.container) {
          console.log("Updating columnWidth with formatted columns.");
          this.formattingSettings.columnWidth.container.containerItems = [];
          this.formattingSettings.columnWidth.container.containerItems.push(...fieldWidthCardItems);
          console.log("formattingSettings: ", this.formattingSettings);
        }
      }

 

2 Replies

  • BeaBF's avatar
    BeaBF
    Icon for Super User rankSuper User

    a-smiggle 

     

    Settings.ts:

    import powerbi from "powerbi-visuals-api";
    import { formattingSettings, formattingSettingsCard, formattingSettingsTypes } from "powerbi-visuals-utils-formattingmodel";

    // --- Dynamic Column Width Item ---
    export class ColumnWidthCardItem extends formattingSettings.SimpleCard {
    public width: formattingSettings.NumUpDown;

    constructor(name: string, displayName: string, widthValue: number) {
    super();
    this.name = name; // dynamic key
    this.displayName = displayName; // what shows in the formatting pane

    this.width = new formattingSettings.NumUpDown({
    name: "width",
    displayName: "Column Width",
    value: widthValue,
    options: {
    minValue: { type: formattingSettingsTypes.ValidatorType.Min, value: 20 },
    maxValue: { type: formattingSettingsTypes.ValidatorType.Max, value: 1000 },
    },
    });

    // Important: binds this property to the card
    this.slices = [this.width];
    }
    }

    // --- Container Card ---
    export class ColumnWidthCardSetting extends formattingSettings.SimpleCard {
    public container: formattingSettings.Container;

    constructor() {
    super();
    this.name = "columnWidth";
    this.displayName = "Column Width";

    // Initialize empty container for dynamic items
    this.container = {
    displayName: "Apply settings to",
    containerItems: []
    };
    }
    }

    // --- Main Visual Settings Model ---
    export class VisualSettingsModel {
    columnWidth: ColumnWidthCardSetting;
    cards: Array<formattingSettings.SimpleCard | formattingSettings.CompositeCard>;

    constructor() {
    this.columnWidth = new ColumnWidthCardSetting();
    this.cards = [this.columnWidth];
    }
    }

     

    Visual.ts:

    // Populate formatting settings
    this.formattingSettings = this.formattingSettingsService.populateFormattingSettingsModel(
    VisualSettingsModel,
    dataView
    );

    // Only proceed if dataView has matrix data
    if (dataView && dataView.matrix) {
    const formattedColumns = formatMatrixForFGGrid(dataView.matrix).columns || [];

    // Generate dynamic container items per column
    const containerItems = formattedColumns.map((col: any) => {
    // Default width value (can come from previous saved settings)
    let widthValue = 120;

    // Check if a value already exists in formattingSettings
    const match = this.formattingSettings.columnWidth.container.containerItems.find(
    (item: any) => item.name === col.index
    );
    if (match && match.width) {
    widthValue = match.width.value;
    }

    return new ColumnWidthCardItem(col.index, col.title, widthValue);
    });

    // Assign dynamically generated items to the container
    this.formattingSettings.columnWidth.container.containerItems = containerItems;
    }

    // --- OPTIONAL: log to debug ---
    console.log("Dynamic ColumnWidth Settings:", this.formattingSettings);

     

     

    • Each ColumnWidthCardItem has a slices array pointing to its width property.

    • Do not replace the entire formattingSettings object after the visual has been initialized — just update containerItems.

    • populateFormattingSettingsModel must run before rendering the formatting pane, so Power BI knows about the slices.

    • The name of the card item (col.index) is used to track which column the setting belongs to.

    BBF


    💡 Did I answer your question? Mark my post as a solution!

    👍 Kudos are appreciated

    🔥 Proud to be a Super User!

     

  • Getting the “Apply settings to” dropdown to appear is only part of the setup.

    The important bit is making sure the selected series is actually used when you build the formatting model and when you read those values during update/render.

    I'd check two things first:

    • What value you're using to identify the selected series.

    • Whether that value is mapped back to the actual series identity when the visual updates.

    If the formatting pane changes but the visual doesn't, I'd also log the formattingSettings object on every update and confirm that the selected series value is actually changing.

    That usually makes it much easier to see whether the issue is in the formatting model or in how the visual consumes the setting.