Zvýraznenie údajových bodov vo vizuáloch služby Power BI

V tomto článku sa popisuje, ako zvýrazniť údaje vo vizuáloch služby Power BI.

Predvolene, vždy, keď je vybratý prvok, values pole v dataView objekte sa filtruje len na vybraté hodnoty. Toto filtrovanie spôsobí, že všetky ostatné vizuály na stránke zobrazia len vybraté údaje.

Ak nastavíte vlastnosť supportsHighlight v súbore capabilities.json na true, dostanete celé nefiltrované pole values spolu s poľom´highlights. Pole highlights bude mať rovnakú dĺžku ako pole hodnoty a všetky hodnoty, ktoré nie sú vybraté, budú nastavené na null. Po zapnutej vlastnosti vizuál zvýrazní príslušné údaje porovnaním values poľa s highlights poľom.

V príklade si všimnete:

  • Bez podpory zvýraznenia je výber jedinou hodnotou v values poli a jediným pruhom prezentovaným v zobrazení údajov.
  • S podporou zvýraznenia sú všetky hodnoty v values poli. highlightsPole obsahuje null hodnotu pre nezvýrazňované prvky. Všetky pruhy sa zobrazia v zobrazení údajov so zvýrazneným pruhom inej farby.

K dispozícii môže byť aj niekoľko výberov a čiastočných zvýraznení. Zvýraznené hodnoty sa zobrazia v zobrazení údajov.

Poznámka

Priradenie zobrazenia tabuľkových údajov nepodporuje funkciu zvýraznení.

Zvýraznenie údajových bodov s priradením zobrazenia kategorických údajov

V prípade vizuálov s kategorickým mapovaním zobrazenia údajovpridajte "supportsHighlight": true do capabilities.json súboru. Príklad:

{
    "dataRoles": [
        {
            "displayName": "Category",
            "name": "category",
            "kind": "Grouping"
        },
        {
            "displayName": "Value",
            "name": "value",
            "kind": "Measure"
        }
    ],
    "dataViewMappings": [
        {
            "categorical": {
                "categories": {
                    "for": {
                        "in": "category"
                    }
                },
                "values": {
                    "for": {
                        "in": "value"
                    }
                }
            }
        }
    ],
    "supportsHighlight": true
}

Zdrojový kód predvoleného vizuálu po odstránení nepotrebného kódu bude vyzerať takto:

"use strict";

// ... default imports list

import DataViewCategorical = powerbi.DataViewCategorical;
import DataViewCategoryColumn = powerbi.DataViewCategoryColumn;
import PrimitiveValue = powerbi.PrimitiveValue;
import DataViewValueColumn = powerbi.DataViewValueColumn;

import { VisualSettings } from "./settings";

export class Visual implements IVisual {
    private target: HTMLElement;
    private settings: VisualSettings;

    constructor(options: VisualConstructorOptions) {
        console.log('Visual constructor', options);
        this.target = options.element;
        this.host = options.host;

    }

    public update(options: VisualUpdateOptions) {
        this.settings = Visual.parseSettings(options && options.dataViews && options.dataViews[0]);
        console.log('Visual update', options);

    }

    private static parseSettings(dataView: DataView): VisualSettings {
        return <VisualSettings>VisualSettings.parse(dataView);
    }

    /**
     * This function gets called for each of the objects defined in the capabilities files and allows you to select which of the
     * objects and properties you want to expose to the users in the property pane.
     *
     */
    public enumerateObjectInstances(options: EnumerateVisualObjectInstancesOptions): VisualObjectInstance[] | VisualObjectInstanceEnumerationObject {
        return VisualSettings.enumerateObjectInstances(this.settings || VisualSettings.getDefault(), options);
    }
}

Importujte požadované rozhrania na spracovanie údajov zo služby Power BI:

import DataViewCategorical = powerbi.DataViewCategorical;
import DataViewCategoryColumn = powerbi.DataViewCategoryColumn;
import PrimitiveValue = powerbi.PrimitiveValue;
import DataViewValueColumn = powerbi.DataViewValueColumn;

