Register now to learn Fabric in free live sessions led by the best Microsoft experts. From Apr 16 to May 9, in English and Spanish.
I'm currently trying to understand how visuals are developed per TypeScript. My experience with programming is mostly limited to C# in school - so not that much. That means that I'm having a lot of troubly unterstanding it completely.
In order to pick up the way visuals are created, I wrote the simple bar chart myself - by copying it line by line, and looking at what everything does. It helped me a lot. When I start the server, load the developer visual, and add data to it - nothing happens. Only the update count goes up.
The config/capability files are all the same as in the github example. The barchart code is the same, too.
How can I "debug" this file? How do I find out, what is going wrong?
EDIT: Editing to say, that I have tried debugging.
Solved! Go to Solution.
You should use module instead of namespace:
namespace powerbi.extensibility.visual
As a result you should have this:
module powerbi.extensibility.visual
Ignat Vilesov,
Software Engineer
Microsoft Power BI Custom Visuals
Make sure you run command in the right visual project and the file name in tsconfig.json is correct. To debug your visual, use the try...catch statement.
https://github.com/Microsoft/PowerBI-visuals/blob/master/tools/debugging.md
Hello, thank you for your time.
I have already tried debugging, but there were no errors. When I try compiling my code with pbiviz start, it throws this error in the .tmp/precompile/visualPlugin.ts file:
{Projectname} is not a property of "Visual"
The filenames/-paths in pbiviz.json and tsconfig.json are right.
Did you fill capabilities.json?
Could you please share your current code?
Ignat Vilesov,
Software Engineer
Microsoft Power BI Custom Visuals
Apparently my posts with the code are getting deleted. barChart.ts and capabilities.json are the same as in the tutorial. My classes name is the same as the foldername. pbiviz.json and tsconfig.json have the right names.
EDIT: Now they're there. I deleted unnecessary ones.
"visualClassName": "secondTry.ts",
You need to specify just class name without file extension:
"visualClassName": "secondTry",
You should use module instead of namespace:
namespace powerbi.extensibility.visual
As a result you should have this:
module powerbi.extensibility.visual
Ignat Vilesov,
Software Engineer
Microsoft Power BI Custom Visuals
Thank you very much.
Yes, of course.
tsconfig.json:
{ "compilerOptions": { "allowJs": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, "target": "ES5", "sourceMap": true, "out": "./.tmp/build/barChart.js" }, "files": [ "node_modules/powerbi-visuals-utils-dataviewutils/lib/index.d.ts", "node_modules/powerbi-visuals-utils-typeutils/lib/index.d.ts", "node_modules/powerbi-visuals-utils-colorutils/lib/index.d.ts", ".api/v1.7.0/PowerBI-visuals.d.ts", "src/barChart.ts", "src/objectEnumerationUtility.ts", "src/colorPalette.ts" ] }
pbiviz.json:
{ "visual": { "name": "secondTry", "displayName": "secondTry", "guid": "secondTry907B5A8A17DF4B48A41E2DB3737B35D8", "visualClassName": "secondTry.ts", "version": "1.0.0", "description": "", "supportUrl": "", "gitHubUrl": "" }, "apiVersion": "1.7.0", "author": { "name": "", "email": "" }, "assets": { "icon": "assets/icon.png" }, "externalJS": [ "node_modules/powerbi-visuals-utils-dataviewutils/lib/index.js", "node_modules/powerbi-visuals-utils-typeutils/lib/index.js", "node_modules/powerbi-visuals-utils-colorutils/lib/index.js", "node_modules/d3/d3.min.js" ], "style": "style/visual.less", "capabilities": "capabilities.json", "dependencies": "dependencies.json", "stringResources": [] }
capabilities.json:
{ "dataRoles": [ { "displayName": "Category Data", "name": "category", "kind": "Grouping" }, { "displayName": "Measure Data", "name": "measure", "kind": "Measure" } ], "dataViewMappings": [ { "conditions": [ { "category": { "max": 1 }, "measure": { "max": 1 } } ], "categorical": { "categories": { "for": { "in": "category" } }, "values": { "select": [ { "bind": { "to": "measure" } } ] } } } ], "objects": { "enableAxis": { "displayName": "Enable Axis", "properties": { "show": { "displayName": "Enable Axis", "type": { "bool": true } } } }, "colorSelector": { "displayName": "Data Colors", "properties": { "fill": { "displayName": "Color", "type": { "fill": { "solid": { "color": true } } } } } }, "generalView": { "displayName": "General View", "properties": { "opacity": { "displayName": "Bars Opacity", "type": { "integer": true } } } } } }
barChart.ts:
namespace powerbi.extensibility.visual { /** * Interface for barcharts viewmodel. * * @interface * @property {barChartDataPoint[]} dataPoints - Set of datapoints * @property {number} dataMax - Highest data value in dataPoints * @property {BarChartSettings} settings - Viewmodel settings (?) */ interface BarChartViewModel { dataPoints: BarChartDataPoint[]; dataMax: number; settings: BarChartSettings; } /** * A datapoint. * * @interface * @property {PrimitiveValue} value - Value for datapoint * @property {string} category - Categorization of datapoint * @property {string} color - Color of datapoint * @property {ISelectionId} selectionId - Id of the datapoint when selected. */ interface BarChartDataPoint { value: PrimitiveValue; category: string; color: string; selectionId: ISelectionId; } /** * Settings of barchart * * @interface * @property {object} enableAxis - Has property "show" -> boolean * @property {object} generalView - Has property "opacity" -> number */ interface BarChartSettings { enableAxis: { show: boolean; }; generalView: { opacity: number; }; } /** * Function that converts queried data into a view model that will be used by the visual. * * @function * @param {VisualUpdateOptions} options - Contains references to the size of the container * and the dataView which contains all the data * the visual had queried. * @param {IVisualHost} host - Contains references to the host which contains services */ function visualTransform( options: VisualUpdateOptions, host: IVisualHost 😞 BarChartViewModel { //Getting colors let Colors = null; //changed for sharing //DataView object that contains all data needed to render visual (capabilities) let dataViews = options.dataViews; //Saving default settings in var let defaultSettings: BarChartSettings = { enableAxis: { show: false }, generalView: { opacity: 100 } }; //Creating viewmodel let viewModel: BarChartViewModel = { dataPoints: [], dataMax: 0, settings: <BarChartSettings>{} }; //Returning viewmodel if it's not empty if ( !dataViews || !dataViews[0] || !dataViews[0].categorical || !dataViews[0].categorical.categories || !dataViews[0].categorical.categories[0].source || !dataViews[0].categorical.values ) return viewModel; //Saving JSON object in vars let categorical = dataViews[0].categorical; let category = categorical.categories[0]; let dataValue = categorical.values[0]; let barChartDataPoints: BarChartDataPoint[] = []; let dataMax: number; let colorPalette: IColorPalette = host.colorPalette; //Change to colors ? let objects = dataViews[0].metadata.objects; let barChartSettings: BarChartSettings = { //setting settings enableAxis: { show: getValue<boolean>( //method in objectEnumerationUtility.ts objects, "enableAxis", "show", defaultSettings.enableAxis.show ) }, generalView: { opacity: getValue<number>( //method in objectEnumerationUtility.ts objects, "generalView", "opacity", defaultSettings.generalView.opacity ) } }; for ( //Going through each datapoint var i = 0, len = Math.max(category.values.length, dataValue.values.length); i < len; i++ ) { let defaultColor: Fill = { //defining defaultColor of datapoint solid: { color: colorPalette.getColor(Colors[i] + "").value } }; //Colors[i] + "").value barChartDataPoints.push({ //Pushing datapoints into array category: category.values[i] + "", value: dataValue.values[i], color: getCategoricalObjectValue<Fill>( category, i, "colorSelector", "fill", defaultColor ).solid.color, selectionId: host .createSelectionIdBuilder() .withCategory(category, i) .createSelectionId() }); } //Saving max value in var dataMax = <number>dataValue.maxLocal; //Returning newly constructed VisualData return { dataPoints: barChartDataPoints, dataMax: dataMax, settings: barChartSettings }; } /** * The barChart class. * * @class */ export class secondTry implements IVisual { private svg: d3.Selection<SVGElement>; private host: IVisualHost; private selectionManager: ISelectionManager; private xAxis: d3.Selection<SVGElement>; private barContainer: d3.Selection<SVGElement>; private barChartSettings: BarChartSettings; private barChartContainer: d3.Selection<SVGElement>; private barDataPoints: BarChartDataPoint[]; static Config = { xScalePadding: 0.1, solidOpacity: 1, transparentOpacity: 0.5, margins: { top: 0, right: 0, bottom: 25, left: 30 }, xAxisFontMultiplier: 0.04 }; /** * Creates barChart-instance. * @param options Reference to element that will contain the visual. */ constructor(options: VisualConstructorOptions) { //Setting a host: Contains various services for visual (color, createSelectionIdBuilder/Manager, allowInteraction) this.host = options.host; //Creates a selection manager: Notifies if something was selected this.selectionManager = options.host.createSelectionManager(); //Creates "svg" element on site and saves it in var let svg = (this.svg = d3 .select(options.element) .append("svg") .classed("barChart", true)); //Creates "g" element and classes it barContainer/xAxis, saves it in var this.barContainer = svg.append("g").classed("barContainer", true); this.xAxis = svg.append("g").classed("xAxis", true); } /** * Updates state of visual when changed. * @param options Reference to datapoints/container. */ @logExceptions() public update(options: VisualUpdateOptions) { //getting the viewModel with visualTransform -> Creates valid datapoints for visual let viewModel: BarChartViewModel = visualTransform(options, this.host); let settings = (this.barChartSettings = viewModel.settings); this.barDataPoints = viewModel.dataPoints; //getting viewport sizes let width = options.viewport.width; let height = options.viewport.height; //setting correct sizes to visual this.svg.attr({ width: width, height: height }); //if enableAxis.show == true if (settings.enableAxis.show) { let margins = secondTry.Config.margins; height -= margins.bottom; //Höhe um untere Margin verkleinern } //Correcting font-size this.xAxis.style( "font-size", d3.min([height, width]) * secondTry.Config.xAxisFontMultiplier ); //Correcting y-scale let yScale = d3.scale .linear() .domain([0, viewModel.dataMax]) .range([height, 0]); //correcting x-scale let xScale = d3.scale .ordinal() .domain(viewModel.dataPoints.map(d => d.category)) .rangeRoundBands([0, width], secondTry.Config.xScalePadding, 0.2); //Creating xAxis? let xAxis = d3.svg.axis().scale(xScale).orient("bottom"); //Moving x-Axis down by {height} pixels this.xAxis.attr("transform", "translate(0, " + height + ")").call(xAxis); //Selecting all .bar elements into barContainer and saving in var let bars = this.barContainer.selectAll(".bar").data(viewModel.dataPoints); //Placeholder nodes bars.enter().append("rect").classed("bar", true); bars.attr({ width: xScale.rangeBand(), height: d => height - yScale(<number>d.value), y: d => yScale(<number>d.value), x: d => xScale(d.category), fill: d => d.color, //Fat Arrow means function. "function d returns d.color". Doesn't have own "this" "fill-opacity": viewModel.settings.generalView.opacity / 100 }); let selectionManager = this.selectionManager; let allowInteractions = this.host.allowInteractions; //Must be anonymous function isntead of =>, d3 uses 'this' as ref to clicked obj bars.on("click", function(d) { // Allow selection only if the visual is rendered in a view that supports interactivity (e.g. Report) if (allowInteractions) { selectionManager.select(d.selectionId).then((ids: ISelectionId[]) => { bars.attr({ "fill-opacity": ids.length > 0 //If length of id is > 0, then transparent, else solid ? secondTry.Config.transparentOpacity : secondTry.Config.solidOpacity }); //Setting clicked obj solid d3.select(this).attr({ "fill-opacity": secondTry.Config.solidOpacity }); }); (<Event>d3.event).stopPropagation(); } }); //Remove elements with no data bars.exit().remove(); } /** * Enumerates through the objects defined in the capabilities and adds the properties to the format pane * * @function * @param {EnumerateVisualObjectInstancesOptions} options - Map of defined objects */ @logExceptions() public enumerateObjectInstances( options: EnumerateVisualObjectInstancesOptions 😞 VisualObjectInstanceEnumeration { let objectName = options.objectName; let objectEnumeration: VisualObjectInstance[] = []; switch (objectName) { case "enableAxis": objectEnumeration.push({ objectName: objectName, properties: { show: this.barChartSettings.enableAxis.show }, selector: null }); break; case "colorSelector": for (let barDataPoint of this.barDataPoints) { objectEnumeration.push({ objectName: objectName, displayName: barDataPoint.category, properties: { fill: { solid: { color: barDataPoint.color } } }, selector: barDataPoint.selectionId }); } break; case "generalView": objectEnumeration.push({ objectName: objectName, properties: { opacity: this.barChartSettings.generalView.opacity }, validValues: { opacity: { numberRange: { min: 10, max: 100 } } }, selector: null }); break; } return objectEnumeration; } } /** * Logging exceptions for debugging purposes. * @function */ export function logExceptions(): MethodDecorator { return function( target: Object, propertyKey: string, descriptor: TypedPropertyDescriptor<Function> 😞 TypedPropertyDescriptor<Function> { return { value: function() { try { return descriptor.value.apply(this, arguments); } catch (e) { console.error(e); throw e; } } }; }; } }
.tmp/precompile/visualPlugin.ts:
module powerbi.visuals.plugins { export var secondTry907B5A8A17DF4B48A41E2DB3737B35D8_DEBUG = { name: 'secondTry907B5A8A17DF4B48A41E2DB3737B35D8_DEBUG', displayName: 'secondTry', class: 'secondTry.ts', version: '1.0.0', apiVersion: '1.7.0', create: (options: extensibility.visual.VisualConstructorOptions) => new powerbi.extensibility.visual.secondTry907B5A8A17DF4B48A41E2DB3737B35D8.secondTry.ts(options), custom: true }; }
Covering the world! 9:00-10:30 AM Sydney, 4:00-5:30 PM CET (Paris/Berlin), 7:00-8:30 PM Mexico City
Check out the April 2024 Power BI update to learn about new features.
User | Count |
---|---|
13 | |
2 | |
2 | |
1 | |
1 |