Skip to main content
cancel
Showing results for 
Search instead for 
Did you mean: 

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.

Reply
ConnectedRoman
Frequent Visitor

How do I find out what doesn't work?

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.

1 ACCEPTED 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

pbicvsupport@microsoft.com

 

View solution in original post

9 REPLIES 9
v-chuncz-msft
Community Support
Community Support

@ConnectedRoman,

 

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

Community Support Team _ Sam Zha
If this post helps, then please consider Accept it as the solution to help the other members find it more quickly.

@v-chuncz-msft

 

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

pbicvsupport@microsoft.com

@v-viig

 

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",

 

@v-viig

 

Thank you for your answer. I changed that, but it is still throwing the same error.

 

Capture.PNG

 

 

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

pbicvsupport@microsoft.com

 

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

Helpful resources

Announcements
Microsoft Fabric Learn Together

Microsoft Fabric Learn Together

Covering the world! 9:00-10:30 AM Sydney, 4:00-5:30 PM CET (Paris/Berlin), 7:00-8:30 PM Mexico City

PBI_APRIL_CAROUSEL1

Power BI Monthly Update - April 2024

Check out the April 2024 Power BI update to learn about new features.

April Fabric Community Update

Fabric Community Update - April 2024

Find out what's new and trending in the Fabric Community.