Adatpontok kiemelése Power BI-vizualizációkbanHighlight data points in Power BI Visuals
Egy elem kijelölésekor a dataView
objektum values
tömbje alapértelmezés szerint csak a kijelölt értékekre lesz szűrve.By default whenever an element is selected the values
array in the dataView
object will be filtered to just the selected values. Emiatt az oldal többi vizualizációja csak a kijelölt adatokat jeleníti meg.It will cause all other visuals on the page to display just the selected data.
Ha capabilities.json
fájl supportsHighlight
tulajdonságát true
értékre állítja, a teljes szűretlen values
tömböt kapja eredményül egy highlights
tömbbel együtt.If you set the supportsHighlight
property in your capabilities.json
to true
, you'll receive the full unfiltered values
array along with a highlights
array. A highlights
tömb az értékek tömbjével megegyező hosszúságú lesz, a nem kijelölt értékek pedig null
lesznek beállítva.The highlights
array will be the same length as the values array and any non-selected values will be set to null
. A tulajdonság engedélyezésével a vizualizáció felelősségévé válik a megfelelő adatok kiemelése – values
a tömb highlights
a tömbbel való összehasonlításával.With this property enabled it's the visual's responsibility to highlight the appropriate data by comparing the values
array to the highlights
array.
A példában megfigyelheti, hogy 1 sáv ki van jelölve.In the example, you'll notice that 1 bar is selected. Ez az egyetlen érték a kiemelt tömbben.And it's the only value in the highlights array. Emellett fontos megjegyezni, hogy több kijelölés, valamint részleges kiemelés is szerepelhet.It's also important to note that there could be multiple selections and partial highlights. A kiemelt értékek az adatnézetben jelennek meg.The highlighted values will be presented in the data view.
Megjegyzés
A táblázatos adatnézet leképezése nem támogatja a kiemelés funkciót.Table data view mapping doesn't support the highlights feature.
Az adatpontok kiemelése kategorikus adatnézet-leképezésselHighlight data points with categorical data view mapping
A kategorikus adatnézet-leképezést tartalmazó vizualizációk capabilities.json
fájljában szerepel a "supportsHighlight": true
paraméter.The visuals with categorical data view mapping have capabilities.json
with "supportsHighlight": true
parameter. Például:For example:
{
"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
}
A felesleges kód eltávolítását követően az alapértelmezett vizualizációs forráskód a következőképpen fog kinézni:The default visual source code after removing unnecessary code will look like this:
"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);
}
}
A Power BI-ból származó adatok feldolgozásához szükséges interfészek importálása:Import required interfaces to process data from Power BI:
import DataViewCategorical = powerbi.DataViewCategorical;
import DataViewCategoryColumn = powerbi.DataViewCategoryColumn;
import PrimitiveValue = powerbi.PrimitiveValue;
import DataViewValueColumn = powerbi.DataViewValueColumn;
A gyökérszintű div
elem létrehozása a kategóriaértékekhez:Create root div
element for category values:
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);
}
// ...
}
A div elemek tartalmának törlése az új adatok renderelése előtt:Clear content of div elements before rendering new data:
// ...
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);
}
// ...
}
A kategóriák és a mértékértékek beolvasása a dataView
objektumból:Get categories and measure values from dataView
object:
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;
// ...
}
Ahol a categoryValues
egy kategóriaértékekből álló tömb, a measureValues
egy mértékekből álló tömb, és a measureHighlights
tartalmazza az értékek kiemelt részeit.Where categoryValues
is an array of category values, measureValues
is an array of measures, and measureHighlights
is highlighted parts of values.
Megjegyzés
A measureHighlights
tulajdonságnak lehet kevesebb értéke, mint a categoryValues
tulajdonságnak.Values of measureHighlights
property can be less that values of categoryValues
property.
Ez azt jelenti, hogy az értékeknek egy része lett kijelölve.In means that value was higlighted partially.
A categoryValues
tömb enumerálása és a megfelelő értékek és kiemelések beolvasása:Enumerate categoryValues
array and get corresponding values and highlights:
// ...
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);
});
A div
és p
elemek létrehozása a vizuális DOM adatnézet-értékeinek megjelenítéséhez és vizualizációjához:Create div
and p
elements to display and visualize data view values in visual DOM:
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 vizualize 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 vizualize 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);
});
A szükséges stílusok alkalmazása az elemekre a flex box
használatához, és a div elemek színeinek meghatározása:Apply required styles for elements to use flex box
and define colors for div elements:
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;
}
Az eredményben elvileg a vizualizáció következő nézetének kell szerepelnie.In the result, you should have the following view of the visual.
Az adatpontok kiemelése mátrix adatnézet-leképezésselHighlight data points with matrix data view mapping
A mátrix adatnézet-leképezést tartalmazó vizualizációk capabilities.json
fájljában szerepel a "supportsHighlight": true
paraméter.The visuals with matrix data view mapping have capabilities.json
with "supportsHighlight": true
parameter. Például:For example:
{
"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
}
A mátrix adatnézet-leképezés hierarchiájának létrehozására szolgáló mintaadatok:The sample data to create hierarchy for matrix data view mapping:
1. sorRow1 | 2. sorRow2 | 3. sorRow3 | 1. oszlopColumn1 | 2. oszlopColumn2 | 3. oszlopColumn3 | ÉrtékekValues |
---|---|---|---|---|---|---|
R1R1 | R11R11 | R111R111 | C1C1 | C11C11 | C111C111 | 11 |
R1R1 | R11R11 | R112R112 | C1C1 | C11C11 | C112C112 | 22 |
R1R1 | R11R11 | R113R113 | C1C1 | C11C11 | C113C113 | 33 |
R1R1 | R12R12 | R121R121 | C1C1 | C12C12 | C121C121 | 44 |
R1R1 | R12R12 | R122R122 | C1C1 | C12C12 | C122C122 | 55 |
R1R1 | R12R12 | R123R123 | C1C1 | C12C12 | C123C123 | 66 |
R1R1 | R13R13 | R131R131 | C1C1 | C13C13 | C131C131 | 77 |
R1R1 | R13R13 | R132R132 | C1C1 | C13C13 | C132C132 | 88 |
R1R1 | R13R13 | R133R133 | C1C1 | C13C13 | C133C133 | 99 |
R2R2 | R21R21 | R211R211 | C2C2 | C21C21 | C211C211 | 1010 |
R2R2 | R21R21 | R212R212 | C2C2 | C21C21 | C212C212 | 1111 |
R2R2 | R21R21 | R213R213 | C2C2 | C21C21 | C213C213 | 1212 |
R2R2 | R22R22 | R221R221 | C2C2 | C22C22 | C221C221 | 1313 |
R2R2 | R22R22 | R222R222 | C2C2 | C22C22 | C222C222 | 1414 |
R2R2 | R22R22 | R223R223 | C2C2 | C22C22 | C223C223 | 1616 |
R2R2 | R23R23 | R231R231 | C2C2 | C23C23 | C231C231 | 1717 |
R2R2 | R23R23 | R232R232 | C2C2 | C23C23 | C232C232 | 1818 |
R2R2 | R23R23 | R233R233 | C2C2 | C23C23 | C233C233 | 1919 |
Hozza létre az alapértelmezett vizualizációs projektet, és alkalmazza a capabilities.json
mintáját.Create the default visual project and apply sample of capabilities.json
.
A felesleges kód eltávolítását követően az alapértelmezett vizualizációs forráskód a következő lesz:Default visual source code after removing unessesray code will look:
"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);
}
}
A Power BI-ból származó adatok feldolgozásához szükséges interfészek importálása:Import required interfaces to process data from Power BI:
import DataViewMatrix = powerbi.DataViewMatrix;
import DataViewMatrixNode = powerbi.DataViewMatrixNode;
import DataViewHierarchyLevel = powerbi.DataViewHierarchyLevel;
Hozzon létre két div
elemet a vizualizáció elrendezéséhez:Create two div
elements for 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";
}
Ellenőrizze az update
metódusban szereplő adatokat annak ellenőrzésére, hogy a vizualizáció kap-e adatokat:Check the data in update
method, to ensure that visual gets data:
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
}
// ...
}
A div
elemek tartalmának törlése az új adatok renderelése előtt:Clear content of div
elements before render new data:
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);
// ...
}
A treeWalker
függvény létrehozása a mátrix adatstruktúrájának bejárásához:Create treeWalker
function to traverse matrix data structure:
public update(options: VisualUpdateOptions) {
// ...
const treeWalker = (matrixNode: DataViewMatrixNode, index: number, levels: DataViewHierarchyLevel[], div: HTMLDivElement) => {
}
// ...
}
Ahol a matrixNode
az aktuális csomópont, a levels
tartalmazza ezen hierarchiaszint metaadatoszlopait, és a div
a gyermek HTML-elemek szülőeleme.Where matrixNode
is the current node, levels
is metadata columns of this hierarchy level, div
- parent element for child HTML elements.
A treeWalker
rekurzív függvény; létre kell hozni a div
elem és a p
elemet a fejlécként használt szöveghez, és a függvényt kell meghívni a csomópont gyermekelemeire:The treeWalker
is recursive function, need to create div
element and p
for text as header, and call the function for child elements of node:
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));
}
}
// ...
}
A függvény meghívása a mátrix adatnézet-struktúra oszlopának és sorának gyökérelemére:Call the function for root elements of column and row of matrix data view structure:
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));
}
A selectionID (kijelölésazonosító) generálása a csomópontokhoz, és gombok létrehozása a csomópontok megjelenítéséhez:Generate selectionID for nodes and Create buttons to display nodes:
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();
});
// ...
}
// ...
}
A kiemelés használatának fő lépése az értékek kiegészítő tömbjének feldolgozása.The main step of using highlighting is to process additional array of values.
A végcsomópont objektumát megvizsgálva látható, hogy az értékek tömbjének két tulajdonsága van – value (érték) és highlight (kiemelés):If you inspect the object of terminal node, you can see that the values array has two properties - value and 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
}
}
}
Ahol a value
(érték) tulajdonság jelöli a csomópont értékét egy másik vizualizációból való kijelölés alkalmazása nélkül, és a highlight (kiemelés) tulajdonság azt jelzi, hogy az adatok melyik része lett kijelölve.Where value
property represents value of node without applying a selection from other visual, and highlight property indicates which part of data was highlighted.
Megjegyzés
A highlight
(kiemelés) tulajdonságnak lehet kevesebb értéke, mint a value
(érték) tulajdonságnak.Value of highlight
property can be less that value of value
property.
Ez azt jelenti, hogy az értékeknek egy része lett kijelölve.In means that value was higlighted partially.
A csomópont values
(értékek) tömbjének – ha meg van jelenítve – feldolgozására szolgáló kód beszúrása:Add the code to process the values
array of node if it is presented:
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) {
// ...
}
}
// ...
}
A visszaadott eredmény a vizualizáció, gombokkal és a highlighted value/default value
(kiemelt érték/alapértelmezett érték) értékekkelAs the result you'll get the visual with buttons and values highlighted value/default value