Визуальный элемент Power BI не будет отображать или отображать мои добавленные параметры форматирования
Я пытаюсь работать над этим визуальным элементом для Power BI, но сталкиваюсь со странной проблемой. Предполагается, что это столбчатая диаграмма (будет преобразована в линию), которая поддерживает меру по осям X и Y. Когда я пытаюсь построить график с моими данными, я получаю пустой визуал без ошибок. Мои параметры форматирования, которые я добавил (например, всплывающие подсказки или параметры переключения), даже не отображаются на панели параметров визуального форматирования. Я напортачил, и я не могу заставить это работать или действительно изменить, независимо от того, что я делаю, за исключением того, что я захожу и бросаю случайные символы в свой код, чтобы нарушить синтаксис. Я даже пробовал переходить к более старым визуальным файлам, с которыми возился в прошлом, и удостоверился, что не делаю ничего плохого, например, неправильно использую модули или неправильно размещаю их. Я даже повторно импортировал все свои модули, проверил свои переменные, проверил опечатки и т. Д.Кажется, я не могу заставить эту штуку работать. Я даже попытался удалить часть своих возможностей, которая позволяет наносить меры на ось, чтобы увидеть, будет ли она построена со столбцами нормальных значений. К сожалению, безрезультатно. Может быть, у меня застой в глазах и я упустил что-то очевидное, или это что-то более сложное.
Буду очень признателен за любую помощь. Даже если это что-то не связано с проблемой.
"use strict";
import "@babel/polyfill";
import "./../style/visual.less";
import powerbi from "powerbi-visuals-api";
import VisualConstructorOptions = powerbi.extensibility.visual.VisualConstructorOptions;
import VisualUpdateOptions = powerbi.extensibility.visual.VisualUpdateOptions;
import IVisual = powerbi.extensibility.visual.IVisual;
import EnumerateVisualObjectInstancesOptions = powerbi.EnumerateVisualObjectInstancesOptions;
import VisualObjectInstance = powerbi.VisualObjectInstance;
import DataView = powerbi.DataView;
import VisualObjectInstanceEnumerationObject = powerbi.VisualObjectInstanceEnumerationObject;
import * as d3 from "d3";
import { VisualSettings } from "./settings";
import ISelectionManager = powerbi.extensibility.ISelectionManager;
import { ChartDataPoint, ChartViewModel } from "./viewmodels/model";
import IVisualHost = powerbi.extensibility.visual.IVisualHost;
import * as DataViewObject from 'powerbi-visuals-utils-dataviewutils';
interface DataPoints {
duration: number;
value: number;
details: number;
wells: string;
colour: string;
identity: powerbi.visuals.ISelectionId;
highlighted: boolean;
};
interface ViewModel {
dataPoints: DataPoints[];
maxValue: number;
highlights: boolean;
};
export class Visual implements IVisual {
private host: IVisualHost;
private svg: d3.Selection<SVGElement>;
private barGroup: d3.Selection<SVGElement>;
private viewModel: ViewModel;
private locale: string;
private selectionManager: ISelectionManager;
private xAxisGroup: d3.Selection<SVGElement>;
private yAxisGroup: d3.Selection<SVGElement>;
private settings = {
axis: {
x: {
padding: {
default: 50,
value: 50
},
show: {
default: true,
value: true
}
},
y: {
padding: {
default: 50,
value: 50
}
},
border: {
top: {
default: 10,
value: 10
}
}
}
}
constructor(options: VisualConstructorOptions) {
this.host = options.host;
this.svg = d3.select(options.element)
.append(".svg")
.classed("Visual", true);
this.barGroup = this.svg.append("g")
.classed("bar-group", true); //this was chart
this.xAxisGroup = this.svg.append("g")
.classed("x-axis", true);
this.selectionManager = this.host.createSelectionManager();
this.yAxisGroup = this.svg.append("g")
.classed("y-axis", true);
}
//This contains the 'canvas', its scaling, and how it can or cannot interact
public update(options: VisualUpdateOptions) {
//this.updateSettings(options);
let viewModel = this.getViewModel(options);
let width = options.viewport.width;
let height = options.viewport.height;
let xAxisPadding = this.settings.axis.x.show.value ? this.settings.axis.x.padding.value : 0;
// let yAxisPadding = this.settings.axis.y.show.value ? this.settings.axis.y.padding.value : 0;
this.svg.attr({
width: width,
height: height
});
let yScale = d3.scale.linear()
.domain([0, this.viewModel.maxValue])
.range([height - xAxisPadding, 0 + this.settings.axis.border.top.value]);
let xScale = d3.scale.linear()
.domain(viewModel.dataPoints.map(d => d.duration))
// .rangeRoundBands([yAxisPadding, width], this.xPadding);
let xAxis = d3.svg.axis() //come back to this later if it causes issues. https://www.youtube.com/watch?v=zLNfXxDsa-s&list=PL6z9i4iVbl8C2mtjFlH3ECb3q00eFDLAG&index=14 3:40 in
.scale(xScale)
.orient("bottom")
.tickSize(.5);
let yAxis = d3.svg.axis()
.scale(yScale)
.orient("left")
.tickSize(.5);
this.xAxisGroup
.call(xAxis)
.attr({
transform: "translate(0 " + (height - xAxisPadding) + ")"
})
.style({
fill: "#777777"
})
.selectAll("text")
.attr({
"text-anchor": "end",
"font-size": "x-small"
});
this.yAxisGroup
.call(yAxis)
.attr({
transform: "translate(" + this.settings.axis.y.padding + ",0)"
})
.style({
fill: "#777777"
})
.selectAll("text")
.style({
"text-anchor": "end",
"font-size": "x-small"
});
let bars = this.barGroup
.selectAll(".bar") //keep an eye on this. was '.lines'
.data(viewModel.dataPoints);
bars.enter()
.append("svg")
.classed("bar", true); //this was chart
bars
.attr({
//width: xScale.range(),
height: d => height = yScale(d.value) - xAxisPadding,
x: d => xScale(d.duration),
})
.style({
fill: d => d.colour,
"fill-opacity": d => viewModel.highlights ? d.highlighted ? 1.0 : 0.5 : 1.0
})
.on("click", (d) => {
this.selectionManager
.select(d.identity, true)
.then(ids => {
bars.style({
"fill-opacity": ids.length > 0 ?
d => ids.indexOf(d.identity) >= 0 ? 1.0 : 0.5
: 1.0
});
})
});
bars.exit()
.remove();
}
/* private updateSettings(options: VisualUpdateOptions) {
this.settings.axis.x.show.value = DataViewObjects.getValue
(options.dataViews[0].metadata.objects, {
objectName: "xAxis",
propertyName: "show"
})
}*/
private getViewModel(options: VisualUpdateOptions): ViewModel {
let dv = options.dataViews;
let viewModel: ViewModel = {
dataPoints: [],
maxValue: 0,
highlights: false
};
/* if (!dv
|| !dv[0]
|| !dv[0].categorical
|| !dv[0].categorical.categories
|| !dv[0].categorical.categories[0].source
|| !dv[0].categorical.values)
return viewModel;*/
let view = dv[0].categorical;
let categories = view.categories[0];
let values = view.values[0];
let highlights = values.highlights;
for (let i = 0, len = Math.max(categories.values.length, values.values.length); i < len; i++) {
viewModel.dataPoints.push({
duration: <number>values.values[i],
value: <number>values.values[i],
details: <number>categories.values[i],
wells: <string>categories.values[i],
colour: this.host.colorPalette.getColor(<string>categories.values[i]).value,
identity: this.host.createSelectionIdBuilder()
.withCategory(categories, i)
.createSelectionId(),
highlighted: highlights ? highlights[i] ? true : false : false
});
}
viewModel.maxValue = d3.max(viewModel.dataPoints, d => d.value);
viewModel.highlights = viewModel.dataPoints.filter(d => d.highlighted).length > 0;
return viewModel;
}
public enumerateObjectInstances(options: EnumerateVisualObjectInstancesOptions): VisualObjectInstance[] | VisualObjectInstanceEnumerationObject {
let propertyGroupName = options.objectName;
let properties: VisualObjectInstance[] = [];
switch (propertyGroupName) {
case "xAxisGroup":
properties.push({
objectName: propertyGroupName,
properties: {
show: this.settings.axis.x.show.value
},
selector: null
});
break;
};
return properties
}
}
Для всех, кому интересно, моя конечная цель (в конечном итоге) - получить линейный график, который может поддерживать меру (вычисляемый столбец) по любой оси. Например, даты для оси X очень распространены. Я также хотел бы иметь возможность использовать меру, которая может принимать диапазон дат, например 9/5/2019–9/10/2019, и преобразовывать его в продолжительность. Ожидаемый результат - 1-5.
Измерение оси Y может делать что-то вроде... например, отображать все значения, которые соответствуют определенным параметрам.
{
"supportsHighlight": true,
"dataRoles": [
{
"displayName": "Y Axis",
"name": "values",
"kind": "Grouping"
},
{
"displayName": "Details",
"name": "details",
"kind": "Grouping"
},
{
"displayName": "Duration",
"name": "duration",
"kind": "Measure"
}
],
"objects": {
"xAxis": {
"displayName": "X Axis",
"properties": {
"show": {
"displayName": "Show X Axis",
"type": {
"bool": true
}
}
}
},
"dataPoint": {
"displayName": "Data colors",
"properties": {
"defaultColor": {
"displayName": "Default color",
"type": {
"fill": {
"solid": {
"color": true
}
}
}
},
"showAllDataPoints": {
"displayName": "Show all",
"type": {
"bool": true
}
},
"fill": {
"displayName": "Fill",
"type": {
"fill": {
"solid": {
"color": true
}
}
}
},
"fillRule": {
"displayName": "Color saturation",
"type": {
"fill": {}
}
},
"fontSize": {
"displayName": "Text Size",
"type": {
"formatting": {
"fontSize": true
}
}
}
}
}
},
"dataViewMappings": [
{
"categorical": {
"categories": {
"for": {
"in": "duration"
},
"dataReductionAlgorithm": {
"top": {
"count": 450
}
}
},
"values": {
"group": {
"by": "values",
"select": [
{
"bind": {
"to": "details"
}
}
]
},
"dataReductionAlgorithm": {
"top": {
"count": 450
}
}
}
}
}
]
}
1 ответ
Судя по комментариям, часть задачи состоит в том, чтобы получить ось Y, на которой наименьшее число отображается вверху, а наибольшее число - внизу. Это можно сделать с помощью настраиваемой строки формата для меры. Вот быстрый и грязный мокап с использованием стандартной линейной диаграммы.
Если вы установите для формата меры значение "Пользовательский" и используете строку формата без символа "-" для отрицательного значения, например "0;0;0", визуальный элемент не будет отображать "-" на оси y. Это приводит к желаемому эффекту.
Обратите внимание, что эта мера -1 * SUM ( 'Table'[Column1] )
. Этот столбец содержит только положительные значения.