Vytvorte koreňový prvok div pre hodnoty kategórií:

export class Visual implements IVisual {
    private target: HTMLElement;
    private settings: VisualSettings;

    private div: HTMLDivElement; // new property

    constructor(options: VisualConstructorOptions) {
        console.log('Visual constructor', options);
        this.target = options.element;
        this.host = options.host;

        // create div element
        this.div = document.createElement("div");
        this.div.classList.add("vertical");
        this.target.appendChild(this.div);

    }
    // ...
}

Pred vykreslením nových údajov vymažte obsah prvkov div:

// ...
public update(options: VisualUpdateOptions) {
    this.settings = Visual.parseSettings(options && options.dataViews && options.dataViews[0]);
    console.log('Visual update', options);

    while (this.div.firstChild) {
        this.div.removeChild(this.div.firstChild);
    }
    // ...
}

Načítajte kategórie a hodnoty miery z objektu dataView:

public update(options: VisualUpdateOptions) {
    this.settings = Visual.parseSettings(options && options.dataViews && options.dataViews[0]);
    console.log('Visual update', options);

    while (this.div.firstChild) {
        this.div.removeChild(this.div.firstChild);
    }

    const dataView: DataView = options.dataViews[0];
    const categoricalDataView: DataViewCategorical = dataView.categorical;
    const categories: DataViewCategoryColumn = categoricalDataView.categories[0];
    const categoryValues = categories.values;

    const measures: DataViewValueColumn = categoricalDataView.values[0];
    const measureValues = measures.values;
    const measureHighlights = measures.highlights;
    // ...
}

Kde categoryValues je pole hodnôt kategórií, measureValues je pole miery a measureHighlights sú zvýraznené časti hodnôt.

Poznámka

Hodnoty vlastnosti measureHighlights môžu byť menšie ako hodnoty vlastnosti categoryValues. Znamená to, že hodnota bola zvýraznená čiastočne.

Vyčíslite pole categoryValues a načítajte zodpovedajúce hodnoty a zvýraznenia:

// ...
const measureHighlights = measures.highlights;

categoryValues.forEach((category: PrimitiveValue, index: number) => {
    const measureValue = measureValues[index];
    const measureHighlight = measureHighlights && measureHighlights[index] ? measureHighlights[index] : null;
    console.log(category, measureValue, measureHighlight);

});

Vytvorte prvky div a p na zobrazenie a vizualizáciu hodnôt zobrazenia údajov v platforme DOM vizuálu:

categoryValues.forEach((category: PrimitiveValue, index: number) => {
    const measureValue = measureValues[index];
    const measureHighlight = measureHighlights && measureHighlights[index] ? measureHighlights[index] : null;
    console.log(category, measureValue, measureHighlight);

    // div element. it contains elements to display values and visualize value as progress bar
    let div = document.createElement("div");
    div.classList.add("horizontal");
    this.div.appendChild(div);

    // div element to visualize value of measure
    let barValue = document.createElement("div");
    barValue.style.width = +measureValue * 10 + "px";
    barValue.style.display = "flex";
    barValue.classList.add("value");

    // element to display category value
    let bp = document.createElement("p");
    bp.innerText = category.toString();

    // div element to visualize highlight of measure
    let barHighlight = document.createElement("div");
    barHighlight.classList.add("highlight")
    barHighlight.style.backgroundColor = "blue";
    barHighlight.style.width = +measureHighlight * 10 + "px";

    // element to display highlighted value of measure
    let p = document.createElement("p");
    p.innerText = `${measureHighlight}/${measureValue}`;
    barHighlight.appendChild(bp);

    div.appendChild(barValue);

    barValue.appendChild(barHighlight);
    div.appendChild(p);
});

Použite požadované štýly pre prvky tak, aby používali flexbox, a definujte farby pre prvky div:

div.vertical {
    display: flex;
    flex-direction: column;
}

div.horizontal {
    display: flex;
    flex-direction: row;
}

