แชร์ผ่าน


ไฮไลต์จุดข้อมูลใน Power BI Visuals

บทความนี้อธิบายวิธีการไฮไลท์ข้อมูลบนวิชวล Power BI

ตามค่าเริ่มต้น เมื่อเลือก values องค์ประกอบ อาร์เรย์ ใน dataViewออบเจ็กต์ จะถูกกรองให้แสดงเฉพาะค่าที่เลือกเท่านั้น valuesเมื่อกรองอาร์เรย์ วิชวลอื่น ๆ ทั้งหมดบนหน้าจะแสดงเฉพาะข้อมูลที่เลือกเท่านั้น

ถ้าคุณตั้งค่าคุณสมบัติ ในsupportsHighlightไฟล์ของคุณcapabilities.jsonเป็น truehighlights จะส่งผลให้อาร์เรย์แบบvaluesไม่มีการกรองเต็มรูปแบบพร้อมกับอาร์เรย์ อาร์เรย์ highlights มีความยาวเท่ากับอาร์เรย์ค่า และค่าที่ไม่ได้เลือกจะถูกตั้งค่า nullเป็น เมื่อเปิดใช้งานคุณสมบัตินี้ ข้อมูลที่เหมาะสมในวิชวลจะถูกเน้นโดยการ values เปรียบเทียบอาร์เรย์กับ highlights อาร์เรย์

ในตัวอย่าง โปรดสังเกตว่า:

  • หากไม่มี การสนับสนุนการไฮไลต์ การเลือกจะเป็นค่าเดียวใน values อาร์เรย์และแถบเดียวที่แสดงในมุมมองข้อมูล
  • ด้วย การสนับสนุนไฮไลต์ ค่าทั้งหมดจะอยู่ใน values อาร์เรย์ อาร์เรย์ highlights ประกอบด้วย null ค่าสําหรับองค์ประกอบที่ไม่ได้ไฮไลต์ แถบทั้งหมดจะปรากฏในมุมมองข้อมูล และแถบที่ไฮไลต์จะเป็นสีที่แตกต่างกัน

นอกจากนี้ยังสามารถเลือกได้หลายรายการและการไฮไลต์บางส่วน ค่าที่ไฮไลต์ไว้จะแสดงในมุมมองข้อมูล

หมายเหตุ

การแมปมุมมองข้อมูลแบบตารางไม่สนับสนุนคุณลักษณะการไฮไลต์

ไฮไลต์จุดข้อมูลด้วยการแมปมุมมองข้อมูลตามประเภท

สําหรับวิชวลที่มีการแมปมุมมองข้อมูลตามประเภท ให้capabilities.jsonเพิ่ม"supportsHighlight": trueไปยังไฟล์ ตัวอย่างเช่น:

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

มุมมองของวิชวลต่อไปนี้เป็นผลลัพธ์:

The visuals with categorical data view mapping and highlight

ไฮไลต์จุดข้อมูลด้วยการแมปมุมมองข้อมูลแบบเมทริกซ์

สําหรับวิชวลที่มีการแมปมุมมองข้อมูลแบบเมทริกซ์ ให้capabilities.jsonเพิ่ม "supportsHighlight": true ไปยังไฟล์ ตัวอย่างเช่น:

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

ข้อมูลตัวอย่างเพื่อสร้างลําดับชั้นสําหรับการแมปมุมมองข้อมูลแบบเมทริกซ์:

Row1 Row2 Row3 คอลัมน์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));
}

สร้าง selectionID สําหรับโหนดและสร้างปุ่มเพื่อแสดงโหนด:

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

Animation selecting data points on the visual, with matrix data views mapping and highlight.