Getting Started with PCF controls: Create your first PCF control

Power Dynamite
4 min readJun 25, 2022

It is just an extended version of Microsoft documentation Click to visit docs.

In this article, I’ll show you how to create a linear slider code component that lets users alter numerical values visually rather than by entering them manually into a column.

Prerequisites

  1. Install Node.js
  2. Install VS Code
  3. Install PowerApps CLI
  4. Add MS Build Path

Let’s get started

1.Create a Project

Open the CMD and move to the location where you want to create the project

cd <<Location Folder>>
mkdir LinearInput

2. Create a new component project by passing basic parameters

pac pcf init --namespace SampleNamespace --name LinearInputControl --template field
npm install

3.To generate ManifestDesignTypes.d.ts file

npm run refreshTypes

Open the project folder in VS code

4. Update the ControlManifest.Input.xml with below code

<?xml version="1.0" encoding="utf-8" ?>
<manifest>
<control namespace="SampleNamespace" constructor="LinearInputControl" version="1.1.0" display-name-key="LinearInputControl_Display_Key" description-key="LinearInputControl_Desc_Key" control-type="standard">
<type-group name="numbers">
<type>Whole.None</type>
<type>Currency</type>
<type>FP</type>
<type>Decimal</type>
</type-group>
<property name="controlValue" display-name-key="controlValue_Display_Key" description-key="controlValue_Desc_Key" of-type-group="numbers" usage="bound" required="true" />
<resources>
<code path="index.ts" order="1" />
<css path="css/LinearInputControl.css" order="1" />
</resources>
</control>
</manifest>

5. You need to create css folder inside LinearInputControl. After that you need to create LinearInputControl.css in css folder

6. Add the following code in css file.

.SampleNamespace\.LinearInputControl input[type=range].linearslider {   
margin: 1px 0;
background:transparent;
-webkit-appearance:none;
width:100%;padding:0;
height:24px;
-webkit-tap-highlight-color:transparent
}
.SampleNamespace\.LinearInputControl input[type=range].linearslider:focus {
outline: none;
}
.SampleNamespace\.LinearInputControl input[type=range].linearslider::-webkit-slider-runnable-track {
background: #666;
height:2px;
cursor:pointer
}
.SampleNamespace\.LinearInputControl input[type=range].linearslider::-webkit-slider-thumb {
background: #666;
border:0 solid #f00;
height:24px;
width:10px;
border-radius:48px;
cursor:pointer;
opacity:1;
-webkit-appearance:none;
margin-top:-12px
}
.SampleNamespace\.LinearInputControl input[type=range].linearslider::-moz-range-track {
background: #666;
height:2px;
cursor:pointer
}
.SampleNamespace\.LinearInputControl input[type=range].linearslider::-moz-range-thumb {
background: #666;
border:0 solid #f00;
height:24px;
width:10px;
border-radius:48px;
cursor:pointer;
opacity:1;
-webkit-appearance:none;
margin-top:-12px
}
.SampleNamespace\.LinearInputControl input[type=range].linearslider::-ms-track {
background: #666;
height:2px;
cursor:pointer
}
.SampleNamespace\.LinearInputControl input[type=range].linearslider::-ms-thumb {
background: #666;
border:0 solid #f00;
height:24px;
width:10px;
border-radius:48px;
cursor:pointer;
opacity:1;
-webkit-appearance:none;
}

7. Update the index.ts file

import { IInputs, IOutputs } from "./generated/ManifestTypes";export class LinearInputControl implements ComponentFramework.StandardControl<IInputs, IOutputs> {
private _value: number;
private _notifyOutputChanged: () => void;
private labelElement: HTMLLabelElement;
private inputElement: HTMLInputElement;
private _container: HTMLDivElement;
private _context: ComponentFramework.Context<IInputs>;
private _refreshData: EventListenerOrEventListenerObject;
public init(context: ComponentFramework.Context<IInputs>, notifyOutputChanged: () => void, state: ComponentFramework.Dictionary, container: HTMLDivElement): void {
this._context = context;
this._container = document.createElement("div");
this._notifyOutputChanged = notifyOutputChanged;
this._refreshData = this.refreshData.bind(this);
// creating HTML elements for the input type range and binding it to the function which refreshes the control data
this.inputElement = document.createElement("input");
this.inputElement.setAttribute("type", "range");
this.inputElement.addEventListener("input", this._refreshData);
//setting the max and min values for the control.
this.inputElement.setAttribute("min", "1");
this.inputElement.setAttribute("max", "1000");
this.inputElement.setAttribute("class", "linearslider");
this.inputElement.setAttribute("id", "linearrangeinput");
// creating a HTML label element that shows the value that is set on the linear range control
this.labelElement = document.createElement("label");
this.labelElement.setAttribute("class", "LinearRangeLabel");
this.labelElement.setAttribute("id", "lrclabel");
// retrieving the latest value from the control and setting it to the HTMl elements.
this._value = context.parameters.controlValue.raw!;
this.inputElement.setAttribute("value", context.parameters.controlValue.formatted ? context.parameters.controlValue.formatted : "0");
this.labelElement.innerHTML = context.parameters.controlValue.formatted ? context.parameters.controlValue.formatted : "0";
// appending the HTML elements to the control's HTML container element.
this._container.appendChild(this.inputElement);
this._container.appendChild(this.labelElement);
container.appendChild(this._container);
}
public refreshData(evt: Event): void {
this._value = (this.inputElement.value as any) as number;
this.labelElement.innerHTML = this.inputElement.value;
this._notifyOutputChanged();
}
public updateView(context: ComponentFramework.Context<IInputs>): void {
// storing the latest context from the control.
this._value = context.parameters.controlValue.raw!;
this._context = context;
this.inputElement.setAttribute("value", context.parameters.controlValue.formatted ? context.parameters.controlValue.formatted : "");
this.labelElement.innerHTML = context.parameters.controlValue.formatted ? context.parameters.controlValue.formatted : "";
}
public getOutputs(): IOutputs {
return {
controlValue: this._value
};
}
public destroy(): void {
this.inputElement.removeEventListener("input", this._refreshData);
}
}

Build your project

npm run build

Debugging your code component

npm start watch

Packaging your code components

1.Create a Solutions folder in current folder

mkdir Solutions

2.Move to Solutions folder

cd Solutions

3. Create a new solution project in the LinearInputControl folder

pac solution init --publisher-name Samples --publisher-prefix samples

4.Now, You need to refer to the location where the created component is located

pac solution add-reference --path ..\

Or

pac solution add-reference --path ..\..

You can try both which ever works for you.

Generate a zip file from your solution project

1.Run command in Solutions folder

msbuild /t:restore

2. Run MSbuild

msbuild

The generated solution zip file is located in the Solution\bin\debug folder.

— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —

Follow me on Medium for more.

--

--