Introduction to creating a code component

Completed

Power Apps provides a ton of functionality out of the box for app makers to build their apps, but sometimes there's a need to build a piece of UI that isn't included. Examples might be replacing a percentage value with a gauge, rendering a bar code rather than an ID, or replacing existing controls with controls that have more functionality—like a drag-and-drop grid view. You can also wrap the Power Apps component framework around existing components you've written in other web frameworks, like React or Angular.

Creating these components allows you to use the full breadth of the modern web development ecosystem: the libraries, frameworks, and other tools you may be familiar with, and packages that capability into a form that allows app makers to build apps with your code, as if it were an out of the box part of the platform.

Note

You can find other sample components—including those that use Angular or React—in our documentation.

Custom Power Apps components are frequently referred to as code components because they require custom code to implement them. They consist of three elements: a manifest, an implementation, and resources. In the following exercise, you'll write your own custom code component: a hello world message that will look like the following image.

Screenshot of the hello pcf custom code component.

This component will listen to changes coming from the host app, and allow the user to make changes that are then pushed to the host app. The following steps will help you build this component.

Install Power Apps CLI

To prepare your computer to build code components, follow these steps:

  1. Install Npm (comes with Node.js) or Node.js (comes with npm). We recommend that you use LTS (Long-Term Support) version 10.15.3 or higher.

  2. Install Visual Studio Code.

  3. Install Power Platform Tools extension.

Create a new component project

To create a new component project, follow these steps:

Create a directory where you'll build your component. In this sample, you'll place the component in C:\source\hello-pcf; however, you can create your own directory. To create your own directory, you'll use a Visual Studio Code.

  1. Start Visual Studio Code.

  2. Select Terminal and select New Terminal.

    Screenshot showing the new terminal selection in Visual Studio Code.

  3. Change directory to your source folder.

    cd source
    
  4. From your source directory, create a directory named hello-pcf.

    md hello-pcf
    
  5. Change directory to hello-pcf.

    cd hello-pcf
    

    Screenshot showing the terminal with commands to create and change directory.

  6. Initialize your component project by using Power Platform CLI with the following command:

    pac pcf init --namespace SampleNamespace --name HelloPCF --template field
    

    The following image shows an example of the output that you should see.

    Screenshot showing the terminal with the pcf init command.

  7. Install the project build tools by using the command npm install. You might see some warnings displayed; however, you can ignore them.

    npm install
    

    Screenshot showing the terminal with the npm install command.

  8. Run the command below to open project in Visual Studio Code.

    code -a .
    
  9. The project contents should look like the image below.

    Screenshot showing the project files.

Update your code component's manifest

Update the manifest file to accurately represent your control.

  1. Expand the HelloPCF folder and open the ControlManifest.Input.xml file.

    Screenshot showing the control manifest input XML file.

  2. Change the version to 1.0.0 and description-key to Says hello.

    Screenshot showing the changes made to the control.

  3. Locate the <property node.

  4. Change the name value to Name, display-name-key to Name, and description-key to A name.

    Screenshot showing the changes made to the property node.

  5. Locate the <resources\ node.

  6. Include a reference to a CSS file named hello-pcf.css that you'll create.

    <css path="css/hello-pcf.css" order="1" />
    

    Screenshot showing the changes made to the resources node

  7. Save your changes by selecting File and then Save.

Add styling to your code component

To add styling to your code component, follow these steps:

  1. Make sure you still have the ControlManifest.Input.xml file selected and then select New Folder.

    Screenshot showing the add new folder button.

  2. Name the new folder css.

  3. Select the css folder you created and select New File.

  4. Name the new file hello-pcf.css.

  5. Open the new hello-pcf.css file you created, and paste the following CSS snippet.

    .SampleNamespace\.HelloPCF {
          font-size: 1.5em;
        }
    
  6. The content of the CSS file should now look like the image below.

    Screenshot showing the content of the CSS file.

  7. Select File and select Save.

Build your code component

Before you can implement your component logic, you need to run a build on your component. This makes sure that the right TypeScript types are generated to match the properties in your ControlManifest.xml document.

Go back to the terminal and build your project by using the following command.

npm run build

The component is compiled into the out/controls/HelloPCF directory. The build artifacts include:

  • css folder

  • bundle.js - Bundled component source code

  • ControlManifest.xml - Actual component manifest file that is uploaded to the Microsoft Dataverse organization

    Screenshot showing the out folder content.

Implement your code component's logic