div.highlight {
    background-color: blue
}

div.value {
    background-color: red;
    display: flex;
}

Výsledkom by malo byť nasledujúce zobrazenie vizuálu.

Vizuály s priradením zobrazenia kategorických údajov a zvýraznením

Zvýraznenie údajových bodov s priradením zobrazenia maticových údajov

V prípade vizuálov s mapovaním maticového zobrazenia údajovpridajte "supportsHighlight": true do capabilities.json súboru. Príklad:

{
    "dataRoles": [
        {
            "displayName": "Columns",
            "name": "columns",
            "kind": "Grouping"
        },
        {
            "displayName": "Rows",
            "name": "rows",
            "kind": "Grouping"
        },
        {
            "displayName": "Value",
            "name": "value",
            "kind": "Measure"
        }
    ],
    "dataViewMappings": [
        {
            "matrix": {
                "columns": {
                    "for": {
                        "in": "columns"
                    }
                },
                "rows": {
                    "for": {
                        "in": "rows"
                    }
                },
                "values": {
                    "for": {
                        "in": "value"
                    }
                }
            }
        }
    ],
    "supportsHighlight": true
}

Vzorové údaje na vytvorenie hierarchie pre priradenie zobrazenia maticových údajov:

Riadok1 Riadok2 Riadok3 Stĺpec1 Stĺpec2 Stĺpec3 Hodnoty
R1 R11 R111 S1 S11 S111 1
R1 R11 R112 S1 S11 S112 2
R1 R11 R113 S1 S11 S113 3
R1 R12 R121 S1 S12 S121 4
R1 R12 R122 S1 S12 S122 5
R1 R12 R123 S1 S12 S123 6
R1 R13 R131 S1 S13 S131 7
R1 R13 R132 S1 S13 S132 8
R1 R13 R133 S1 S13 S133 9
R2 R21 R211 S2 S21 S211 10
R2 R21 R212 S2 S21 S212 11
R2 R21 R213 S2 S21 S213 12
R2 R22 R221 S2 S22 S221 13
R2 R22 R222 S2 S22 S222 14
R2 R22 R223 S2 S22 S223 16
R2 R23 R231 S2 S23 S231 17
R2 R23 R232 S2 S23 S232 18
R2 R23 R233 S2 S23 S233 19

Vytvorte projekt predvoleného vizuálu a použite ukážku vlastnosti capabilities.json.

Zdrojový kód predvoleného vizuálu po odstránení nepotrebného kódu bude vyzerať takto:

"use strict";

// ... default imports

import { VisualSettings } from "./settings";

export class Visual implements IVisual {
    private target: HTMLElement;
    private settings: VisualSettings;


    constructor(options: VisualConstructorOptions) {
        console.log('Visual constructor', options);
        this.target = options.element;
        this.host = options.host;
    }

    public update(options: VisualUpdateOptions) {
        this.settings = Visual.parseSettings(options && options.dataViews && options.dataViews[0]);
        console.log('Visual update', options);

    }

    private static parseSettings(dataView: DataView): VisualSettings {
        return <VisualSettings>VisualSettings.parse(dataView);
    }

    /**
     * This function gets called for each of the objects defined in the capabilities files and allows you to select which of the
     * objects and properties you want to expose to the users in the property pane.
     *
     */
    public enumerateObjectInstances(options: EnumerateVisualObjectInstancesOptions): VisualObjectInstance[] | VisualObjectInstanceEnumerationObject {
        return VisualSettings.enumerateObjectInstances(this.settings || VisualSettings.getDefault(), options);
    }
}

Importujte požadované rozhrania na spracovanie údajov zo služby Power BI:

import DataViewMatrix = powerbi.DataViewMatrix;
import DataViewMatrixNode = powerbi.DataViewMatrixNode;
import DataViewHierarchyLevel = powerbi.DataViewHierarchyLevel;

Vytvorte dva prvky div pre rozloženie vizuálu:

constructor(options: VisualConstructorOptions) {
    // ...
    this.rowsDiv = document.createElement("div");
    this.target.appendChild(this.rowsDiv);

    this.colsDiv = document.createElement("div");
    this.target.appendChild(this.colsDiv);
    this.target.style.overflowY = "auto";
}

Skontrolujte údaje v metóde update a ubezpečte sa, že vizuál načítava údaje:

public update(options: VisualUpdateOptions) {
    this.settings = Visual.parseSettings(options && options.dataViews && options.dataViews[0]);
    console.log('Visual update', options);

    const dataView: DataView = options.dataViews[0];
    const matrixDataView: DataViewMatrix = dataView.matrix;

    if (!matrixDataView ||
        !matrixDataView.columns ||
        !matrixDataView.rows ) {
        return
    }
    // ...
}

Pred vykreslenia div nových údajov vymažte obsah prvkov:

public update(options: VisualUpdateOptions) {
    // ...

    // remove old elements
    // to better performance use D3js pattern:
    // https://d3js.org/#enter-exit
    while (this.rowsDiv.firstChild) {
        this.rowsDiv.removeChild(this.rowsDiv.firstChild);
    }
    const prow = document.createElement("p");
    prow.innerText = "Rows";
    this.rowsDiv.appendChild(prow);

    while (this.colsDiv.firstChild) {
        this.colsDiv.removeChild(this.colsDiv.firstChild);
    }
    const pcol = document.createElement("p");
    pcol.innerText = "Columns";
    this.colsDiv.appendChild(pcol);
    // ...
}

Vytvorte funkciu treeWalker na prechádzanie štruktúrou maticových údajov:

public update(options: VisualUpdateOptions) {
    // ...
    const treeWalker = (matrixNode: DataViewMatrixNode, index: number, levels: DataViewHierarchyLevel[], div: HTMLDivElement)  => {

    }
    // ...
}

Kde matrixNode je aktuálny uzol, levels sú stĺpce metaúdajov tejto úrovne hierarchie, div – nadradený prvok pre podriadené prvky HTML.

treeWalker je rekurzívna funkcia, je potrebné vytvoriť prvok div a p pre text ako hlavičku a vyvolať funkciu pre podriadené prvky uzla:

public update(options: VisualUpdateOptions) {
    // ...
    const treeWalker = (matrixNode: DataViewMatrixNode, index: number, levels: DataViewHierarchyLevel[], div: HTMLDivElement)  => {
        // ...

        if (matrixNode.children) {
            const childDiv = document.createElement("div");
            childDiv.classList.add("vertical");
            div.appendChild(childDiv);

            const p = document.createElement("p");
            const level = levels[matrixNode.level]; // get current level column metadata from current node
            p.innerText = level.sources[level.sources.length - 1].displayName; // get column name from metadata

            childDiv.appendChild(p); // add paragraph element to div element
            matrixNode.children.forEach((node, index) => treeWalker(node, levels, childDiv, ++levelIndex));
        }
    }
    // ...
}

Vyvolajte funkciu pre koreňové prvky stĺpca a riadka štruktúry zobrazenia maticových údajov:

public update(options: VisualUpdateOptions) {
    // ...
    const treeWalker = (matrixNode: DataViewMatrixNode, index: number, levels: DataViewHierarchyLevel[], div: HTMLDivElement)  => {
        // ...
    }
    // ...
    // remove old elements
    // ...

    // ...
    const rowRoot: DataViewMatrixNode = matrixDataView.rows.root;
    rowRoot.children.forEach((node) => treeWalker(node, matrixDataView.rows.levels, this.rowsDiv));

    const colRoot = matrixDataView.columns.root;
    colRoot.children.forEach((node) => treeWalker(node, matrixDataView.columns.levels, this.colsDiv));
}

Vygenerujte vlastnosť selectionID pre uzly a tlačidlá Vytvoriť na zobrazenie uzlov:

