Creating charts using PowerApps component framework

open-events-work-areaPowerApps component framework is the modern way how developers can create their custom controls and embed them into model-driven apps. This is a significant step towards empowering 3rd party developers to build compelling visual components in PowerApps and Dynamics 365 using the same framework which the Microsoft team uses. Our customers are often asking about some data visualization directly on the forms, so in this article, I would like to show you how using third-party typescript libraries you can build you simple visualization and embed it into your Dynamics CE implementation. We will create a simple custom control which will be using data in json format from the text field to create a pie. As a requirement, you need to know how to develop apps using TypeScript.

In case you just wanna download the source code, please use the link below: https://github.com/LrdSpr/PieChartControl

I would assume that you already have node.js installed and you are aware of how to use it. To set up everything you need and learn some basics, go and check the following article from Microsoft: Get tooling for PowerApps component framework. All my components are stored in C:\PowerAppsControls, so I would use this folder to create new controls or to prepare for publishing an existing one. Now when everything is installed, we can start by creating our project. You need to launch visual studio developer command prompt and execute the following command in the directory where you want your new component to be created, for me it is C:\PowerAppsControls\PieChartControl:

pac pcf init –namespace Pkoval.Xrm.Controls –name PieChart –template field

This command will create your project with appropriate namespace and using the provided template. In our case template is the field. The idea is just to replace the standard text field we have on our form with the pie chart. The text data which we can store in our field will be used to render our pie so that every time you change data in the field pie will be redrawn.

The next step here is to set up all the dependencies which our component requires. In the same directory, you need to launch:

npm install

After that, your control is ready for development.

Now, as we want to create a pie and I don’t want to draw it by myself, we need to set up an additional component which we can use to draw charts. Across several projects, we have been using d3, and I’m quite happy with it. Here are some examples of charts and diagrams you can build using d3

visuals

More examples you can find in their GitHub: https://github.com/d3/d3/wiki/Gallery

To set up d3 use following commands:

npm install –save d3
npm install –save-dev @types/d3

Now we are ready to start development, but before that let’s just briefly review the main parts of the PCF control:

In case you’ve used the same file structure as I just go to the following directory: C:\PowerAppsControls\PieChartControl\PieChart.

In this directory, you can find two files:

ControlManifest.Input.xml – Metadata about your control.

index.ts – TypeScript file which will contain your logic.

Let’s at first configure ours manifest.

manifest

Here you can define name, description, and properties which your component can use to retrieve and operate with some data. Also, you may define generic types which your component will support like SingleLine.Text or Currency. In this case, the component will only be shown for Text and Currency fields. For our simple component, we have only one property. This is SingleLine.Text property and also our control only support standard fields with the same type. Type-Group tag defines which standard types are supported for your component. Our custom property also has a name (JsonChart) and can be later used to get data from the mapped control in Dynamics CE or any other application which can use PCF controls. Properties can have different types: bound, input, and output. Bound property can be mapped to the field in Dynamics CE so that you can get data directly from the application. I would not go into the details here as there are plenty of articles which are written about it.

Let’s move to the coding part and implement our control.

First of all, we need to add some imports:

import * as d3 from 'd3';
import {DefaultArcObject, PieArcDatum} from "d3";

Now we need to define data structure which will be used to store information about our pie. Let’s use a very basic interface which contains two fields ‘category’ and ‘quantity.’

interface PieData {
	category: string;
	quantity: number;
}

The category will be used for labels and quantity to render parts of the pie.

Now let’s define some variables in our PieChart class:

	private _value: string;
	private _container: string;

_value – used to store the current JSON representation of the Pie in Dynamics CE.
_container – identifier which can be used to find our control

Now we are ready to draw our pie, so let’s implement the drawChart function.