To implement your code component's logic, follow these steps:

  1. Open index.ts file.

  2. Above the constructor method, insert the following private variables:

    // The PCF context object\
    private context: ComponentFramework.Context<IInputs>;
    // The wrapper div element for the component\
    private container: HTMLDivElement;
    // The callback function to call whenever your code has made a change to a bound or output property\
    private notifyOutputChanged: () => void;
    // Flag to track if the component is in edit mode or not\
    private isEditMode: boolean;
    // Tracks the event handler so we can destroy it when done\
    private buttonClickHandler: EventListener;
    // Tracking variable for the name property\
    private name: string | null;
    

    Screenshot showing the private variables added to the index.ts file.

  3. Locate the init method and replace it with the method below.

    public init(context: ComponentFramework.Context<IInputs>, notifyOutputChanged: () => void, state: ComponentFramework.Dictionary, container: HTMLDivElement) {
    
    // Track all the things
    
    this.context = context;
    
    this.notifyOutputChanged = notifyOutputChanged;
    
    this.container = container;
    
    this.isEditMode = false;
    
    this.buttonClickHandler = this.buttonClick.bind(this);
    
    }
    
  4. Add the snippet below to the init method. This snippet will create the span that will hold the hello message.

    // Create the span element to hold the hello message
    
    const message = document.createElement("span");
    
    message.innerText = `Hello ${this.isEditMode ? "" :context.parameters.Name.raw}`;
    
  5. Add the snippet below to the init method. This code will create a textbox to edit the name.

    // Create the textbox to edit the name
    
    const textbox = document.createElement("input");
    
    textbox.type = "text";
    
    textbox.style.display = this.isEditMode ? "block" : "none";
    
  6. Add the below if statement to init method.

    if (context.parameters.Name.raw) {
    }
    
  7. Add the snippet below inside the if statement. This snippet will set the textbox value and wrap the textbox and the message in a div.

    textbox.value = context.parameters.Name.raw;
    
    // Wrap the two above elements in a div to box out the content
    
    const messageContainer = document.createElement("div");
    
    messageContainer.appendChild(message);
    
    messageContainer.appendChild(textbox);
    
  8. Add the snippet below inside the if statement. This snippet will create a button that will switch between edit and read mode.

    // Create the button element to switch between edit and read modes
    
    const button = document.createElement("button");
    
    button.textContent = this.isEditMode ? "Save" : "Edit";
    
    button.addEventListener("click", this.buttonClickHandler);
    
  9. Add the snippet below inside the if statement. This snippet will add the message container and a button to the main container.

    // Add the message container and button to the overall control container
    
    this.container.appendChild(messageContainer);
    
    this.container.appendChild(button);
    
  10. The init method should now look like the image below.

    Screenshot showing the content of the init method.

  11. Add button select handler method. Add the following method below the init method.

    public buttonClick() {
    }
    
  12. Add the snippet below inside the buttonClick method. This snippet will get the controls via DOM queries.

    // Get our controls via DOM queries
    
    const textbox = this.container.querySelector("input")!;
    
    const message = this.container.querySelector("span")!;
    
    const button = this.container.querySelector("button")!;
    
  13. Add the snippet below inside the buttonClick method. This snippet will copy the text value to name and the call the notify method if in edit mode.

    // If not in edit mode, copy the current name value to the textbox
    
    if (!this.isEditMode) {
    
    textbox.value = this.name ?? "";
    
    } else if (textbox.value != this.name) {
    
    // if in edit mode, copy the textbox value to name and call the     notify callback
    
    this.name = textbox.value;
    
    this.notifyOutputChanged();
    }
    
  14. Add the snippet below inside the buttonClick method. This snippet will flip the mode flag.

    // flip the mode flag
    this.isEditMode = !this.isEditMode;
    
    
  15. Add the snippet below inside the buttonClick method. This snippet will set up new output, based on the changes.

    // Set up the new output based on changes
    
    message.innerText = `Hello ${this.isEditMode ? "" : this.name}`;
    
    textbox.style.display = this.isEditMode ? "inline" : "none";
    
    textbox.value = this.name ?? "";
    
    button.textContent = this.isEditMode ? "Save" : "Edit";
    
    
  16. The buttonClick method should now look like the image below.

    Screenshot showing the content of the buttonClick method.

  17. Locate the updateView method and replace it with the method below.

        public updateView(context: ComponentFramework.Context<IInputs>): void {
    
     // Checks for updates coming in from outside
    
      this.name = context.parameters.Name.raw;
      const message = this.container.querySelector("span")!;
      message.innerText = `Hello ${this.name}`;
    }
    
  18. Locate the replace getOuptuts and replace it with the method below.

    public getOutputs(): IOutputs {
    return {
    // If our name variable is null, return undefined instead
    Name: this.name ?? undefined
    };
    }
    
  19. Locate the destroy method and replace it with the method below.

    public destroy() {
    // Remove the event listener we created in init
    this.container.querySelector("button")!.removeEventListener("click", this.buttonClickHandler);
    }
    
  20. The updateView, getOutputs, and destroy methods should now look like the image below.

    Screenshot showing the content of the updateView, getOutputs, and destroy methods.

Rebuild and run your code component

To rebuild and run your code component, follow these steps:

  1. Now that your component's logic is implemented, go back to the terminal and rebuild it by using the command below.

    npm run build
    
  2. The build should succeed.

    Screenshot showing the build result

  3. Run your component in Node's test harness by running the command below.

    npm start
    

    Note

    You can also enable watch mode to ensure that any changes to the following assets are made automatically without having to restart the test harness by using the npm start watch command.

    • index.ts file.

    • ControlManifest.Input.xml file.

    • Imported libraries in index.ts.

    • All resources listed in the manifest file

  4. New browser window should load the test harness. (The window should open automatically, but you can also reference the address as found in the command window).

  5. Select Edit.

    Screenshot showing the edit button in the test harness

  6. Enter World and select Save.

  7. You may change the container size.

  8. The test harness should now look like the image below.

    Screenshot showing the control inside the test harness

  9. Close the test harness browser window.

  10. Go back to the terminal and stop the watcher by holding [CONTROL] + C.

  11. Type Y and then [ENTER].