Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
В этой статье описывается, как выделить данные в визуальных элементах Power BI.
По умолчанию при выборе values элемента массив в dataView объекте фильтруется только для отображения выбранных значений. values При фильтрации массива все остальные визуальные элементы на странице отображают только выбранные данные.
Если свойство в файле заданоsupportsHighlight, оно приводит к полному нефильтрованному values массиву вместе с массивомhighlights.truecapabilities.json Массив highlights имеет ту же длину, что и массив значений, и для всех неизбираемых значений задано значение null. Если это свойство включено, соответствующие данные в визуальном элементе выделены путем values сравнения массива с массивом highlights .
В примере обратите внимание, что:
- Без поддержки выделения выбор является единственным значением в
valuesмассиве и единственной строкой, представленной в представлении данных. - При поддержке выделения все значения находятся в массиве
values. Массивhighlightsсодержитnullзначение для не выделенных элементов. Все полосы отображаются в представлении данных, а выделенная панель — другой цвет.
Можно также выделить несколько фрагментов и частичных выделений. Выделенные значения представлены в представлении данных.
Примечание.
Сопоставление представлений табличных данных не поддерживает функцию выделения.
Выделение точек данных с сопоставлением категориального представления данных
Для визуальных элементов с сопоставлением категориального представления данных добавьте "supportsHighlight": true в capabilities.json файл. Например:
{
"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
}
После удаления ненужного кода визуальный исходный код по умолчанию выглядит следующим образом:
"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);
}
}
Импорт необходимых интерфейсов для обработки данных из Power BI:
import DataViewCategorical = powerbi.DataViewCategorical;
import DataViewCategoryColumn = powerbi.DataViewCategoryColumn;
import PrimitiveValue = powerbi.PrimitiveValue;
import DataViewValueColumn = powerbi.DataViewValueColumn;
Создайте корневой div элемент для значений категорий:
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);
}
// ...
}
Очистите содержимое элементов div перед отрисовкой новых данных:
// ...
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);
}
// ...
}
Получение категорий и значений dataView мер из объекта:
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;
// ...
}
Где categoryValues находится массив значений категорий, measureValues является массивом мер и measureHighlights выделены части значений.
Примечание.
Если значения measureHighlights свойства меньше значений categoryValues свойства, то значение было частично выделено.
Перечисление массива и получение соответствующих значений categoryValues и выделений:
// ...
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);
});
Создайте div и элементы для отображения и p визуализации значений представления данных в визуальном элементе 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 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);
});
Примените необходимые стили для используемых flexboxэлементов и определите цвета для элементов 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;
}
Следующее представление визуального элемента является результатом:

Выделение точек данных с сопоставлением представления данных матрицы
Для визуальных элементов с сопоставлением представления данных матрицы добавьте "supportsHighlight": true в capabilities.json файл. Например:
{
"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
}
Примеры данных для создания иерархии для сопоставления представления данных матрицы:
| Строка1 | Строка2 | Строка3 | Столбец1 | Столбец2 | Столбец3 | Значения |
|---|---|---|---|---|---|---|
| R1 | R11 | R111 | C1 | C11 | C111 | 1 |
| R1 | R11 | R112 | C1 | C11 | C112 | 2 |
| R1 | R11 | R113 | C1 | C11 | C113 | 3 |
| R1 | R12 | R121 | C1 | C12 | C121 | 4 |
| R1 | R12 | R122 | C1 | C12 | C122 | 5 |
| R1 | R12 | R123 | C1 | C12 | C123 | 6 |
| R1 | R13 | R131 | C1 | C13 | C131 | 7 |
| R1 | R13 | R132 | C1 | C13 | C132 | 8 |
| R1 | R13 | R133 | C1 | C13 | C133 | 9 |
| R2 | R21 | R211 | C2 | C21 | C211 | 10 |
| R2 | R21 | R212 | C2 | C21 | C212 | 11 |
| R2 | R21 | R213 | C2 | C21 | C213 | 12 |
| R2 | R22 | R221 | C2 | C22 | C221 | 13 |
| R2 | R22 | R222 | C2 | C22 | C222 | 14 |
| R2 | R22 | R223 | C2 | C22 | C223 | 16 |
| R2 | R23 | R231 | C2 | C23 | C231 | 17 |
| R2 | R23 | R232 | C2 | C23 | C232 | 18 |
| R2 | R23 | R233 | C2 | C23 | C233 | 19 |
Создайте визуальный проект по умолчанию и примените пример capabilities.json файла.
После удаления ненужного кода визуальный исходный код по умолчанию выглядит следующим образом:
"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);
}
}
Импортируйте необходимые интерфейсы для обработки данных из Power BI:
import DataViewMatrix = powerbi.DataViewMatrix;
import DataViewMatrixNode = powerbi.DataViewMatrixNode;
import DataViewHierarchyLevel = powerbi.DataViewHierarchyLevel;
Создайте два div элемента для визуального макета:
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";
}
Проверьте данные в методе, чтобы убедиться, что визуальный update элемент получает данные:
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
}
// ...
}
Очистите содержимое div элементов перед отрисовкой новых данных:
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);
// ...
}
Создайте функцию treeWalker для обхода структуры данных матрицы:
public update(options: VisualUpdateOptions) {
// ...
const treeWalker = (matrixNode: DataViewMatrixNode, index: number, levels: DataViewHierarchyLevel[], div: HTMLDivElement) => {
}
// ...
}
Где matrixNode находится текущий узел, levels — это столбцы метаданных этого уровня иерархии— div родительский элемент для дочерних HTML-элементов.
Это treeWalker рекурсивная функция, необходимо создать div элемент и p текст в качестве заголовка и вызвать функцию для дочерних элементов узла:
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));
}
}
// ...
}
Вызовите функцию для корневых элементов столбца и строки структуры представления данных матрицы:
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));
}
Создайте идентификатор выбора для узлов и кнопки создания для отображения узлов:
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();
});
// ...
}
// ...
}
Основным шагом выделения является создание другого массива значений.
Объект узла терминала имеет два свойства для массива значений, значения и выделения:
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
}
}
}
Где value представляет значение узла без применения выделения из другого визуального элемента, highlight указывает, какая часть данных была выделена.
Примечание.
Если значение highlight меньше значения value, то value было частично выделено.
Добавьте код для обработки values массива узла, если он представлен:
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) {
// ...
}
}
// ...
}
Результатом является визуальный элемент с кнопками и значениями, например highlighted value/default value.