public update(options: VisualUpdateOptions) {
    // ...
    const treeWalker = (matrixNode: DataViewMatrixNode, index: number, levels: DataViewHierarchyLevel[], div: HTMLDivElement)  => {
        const selectionID: ISelectionID = this.host.createSelectionIdBuilder()
            .withMatrixNode(matrixNode, levels)
            .createSelectionId();

        let nodeBlock = document.createElement("button");
        nodeBlock.innerText = matrixNode.value.toString();

        nodeBlock.addEventListener("click", (event) => {
            // call select method in the selection manager
            this.selectionManager.select(selectionID);
        });

        nodeBlock.addEventListener("contextmenu", (event) => {
            // call showContextMenu method to display context menu on the visual
            this.selectionManager.showContextMenu(selectionID, {
                x: event.clientX,
                y: event.clientY
            });
            event.preventDefault();
        });
        // ...
    }
    // ...
}

Hlavným krokom pri používaní zvýraznenia je vytvorenie ďalšieho poľa hodnôt.

Keď skontrolujete objekt koncového uzla, môžete vidieť, že pole hodnôt má dve vlastnosti – hodnota a zvýraznenie:

JSON.stringify(options.dataViews[0].matrix.rows.root.children[0].children[0].children[0], null, " ");
{
 "level": 2,
 "levelValues": [
  {
   "value": "R233",
   "levelSourceIndex": 0
  }
 ],
 "value": "R233",
 "identity": {
  "identityIndex": 2
 },
 "values": {
  "0": {
   "value": null,
   "highlight": null
  },
  "1": {
   "value": 19,
   "highlight": 19
  }
 }
}

Kde value predstavuje hodnotu uzla bez použitia výberu z iného vizuálu a udáva časť highlight údajov, ktorá bola zvýraznená.

Poznámka

Ak je hodnota highlight hodnoty menšia ako hodnota , znamená value to, že value bolo zvýraznené čiastočne.

Pridajte kód na values spracovanie poľa uzla, ak je prezentovaný:

public update(options: VisualUpdateOptions) {
    // ...
    const treeWalker = (matrixNode: DataViewMatrixNode, index: number, levels: DataViewHierarchyLevel[], div: HTMLDivElement)  => {
        // ...

        if (matrixNode.values) {
            const sumOfValues = Object.keys(matrixNode.values) // get key property of object (value are 0 to N)
                .map(key => +matrixNode.values[key].value) // convert key property to number
                .reduce((prev, curr) => prev + curr) // sum of values

            let sumOfHighlights = sumOfValues;
            sumOfHighlights = Object.keys(matrixNode.values) // get key property of object (value are 0 to N)
                .map(key => matrixNode.values[key].highlight ? +matrixNode.values[key].highlight : null ) // convert key property to number if it exists
                .reduce((prev, curr) => curr ? prev + curr : null) // convert key property to number

            // create div container for value and highlighted value
            const vals = document.createElement("div");
            vals.classList.add("vertical")
            vals.classList.replace("vertical", "horizontal");
            // create paragraph element for label
            const highlighted = document.createElement("p");
            // Display complete value and highlighted value
            highlighted.innerText = `${sumOfHighlights}/${sumOfValues}`;

            // create div container for value
            const valueDiv = document.createElement("div");
            valueDiv.style.width = sumOfValues * 10 + "px";
            valueDiv.classList.add("value");

            // create div container for highlighted values
            const highlightsDiv = document.createElement("div");
            highlightsDiv.style.width = sumOfHighlights * 10 + "px";
            highlightsDiv.classList.add("highlight");
            valueDiv.appendChild(highlightsDiv);

            // append button and paragraph to div containers to parent div
            vals.appendChild(nodeBlock);
            vals.appendChild(valueDiv);
            vals.appendChild(highlighted);
            div.appendChild(vals);
        } else {
            div.appendChild(nodeBlock);
        }

        if (matrixNode.children) {
            // ...
        }
    }
    // ...
}

Ako výsledok získate vizuál s tlačidlami a hodnotami highlighted value/default value

Vizuál s priradením zobrazení maticových údajov a zvýraznením

Ďalšie kroky