public drawChart(component: string, data: Array) {
    let width = 480,
        height = 250,
        radius = Math.min(width, height) / 2,
        colourValues = d3.scaleOrdinal().range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
    let arc = d3.arc().innerRadius(radius - 30).outerRadius(radius - 100);
    // notice accessor receives d of type Datum 
    let pie = d3.pie().sort(null).value((d: PieData): number => d.quantity);
    let fill = (d: PieArcDatum): string => String(colourValues(d.data.category));
    let text = (d: PieArcDatum): string => d.data.category;
    let tfx = function(d: DefaultArcObject) {
        return "translate(" + ((radius - 12) * Math.sin(((d.endAngle - d.startAngle) / 2) + d.startAngle)) + "," + (-1 * (radius - 12) * Math.cos(((d.endAngle - d.startAngle) / 2) + d.startAngle)) + ")";
    };
    d3.select(component).select("svg").remove();
    let componentSvg = d3.select(component).append('svg').attr('width', width).attr('height', height).append('g').attr('transform', 'translate(' + width / 2 + ',' + height / 2 + ')');
    // create a group for the pie chart 
    let g = componentSvg.selectAll('.arc').data(pie(data)).enter().append('g').attr('class', 'arc');
    // add pie sections 
    g.append('path').attr('d', arc).attr('fill', fill).attr("stroke", "white").style("stroke-width", "2px").style("opacity", 1); // add labels 
    g.append('text').attr("style", "font: 10px sans-serif;").attr("dy", ".35em").attr('transform', tfx).style("text-anchor", function(d: PieArcDatum) {
        var rads = ((d.endAngle - d.startAngle) / 2) + d.startAngle;
        if ((rads > 7 * Math.PI / 4 && rads < Math.PI / 4) || (rads > 3 * Math.PI / 4 && rads < 5 * Math.PI / 4)) {
            return "middle";
        } else if (rads >= Math.PI / 4 && rads <= 3 * Math.PI / 4) {
            return "start";
        } else if (rads >= 5 * Math.PI / 4 && rads <= 7 * Math.PI / 4) {
            return "end";
        } else {
            return "middle";
        }
    }).text(text)
}

As you see d3 has it’s own methods to manipulate with HTML so the first parameter here is to provide identifier which can be used by d3 to locate where to add ‘Scalable Vector Graphics’ (SVG) tag and also before adding this tag I’m trying to find if it already exists and remove it. We need this as we want to redraw the pie on the fly. Of course, d3 supports animations and different transitions so that it is not necessary to remove this tag, you can simply implement the transformation.  As a second parameter, we are expecting the data which has to be rendered.  We can define several colors which can be used by the rendering engine as well as different angles and positions to describe how the pie should be drawn, so if you are ok with trigonometry, it should not cause any problems.

As we now can simply draw our pie according to the data, let’s finish our control by implementing the rest of the mandatory functions.

public init(context: ComponentFramework.Context, notifyOutputChanged: () => void, state: ComponentFramework.Dictionary, container: HTMLDivElement) {
    this._container = "." + container.className.replace(" ", ",.");
    let dataSet = context.parameters.JsonChart.formatted ? context.parameters.JsonChart.formatted : '[{"category":"No Data","quantity":100}]';
    if (this.IsJsonString(dataSet)) {
        this.drawChart(this._container, JSON.parse(dataSet));
    } else {
        this.drawChart(this._container, JSON.parse('[{"category":"Wrong format","quantity":100}]'));
    }
}

init is the main function of every control which is executed once you’ve opened the page. Here we just define the identifier of the place where we located in the DOM so that we can use it later to add our SVG tag. We try to get the current value from the CDS. For that, we call context.parameters.JsonChart.formatted. JsonChart property should be mapped to some standard field in Dynamics CE or any other application. If there is no data, we just show some defaults like ‘No Data’ and pie with the only section. In case there are some issues with the JSON we are showing ‘Wrong format’ message.

The last two functions we need to implement you can find below:

public updateView(context: ComponentFramework.Context): void {
    this._value = context.parameters.JsonChart.raw;
    let dataSet = JSON.parse(context.parameters.JsonChart.formatted ? context.parameters.JsonChart.formatted : '[{"category":"No Data","quantity":100}]');
    if (this.IsJsonString(dataSet)) {
        this.drawChart(this._container, JSON.parse(dataSet));
    } else {
        this.drawChart(this._container, JSON.parse('[{"category":"Wrong format","quantity":100}]'));
    }
}
public getOutputs(): IOutputs {
    return {
        JsonChart: this._value
    };
}

updateView always called when someone changes values in your mapped field as an example if someone will update assigned field using javascript. In this function, we just need to draw our chart again as source data was changed.

And here how our control can look like on the contact form:

examplechart

So as you see, it is quite simple to build custom controls for power platform. We can use lots of different libraries and implement a really great user experience for our end-customers. You can find the source code of this control here: https://github.com/LrdSpr/PieChartControl

2 thoughts on “Creating charts using PowerApps component framework

  1. Hi Bhuvita,

    The format is the following: [{“category”:”No Data”,”quantity”:100}]. Where category is country or vehicle and quantity is a percentage.
    Example: [{“category”:”BMW”,”quantity”:30}, {“category”:”WV”,”quantity”:70}]

Leave a Reply

Your email address will not be published. Required fields are marked *