Plotly.js charts within a PowerApps Component Framework not working

495 views Asked by At

I am not an expert in JavaScript/TypeScript, so maybe I am missing something trivial here. But does anybody has experience with the PowerApps Component Framework (PCF) in conjunction with Plotly.js? I have created in a console a PowerApps/CanvasApp component via

pac pcf init --namespace PlotlyChartComponent --name PlotlyChartComponent --template field

These are the installed dependencies:

package.json

{
  "name": "pcf-project",
  "version": "1.0.0",
  "description": "Project containing your PowerApps Component Framework (PCF) control.",
  "scripts": {
    "build": "pcf-scripts build",
    "clean": "pcf-scripts clean",
    "rebuild": "pcf-scripts rebuild",
    "start": "pcf-scripts start",
    "refreshTypes": "pcf-scripts refreshTypes"
  },
  "dependencies": {
    "assert": "^2.0.0",
    "glslify": "^7.1.1",
    "mapbox-gl": "^2.7.1",
    "plotly.js": "^2.11.1",
    "react": "^16.8.4",
    "react-plotly.js": "^2.5.1",
    "stream": "^0.0.2"
  },
  "devDependencies": {
    "@types/node": "^16.4",
    "@types/plotly.js": "^1.54.20",
    "@types/powerapps-component-framework": "^1.3.0",
    "@typescript-eslint/eslint-plugin": "^4.29.0",
    "@typescript-eslint/parser": "^4.29.0",
    "eslint": "^7.32.0",
    "eslint-plugin-import": "^2.23.4",
    "eslint-plugin-node": "^11.1.0",
    "eslint-plugin-promise": "^5.1.0",
    "pcf-scripts": "^1",
    "pcf-start": "^1",
    "typescript": "^4.3"
  }
}

The actual code looks like this:

index.ts

import {IInputs, IOutputs} from "./generated/ManifestTypes";

import * as Plotly from 'plotly.js';

export class PlotlyChartComponentl implements ComponentFramework.StandardControl<IInputs, IOutputs> {

    private _container: HTMLDivElement;

    private labelElement: HTMLLabelElement;
    private chartElement: HTMLDivElement;

    
    /**
     * Empty constructor.
     */
    constructor() {}

    /**
     * Used to initialize the control instance. Controls can kick off remote server calls and other initialization actions here.
     * Data-set values are not initialized here, use updateView.
     * @param context The entire property bag available to control via Context Object; It contains values as set up by the customizer mapped to property names defined in the manifest, as well as utility functions.
     * @param notifyOutputChanged A callback method to alert the framework that the control has new outputs ready to be retrieved asynchronously.
     * @param state A piece of data that persists in one session for a single user. Can be set at any point in a controls life cycle by calling 'setControlState' in the Mode interface.
     * @param container If a control is marked control-type='standard', it will receive an empty div element within which it can render its content.
     */
    public init(context: ComponentFramework.Context<IInputs>, notifyOutputChanged: () => void, state: ComponentFramework.Dictionary, container:HTMLDivElement): void
    {
        this._container = document.createElement("div");

        this.labelElement = document.createElement("label");
        this.labelElement.innerHTML = "Test Plotly";

        this.chartElement = document.createElement("div");
        this.chartElement.setAttribute("id", "graph");

        //Plotly.newPlot('graph', [{
        //   x: [0, 1, 3, 2, 4, 1],
        //    type: 'histogram'
        //}]);

        this._container.appendChild(this.labelElement);
        this._container.appendChild(this.chartElement);

        container.appendChild(this._container);
    }


    /**
     * Called when any value in the property bag has changed. This includes field values, data-sets, global values such as container height and width, offline status, control metadata values such as label, visible, etc.
     * @param context The entire property bag available to control via Context Object; It contains values as set up by the customizer mapped to names defined in the manifest, as well as utility functions
     */
    public updateView(context: ComponentFramework.Context<IInputs>): void {}

    /**
     * It is called by the framework prior to a control receiving new data.
     * @returns an object based on nomenclature defined in manifest, expecting object[s] for property marked as “bound” or “output”
     */
    public getOutputs(): IOutputs
    {
        return {};
    }

    /**
     * Called when the control is to be removed from the DOM tree. Controls should use this call for cleanup.
     * i.e. cancelling any pending remote calls, removing listeners, etc.
     */
    public destroy(): void {}
}

So far, so good. This will give a result in the PCF Control Sandbox via

npm start watch

enter image description here

But if I uncomment the plot instantiation within init:

    Plotly.newPlot('graph', [{
        x: [0, 1, 3, 2, 4, 1],
        type: 'histogram'
    }]);

I get the following error in the PCF Sandbox environment:

enter image description here

Has anybody a clue on how to fix this? And what would be a good approach to ingest the data into the class that is going to be plotted?

2

There are 2 answers

2
Vadkerti József On BEST ANSWER

You need to just switch the lines. First add the graph to your container then call Plotly.

2
Linus Silberstein On

Just in case anyone else comes along this exact problem:

Tried to replicate your problem and unfortunately still couldn't get it work.

In my case I found a solution by using the react-plotly-module.