Incorporación de interactividad en un objeto visual mediante las selecciones de objetos visuales de Power BI

Power BI proporciona dos maneras de interactuar con objetos visuales: selección y filtrado. En el ejemplo siguiente, se muestra cómo seleccionar un elemento de un objeto visual y notificar a los demás objetos visuales del informe sobre el estado de selección nuevo.

La interfaz corresponde a un objeto Selection:

export interface ISelectionId {
    equals(other: ISelectionId): boolean;
    includes(other: ISelectionId, ignoreHighlight?: boolean): boolean;
    getKey(): string;
    getSelector(): Selector;
    getSelectorsByColumn(): SelectorsByColumn;
    hasIdentity(): boolean;
}

Uso del administrador de selección para seleccionar puntos de datos

El host del objeto visual proporciona un método para crear una instancia del administrador de selección. El administrador de selección tiene un método correspondiente para cada una de las acciones siguientes:

  • Seleccionar
  • Borrar la selección
  • Mostrar el menú contextual
  • Almacenar las selecciones actuales
  • Comprobar el estado de la selección

Creación de una instancia del administrador de selección

Para usar el administrador de selección, cree la instancia de un administrador de selección. Por lo general, los objetos visuales crean una instancia del administrador de selección en la sección constructor del objeto visual.

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();
    }
    // ...
}

Creación de una instancia del generador de selección

Cuando se crea la instancia del administrador de selección, debe crear selections para cada punto de datos del objeto visual. El método createSelectionIdBuilder del objeto del host del objeto visual genera una selección para cada punto de datos. Este método devuelve una instancia del objeto con la interfaz 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;
}

Este objeto tiene métodos correspondientes para crear selections para tipos diferentes de asignaciones de vistas de datos.

Nota

Los métodos withTable y withMatrixNode se introdujeron en la API 2.5.0 de los objetos visuales de Power BI. Si necesita usar selecciones para las asignaciones de vistas de datos de tabla o matriz, actualice la API a la versión 2.5.0 o superior.

Creación de selecciones para la asignación de vistas de datos de categóricos

Revisemos cómo las selecciones representan el mapeo de vistas de datos categóricos para un modelo semántico de ejemplo:

Fabricante Tipo Valor
Chrysler Automóvil nacional 28883
Chrysler Camión nacional 117131
Chrysler Automóvil importado 0
Chrysler Camión importado 6362
Ford Automóvil nacional 50032
Ford Camión nacional 122446
Ford Automóvil importado 0
Ford Camión importado 0
GM Automóvil nacional 65426
GM Camión nacional 138122
GM Automóvil importado 197
GM Camión importado 0
Honda Automóvil nacional 51450
Honda Camión nacional 46115
Honda Automóvil importado 2932
Honda Camión importado 0
Nissan Automóvil nacional 51476
Nissan Camión nacional 47343
Nissan Automóvil importado 5485
Nissan Camión importado 1430
Toyota Automóvil nacional 55643
Toyota Camión nacional 61227
Toyota Automóvil importado 20799
Toyota Camión importado 23614

El objeto visual utiliza la asignación de vistas de datos siguientes:

{
    "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"
                                }
                            }
                        ]
                    }
                }
            }
        }
    ]
}

En el ejemplo anterior, Manufacturer es columns y Type es rows. Se crea una serie al agrupar valores por rows (Type).

El objeto visual debe poder segmentar los datos por Manufacturer o Type.

Por ejemplo, si un usuario selecciona Chrysler por Manufacturer, otros objetos visuales deberían mostrar los datos siguientes:

Fabricante Tipo Valor
Chrysler Automóvil nacional 28883
Chrysler Camión nacional 117131
Chrysler Automóvil importado 0
Chrysler Camión importado 6362

Cuando el usuario selecciona Import Car por Type (selecciona datos por serie), los otros objetos visuales deberían mostrar estos datos:

Fabricante Tipo Valor
Chrysler Automóvil importado 0
Ford Automóvil importado 0
GM Automóvil importado 197
Honda Automóvil importado 2932
Nissan Automóvil importado 5485
Toyota Automóvil importado 20799

Screenshot that shows the visual with selections.

Para mostrar datos segmentados, rellene los cubos de datos del objeto visual como se indica a continuación:

Screenshot that shows visual's data baskets.

En el ejemplo anterior, Manufacturer es la categoría (columnas), Type es la serie (filas) y Sales es el valor (Values) para la serie.

Nota

Se requieren Values para mostrar una serie porque, según la asignación de vistas de datos, Values se agrupan por los datos de Rows.

Creación de selecciones para categorías

// 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);
}

En el código de ejemplo anterior, recorrimos en iteración todas las categorías. En cada iteración llamamos a createSelectionIdBuilder para crear la selección siguiente para cada categoría llamando al método withCategory del generador de selección. El método createSelectionId se usa como método final para devolver el objeto selection generado.

En el método withCategory, pasamos la columna de category; en el ejemplo es Manufacturer y el índice del elemento de categoría.

Creación de selecciones para la serie

// 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);
});

Creación de selecciones para asignación de vistas de datos de tabla

En el ejemplo siguiente se muestra la asignación de vistas de datos de tabla:

{
    "dataRoles": [
        {
            "displayName": "Values",
            "name": "values",
            "kind": "GroupingOrMeasure"
        }
    ],
    "dataViewMappings": [
        {
            "table": {
                "rows": {
                    "for": {
                        "in": "values"
                    }
                }
            }
        }
    ]
}

Para crear una selección para cada fila de la asignación de vistas de datos de tabla, llame al método withTable del generador de selección.

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();
    }
}

El código del objeto visual recorre en iteración las filas de la tabla y cada fila llama al método withTable de tabla. Los parámetros del método withTable son el objeto table y el índice de la fila de la tabla.

Creación de selecciones para asignación de vistas de datos de matriz

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);
            });
        }
    }
}

En el ejemplo, nodeWalker llama de manera recursiva a cada nodo y a los nodos secundarios.

nodeWalker crea un objeto nodeSelection en cada llamada. Cada nodeSelection representa una selection de los nodos correspondientes.

Selección de puntos de datos para segmentar otros objetos visuales

En este ejemplo, creamos un controlador de clics para los elementos de botón. El controlador llama al método select del administrador de selección y pasa el objeto de selección.

button.addEventListener("click", () => {
    // handle click event to apply correspond selection
    this.selectionManager.select(categorySelectionId);
});

La interfaz del método select es:

interface ISelectionManager {
    // ...
    select(selectionId: ISelectionId | ISelectionId[], multiSelect?: boolean): IPromise<ISelectionId[]>;
    // ...
}

El método select puede aceptar una matriz de selecciones. Esto permite que el objeto visual tenga varios puntos de datos seleccionados a la vez. El segundo parámetro, multiSelect, es responsable de las selecciones múltiples. Si el valor de multiSelect es verdadero, Power BI no borra el estado de la selección anterior cuando aplica la selección actual. Si el valor es falso, se sobrescribe la selección anterior.

Un ejemplo típico del uso de multiSelect es controlar el estado del botón Ctrl mediante un evento de clic. Si mantiene presionado el botón Ctrl, puede seleccionar más de un objeto.

button.addEventListener("click", (mouseEvent) => {
    const multiSelect = (mouseEvent as MouseEvent).ctrlKey;
    this.selectionManager.select(seriesSelectionId, multiSelect);
});