Hervorheben von Datenpunkten in Power BI-Visuals
In diesem Artikel wird beschrieben, wie Daten in Power BI-Visuals hervorgehoben werden.
Wenn ein Element ausgewählt ist, wird das values
Array im dataView
Objekt standardmäßig so gefiltert, dass nur die ausgewählten Werte angezeigt werden. Wenn das values
Array gefiltert wird, zeigen alle anderen Visuals auf der Seite nur die ausgewählten Daten an.
Wenn Sie die supportsHighlight
-Eigenschaft in der capabilities.json
-Datei auf true
festlegen, erhalten Sie ein gänzlich ungefiltertes values
-Array zusammen mit einem highlights
-Array. Das highlights
-Array hat die gleiche Länge wie das Wertarray, und alle nicht ausgewählten Werte werden auf null
festgelegt. Ist diese Eigenschaft aktiviert, werden die entsprechenden Daten im Visual hervorgehoben, indem das values
-Array mit dem highlights
-Array verglichen wird.
Beachten Sie im Beispiel:
- Ohne Unterstützung von Hervorhebungen ist die Auswahl der einzige Wert im
values
-Array, und es wird nur ein Balken in der Datenansicht angezeigt. - Mit Unterstützung der Hervorhebung befinden sich alle Werte im
values
-Array. Dashighlights
-Array enthält für nicht hervorgehobene Elemente den Wertnull
. In der Datenansicht werden alle Balken angezeigt, der hervorgehobene Balken in einer anderen Farbe.
Auch die Auswahl mehrerer Elemente ist möglich sowie eine partielle Hervorhebung. Die hervorgehobenen Werte werden in der Datenansicht angezeigt.
Hinweis
Die Zuordnung von Tabellendaten in der Ansicht unterstützt das Feature „Highlights“ nicht.
Hervorheben von Datenpunkten mit Kategoriezuordnung in der Datenansicht
Fügen Sie für Visuals mit kategorischer Zuordnung der Datenansicht der Datei "supportsHighlight": true
zum capabilities.json
hinzu. Zum Beispiel:
{
"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
}
Nachdem Sie unnötigen Code entfernt haben, sieht der Standardquellcode für das Visual wie im folgenden Beispiel aus:
"use strict";
// ... default imports list
import { FormattingSettingsService } from "powerbi-visuals-utils-formattingmodel";
import DataViewCategorical = powerbi.DataViewCategorical;
import DataViewCategoryColumn = powerbi.DataViewCategoryColumn;
import PrimitiveValue = powerbi.PrimitiveValue;
import DataViewValueColumn = powerbi.DataViewValueColumn;
import { VisualFormattingSettingsModel } from "./settings";
export class Visual implements IVisual {
private target: HTMLElement;
private formattingSettings: VisualFormattingSettingsModel;
private formattingSettingsService: FormattingSettingsService;
constructor(options: VisualConstructorOptions) {
console.log('Visual constructor', options);
this.formattingSettingsService = new FormattingSettingsService();
this.target = options.element;
this.host = options.host;
}
public update(options: VisualUpdateOptions) {
this.formattingSettings = this.formattingSettingsService.populateFormattingSettingsModel(VisualFormattingSettingsModel, options.dataViews);
console.log('Visual update', options);
}
// Returns properties pane formatting model content hierarchies, properties and latest formatting values, Then populate properties pane.
// This method is called once every time we open properties pane or when the user edit any format property.
public getFormattingModel(): powerbi.visuals.FormattingModel {
return this.formattingSettingsService.buildFormattingModel(this.formattingSettings);
}
}
Für den Import erforderliche Schnittstellen zum Verarbeiten von Daten aus Power BI:
import DataViewCategorical = powerbi.DataViewCategorical;
import DataViewCategoryColumn = powerbi.DataViewCategoryColumn;
import PrimitiveValue = powerbi.PrimitiveValue;
import DataViewValueColumn = powerbi.DataViewValueColumn;
Erstellen des div
-Stammelements für Kategoriewerte:
export class Visual implements IVisual {
private target: HTMLElement;
private formattingSettings: VisualFormattingSettingsModel;
private formattingSettingsService: FormattingSettingsService;
private div: HTMLDivElement; // new property
constructor(options: VisualConstructorOptions) {
console.log('Visual constructor', options);
this.formattingSettingsService = new FormattingSettingsService();
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);
}
// ...
}
Löschen des Inhalts von div-Elementen vor dem Rendern neuer Daten:
// ...
public update(options: VisualUpdateOptions) {
this.formattingSettings = this.formattingSettingsService.populateFormattingSettingsModel(VisualFormattingSettingsModel, options.dataViews);
console.log('Visual update', options);
while (this.div.firstChild) {
this.div.removeChild(this.div.firstChild);
}
// ...
}
Abrufen von Kategorien und Measurewerten aus dem dataView
-Objekt:
public update(options: VisualUpdateOptions) {
this.formattingSettings = this.formattingSettingsService.populateFormattingSettingsModel(VisualFormattingSettingsModel, options.dataViews);
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;
// ...
}
Hierbei sind categoryValues
ein Array von Kategoriewerten, measureValues
ein Array von Measures und measureHighlights
die hervorgehobenen Teile von Werten.
Hinweis
Wenn die Werte der measureHighlights
-Eigenschaft kleiner als die Werte der categoryValues
-Eigenschaft sind, wurde der Wert partiell hervorgehoben.
Aufzählen des categoryValues
-Arrays und Abrufen der entsprechenden Werte und Hervorhebungen:
// ...
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);
});
Erstellen der Elemente div
und p
, um die Werte der Datenansicht im Visual-DOM darzustellen:
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);
});
Anwenden der erforderlichen Formatvorlagen für Elemente, die flexbox
verwenden sollen, und Definieren der Farben für div-Elemente:
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;
}
Die resultierende Ansicht des Visuals sieht folgendermaßen aus:
Hervorheben von Datenpunkten mit Zuordnung der Matrixdaten in der Ansicht
Fügen Sie für Visuals mit Zuordnung der Matrixdatenansicht der Datei "supportsHighlight": true
zumcapabilities.json
hinzu. Zum Beispiel:
{
"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
}
Die Beispieldaten zum Erstellen einer Hierarchie für die Zuordnung der Matrixdaten in der Ansicht:
Zeile1 | Zeile2 | Zeile3 | Column1 | Column2 | Column3 | Werte |
---|---|---|---|---|---|---|
F1 | R11 | R111 | C1 | C11 | C111 | 1 |
F1 | R11 | R112 | C1 | C11 | C112 | 2 |
F1 | R11 | R113 | C1 | C11 | C113 | 3 |
F1 | R12 | R121 | C1 | C12 | C121 | 4 |
F1 | R12 | R122 | C1 | C12 | C122 | 5 |
F1 | R12 | R123 | C1 | C12 | C123 | 6 |
F1 | R13 | R131 | C1 | C13 | C131 | 7 |
F1 | R13 | R132 | C1 | C13 | C132 | 8 |
F1 | R13 | R133 | C1 | C13 | C133 | 9 |
F2 | R21 | R211 | C2 | C21 | C211 | 10 |
F2 | R21 | R212 | C2 | C21 | C212 | 11 |
F2 | R21 | R213 | C2 | C21 | C213 | 12 |
F2 | R22 | R221 | C2 | C22 | C221 | 13 |
F2 | R22 | R222 | C2 | C22 | C222 | 14 |
F2 | R22 | R223 | C2 | C22 | C223 | 16 |
F2 | R23 | R231 | C2 | C23 | C231 | 17 |
F2 | R23 | R232 | C2 | C23 | C232 | 18 |
F2 | R23 | R233 | C2 | C23 | C233 | 19 |
Erstellen Sie das Visual-Standardprojekt, und übernehmen Sie das Beispiel der capabilities.json
-Datei.
Nachdem Sie unnötigen Code entfernt haben, sieht der Standardquellcode für das Visual wie im folgenden Beispiel aus:
"use strict";
// ... default imports
import { FormattingSettingsService } from "powerbi-visuals-utils-formattingmodel";
import { VisualFormattingSettingsModel } from "./settings";
export class Visual implements IVisual {
private target: HTMLElement;
private formattingSettings: VisualFormattingSettingsModel;
private formattingSettingsService: FormattingSettingsService;
constructor(options: VisualConstructorOptions) {
console.log('Visual constructor', options);
this.formattingSettingsService = new FormattingSettingsService();
this.target = options.element;
this.host = options.host;
}
public update(options: VisualUpdateOptions) {
this.formattingSettings = this.formattingSettingsService.populateFormattingSettingsModel(VisualFormattingSettingsModel, options.dataViews);
console.log('Visual update', options);
}
/**
* Returns properties pane formatting model content hierarchies, properties and latest formatting values, Then populate properties pane.
* This method is called once every time we open properties pane or when the user edit any format property.
*/
public getFormattingModel(): powerbi.visuals.FormattingModel {
return this.formattingSettingsService.buildFormattingModel(this.formattingSettings);
}
}
Importieren der erforderlichen Schnittstellen zum Verarbeiten von Daten aus Power BI:
import DataViewMatrix = powerbi.DataViewMatrix;
import DataViewMatrixNode = powerbi.DataViewMatrixNode;
import DataViewHierarchyLevel = powerbi.DataViewHierarchyLevel;
Erstellen von zwei div
-Elementen für das Visual-Layout:
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";
}
Überprüfen Sie die Daten in der update
-Methode, um sicherzustellen, dass das Visual Daten erhält:
public update(options: VisualUpdateOptions) {
this.formattingSettings = this.formattingSettingsService.populateFormattingSettingsModel(VisualFormattingSettingsModel, options.dataViews);
console.log('Visual update', options);
const dataView: DataView = options.dataViews[0];
const matrixDataView: DataViewMatrix = dataView.matrix;
if (!matrixDataView ||
!matrixDataView.columns ||
!matrixDataView.rows ) {
return
}
// ...
}
Löschen des Inhalts von div
-Elementen vor dem Rendern neuer Daten:
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);
// ...
}
Erstellen der treeWalker
-Funktion zum Durchlaufen der Matrixdatenstruktur:
public update(options: VisualUpdateOptions) {
// ...
const treeWalker = (matrixNode: DataViewMatrixNode, index: number, levels: DataViewHierarchyLevel[], div: HTMLDivElement) => {
}
// ...
}
Dabei ist matrixNode
der aktuelle Knoten, levels
stellt Metadatenspalten der aktuellen Hierarchieebene dar, div
ist das übergeordnete Element für untergeordnete HTML-Elemente.
Die treeWalker
-Funktion ist rekursiv, es müssen div
- und p
-Elemente für den Text als Überschrift erstellt und die Funktion für die untergeordneten Elemente des Knotens aufgerufen werden:
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));
}
}
// ...
}
Aufrufen der Funktion für die Stammelemente von Spalte und Zeile der Matrixstruktur der Datenansicht auf:
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));
}
Generieren Sie die selectionID für Knoten und die Erstellen-Schaltflächen zum Anzeigen von Knoten:
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();
});
// ...
}
// ...
}
Der Hauptschritt für Hervorhebungen besteht im Erstellen eines weiteren Wertearrays.
Das Objekt des letzten Knotens verfügt über zwei Eigenschaften für das Wertearray, „value“ und „highlight“:
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
}
}
}
Dabei stellt value
den Wert des Knotens dar, ohne die Auswahl des anderen Visuals anzuwenden, und highlight
gibt an, welcher Teil der Daten hervorgehoben wurde.
Hinweis
Ist der Wert von highlight
kleiner als der Wert von value
, wurde value
partiell hervorgehoben.
Fügen Sie Code zum Verarbeiten des values
-Arrays des Knotens hinzu, wenn er dargestellt wird:
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) {
// ...
}
}
// ...
}
Das Ergebnis ist ein Visual mit Schaltflächen und Werten, z. B. highlighted value/default value
.