Events
Power BI DataViz World Championships
14 Feb, 16 - 31 Mar, 16
With 4 chances to enter, you could win a conference package and make it to the LIVE Grand Finale in Las Vegas
Learn moreThis browser is no longer supported.
Upgrade to Microsoft Edge to take advantage of the latest features, security updates, and technical support.
Power BI provides two ways to interact with visuals - selecting and filtering. The following example demonstrates how to select an item from one visual and notify the other visuals in the report about the new selection state.
The interface corresponds to a Selection
object:
export interface ISelectionId {
equals(other: ISelectionId): boolean;
includes(other: ISelectionId, ignoreHighlight?: boolean): boolean;
getKey(): string;
getSelector(): Selector;
getSelectorsByColumn(): SelectorsByColumn;
hasIdentity(): boolean;
}
The visual host object provides a method for creating an instance of the selection manager. The selection manager has a corresponding method for each of the following actions:
To use the selection manager, create the instance of a selection manager. Usually, visuals create a selection manager instance in the constructor
section of the visual object.
export class Visual implements IVisual {
private target: HTMLElement;
private host: IVisualHost;
private selectionManager: ISelectionManager;
// ...
constructor(options: VisualConstructorOptions) {
this.host = options.host;
// ...
this.selectionManager = this.host.createSelectionManager();
}
// ...
}
When the selection manager instance is created, you need to create selections
for each data point of the visual. The visual host object's createSelectionIdBuilder
method generates a selection for each data point. This method returns an instance of the object with interface powerbi.visuals.ISelectionIdBuilder
:
export interface ISelectionIdBuilder {
withCategory(categoryColumn: DataViewCategoryColumn, index: number): this;
withSeries(seriesColumn: DataViewValueColumns, valueColumn: DataViewValueColumn | DataViewValueColumnGroup): this;
withMeasure(measureId: string): this;
withMatrixNode(matrixNode: DataViewMatrixNode, levels: DataViewHierarchyLevel[]): this;
withTable(table: DataViewTable, rowIndex: number): this;
createSelectionId(): ISelectionId;
}
This object has corresponding methods to create selections
for different types of data view mappings.
Note
The methods withTable
and withMatrixNode
were introduced on API 2.5.0 of the Power BI visuals.
If you need to use selections for table or matrix data view mappings, update to API version 2.5.0 or higher.
Let's review how selections represent categorical data view mapping for a sample semantic model:
Manufacturer | Type | Value |
---|---|---|
Chrysler | Domestic Car | 28883 |
Chrysler | Domestic Truck | 117131 |
Chrysler | Import Car | 0 |
Chrysler | Import Truck | 6362 |
Ford | Domestic Car | 50032 |
Ford | Domestic Truck | 122446 |
Ford | Import Car | 0 |
Ford | Import Truck | 0 |
GM | Domestic Car | 65426 |
GM | Domestic Truck | 138122 |
GM | Import Car | 197 |
GM | Import Truck | 0 |
Honda | Domestic Car | 51450 |
Honda | Domestic Truck | 46115 |
Honda | Import Car | 2932 |
Honda | Import Truck | 0 |
Nissan | Domestic Car | 51476 |
Nissan | Domestic Truck | 47343 |
Nissan | Import Car | 5485 |
Nissan | Import Truck | 1430 |
Toyota | Domestic Car | 55643 |
Toyota | Domestic Truck | 61227 |
Toyota | Import Car | 20799 |
Toyota | Import Truck | 23614 |
The visual uses the following data view mapping:
{
"dataRoles": [
{
"displayName": "Columns",
"name": "columns",
"kind": "Grouping"
},
{
"displayName": "Rows",
"name": "rows",
"kind": "Grouping"
},
{
"displayName": "Values",
"name": "values",
"kind": "Measure"
}
],
"dataViewMappings": [
{
"categorical": {
"categories": {
"for": {
"in": "columns"
}
},
"values": {
"group": {
"by": "rows",
"select": [
{
"for": {
"in": "values"
}
}
]
}
}
}
}
]
}
In the preceding example, Manufacturer
is columns
and Type
is rows
. A series is created by grouping values by rows
(Type
).
The visual should be able to slice data by Manufacturer
or Type
.
For example, if a user selects Chrysler
by Manufacturer
, other visuals should show the following data:
Manufacturer | Type | Value |
---|---|---|
Chrysler | Domestic Car | 28883 |
Chrysler | Domestic Truck | 117131 |
Chrysler | Import Car | 0 |
Chrysler | Import Truck | 6362 |
When the user selects Import Car
by Type
(selects data by series), the other visuals should show the following data:
Manufacturer | Type | Value |
---|---|---|
Chrysler | Import Car | 0 |
Ford | Import Car | 0 |
GM | Import Car | 197 |
Honda | Import Car | 2932 |
Nissan | Import Car | 5485 |
Toyota | Import Car | 20799 |
To display sliced data, fill the visual's data baskets as follows:
In the preceding example, Manufacturer
is category (columns), Type
is series (rows), and Sales
is Values
for series.
Note
Values
are required for displaying a series because, according to the data view mapping, Values
are grouped by Rows
data.
// categories
const categories = dataView.categorical.categories;
// create label for 'Manufacturer' column
const p = document.createElement("p") as HTMLParagraphElement;
p.innerText = categories[0].source.displayName.toString();
this.target.appendChild(p);
// get count of category elements
const categoriesCount = categories[0].values.length;
// iterate all categories to generate selection and create button elements to use selections
for (let categoryIndex = 0; categoryIndex < categoriesCount; categoryIndex++) {
const categoryValue: powerbi.PrimitiveValue = categories[0].values[categoryIndex];
const categorySelectionId = this.host.createSelectionIdBuilder()
.withCategory(categories[0], categoryIndex) // we have only one category (only one `Manufacturer` column)
.createSelectionId();
this.dataPoints.push({
value: categoryValue,
selection: categorySelectionId
});
console.log(categorySelectionId);
// create button element to apply selection on click
const button = document.createElement("button") as HTMLButtonElement;
button.value = categoryValue.toString();
button.innerText = categoryValue.toString();
button.addEventListener("click", () => {
// handle click event to apply correspond selection
this.selectionManager.select(categorySelectionId);
});
this.target.appendChild(button);
}
In the preceding sample code, we iterate through all categories. In each iteration, we call createSelectionIdBuilder
to create the next selection for each category by calling the withCategory
method of the selection builder. The createSelectionId
method is used as a final method to return the generated selection
object.
In the withCategory
method, we pass the column of category
, in the sample, its Manufacturer
, and the index of category element.
// get groupped values for series
const series: powerbi.DataViewValueColumnGroup[] = dataView.categorical.values.grouped();
// create label for 'Type' column
const p2 = document.createElement("p") as HTMLParagraphElement;
p2.innerText = dataView.categorical.values.source.displayName;
this.target.appendChild(p2);
// iterate all series to generate selection and create button elements to use selections
series.forEach( (ser: powerbi.DataViewValueColumnGroup) => {
// create selection id for series
const seriesSelectionId = this.host.createSelectionIdBuilder()
.withSeries(dataView.categorical.values, ser)
.createSelectionId();
this.dataPoints.push({
value: ser.name,
selection: seriesSelectionId
});
// create button element to apply selection on click
const button = document.createElement("button") as HTMLButtonElement;
button.value =ser.name.toString();
button.innerText = ser.name.toString();
button.addEventListener("click", () => {
// handle click event to apply correspond selection
this.selectionManager.select(seriesSelectionId);
});
this.target.appendChild(button);
});
The following example shows table data view mapping:
{
"dataRoles": [
{
"displayName": "Values",
"name": "values",
"kind": "GroupingOrMeasure"
}
],
"dataViewMappings": [
{
"table": {
"rows": {
"for": {
"in": "values"
}
}
}
}
]
}
To create a selection for each row of table data view mapping, call the withTable
method of selection builder.
public update(options: VisualUpdateOptions) {
const dataView = options.dataViews[0];
dataView.table.rows.forEach((row: DataViewTableRow, rowIndex: number) => {
this.target.appendChild(rowDiv);
const selection: ISelectionId = this.host.createSelectionIdBuilder()
.withTable(dataView.table, rowIndex)
.createSelectionId();
}
}
The visual code iterates the rows of the table and each row calls the withTable
table method. Parameters of the withTable
method are the table
object and the index of the table row.
public update(options: VisualUpdateOptions) {
const host = this.host;
const rowLevels: powerbi.DataViewHierarchyLevel[] = dataView.matrix.rows.levels;
const columnLevels: powerbi.DataViewHierarchyLevel[] = dataView.matrix.rows.levels;
// iterate rows hierarchy
nodeWalker(dataView.matrix.rows.root, rowLevels);
// iterate columns hierarchy
nodeWalker(dataView.matrix.columns.root, columnLevels);
function nodeWalker(node: powerbi.DataViewMatrixNode, levels: powerbi.DataViewHierarchyLevel[]) {
const nodeSelection = host.createSelectionIdBuilder().withMatrixNode(node, levels);
if (node.children && node.children.length) {
node.children.forEach(child => {
nodeWalker(child, levels);
});
}
}
}
In the sample, nodeWalker
recursively calls each node and child node.
nodeWalker
creates a nodeSelection
object on each call. Each nodeSelection
represents a selection
of corresponding nodes.
In this example, we created a click handler for button elements. The handler calls the select
method of the selection manager and passes the selection object.
button.addEventListener("click", () => {
// handle click event to apply correspond selection
this.selectionManager.select(categorySelectionId);
});
The interface of the select
method:
interface ISelectionManager {
// ...
select(selectionId: ISelectionId | ISelectionId[], multiSelect?: boolean): IPromise<ISelectionId[]>;
// ...
}
The select
method can accept an array of selections. This allows your visual to have several data points selected at once. The second parameter, multiSelect
, is responsible for multi-selections. If multiSelect
is true, Power BI doesn't clear the previous selection state when it applies the current selection. If the value is false, the previous selection is overwritten.
A typical example of using multiSelect
is handling the Ctrl button state on a click event. When the Ctrl button is held down, you can select more than one object.
button.addEventListener("click", (mouseEvent) => {
const multiSelect = (mouseEvent as MouseEvent).ctrlKey;
this.selectionManager.select(seriesSelectionId, multiSelect);
});
Events
Power BI DataViz World Championships
14 Feb, 16 - 31 Mar, 16
With 4 chances to enter, you could win a conference package and make it to the LIVE Grand Finale in Las Vegas
Learn moreTraining
Learning path
Use advance techniques in canvas apps to perform custom updates and optimization - Training
Use advance techniques in canvas apps to perform custom updates and optimization
Certification
Microsoft Certified: Power BI Data Analyst Associate - Certifications
Demonstrate methods and best practices that align with business and technical requirements for modeling, visualizing, and analyzing data with Microsoft Power BI.
Documentation
Visual interactions in Power BI visuals - Power BI
This article discusses how to check whether Power BI visuals allows users to interact with a visual.
Apply selection to multiple visuals feature in Power BI - Power BI
This article describes how to apply a selection to multiple visuals by using the support multiple visual selection feature in Power BI.
Create custom Power BI visuals without data binding - Power BI
Learn how to create custom visuals for Power BI without data roles by using the No data binding feature.