Power Apps Custom Components: My Experience
This is a follow on article from my colleague Luke’s article What is the Custom Control Framework and why should Dynamics 365 developers be excited about it? dating back to June 2018. Here I will give an updated explanation of what they are along with my recent experience in developing one, including some tips and tricks.
What Code Components?
Power Apps component framework empowers professional developers and app makers to create code components for model-driven and canvas apps (experimental preview) to provide enhanced user experience for the users to work with data on forms, views, and dashboards. [Source]
From a developers point-of-view, code components are a collection Web Resources bundled together to create a UI component to be used within model-driven and canvas apps. This component should ideally be self-contained, reusable, and relatively lightweight.
The Power Apps component framework (PCF) is a mechanism for pro developers to build components that can be reused within their solutions and enhance user experiences inside their apps to match their context. As a matter of fact, the platform developers of Power Apps have been using this framework for all the controls you see in the unified interface. This allows custom components to be interchanged seamlessly.
A single line of text is a very flexible data structure that could be parsed into anything. Storing a GUID or JSON object won’t make sense to your users but something custom can be developed to transform that data into information. Code components aren’t just to make visual changes either. With access to the CDS Web API, common Power Apps utilities, and device features including camera, location and microphone, you can develop a user experience to match the purpose that field/form/device plays.
These are designed to be reusable so don’t forget that when developing and planning. My favourite site so far is pcf.gallery with a wide variety of controls but you can search around for others quite easily, many with there source available! Don’t waste your effort in developing something that’s already been done.
Here are some of my favourite examples that might help you out:
- Regex Validator (a great way to avoid repeated JavaScript)
- OptionSet Icons / Two Options Icons / Custom Switch
- Date Range Control
- Address Searcher (search suggestions and auto-fills the result)
- Free Text Tag Control (not tied to values like a MultiSelect OptionSet)
Currently, code components are generally available within model-driven apps but are in public preview for canvas apps. For the latest on this and more information visit the docs.
My Experience
I had a requirement to interface with the browser API upon a button click within a model-driven app. With the API result, I needed to create and delete records that were related to the current form’s record. I started with a sub-grid on the form which gave me access to create and delete records. I then considered editing the ribbon of the sub-grid to provide the buttons I needed which would, in turn, call the browser’s API.
This would work but the data presented on the sub-grid wasn’t very meaningful for the user (a JSON object) so I considered a custom control. The sub-grid contained zero to many records and the browser stored an identifier. With the two sets of data, I could present to the user either a store button if the identifier wasn’t found in the list or delete if it was. Along with that, there was a button to delete all.
Being my first, I thought it would be a great way to learn the framework!
It’s worth noting that this is a very custom situation. There is a need to merge both a visual change (to abstract the raw data) and custom functionality (to interact with the browser API). There was no pre-existing solution and this wouldn’t be reusable without the dependent custom entity used as the sub-grid source.
To start, I followed the tutorial on docs.microsoft.com and explored the source code of others. I struggled initially to read the data from the sub-grid but eventually figured out that it must be requested through calling getFormattedValue()
on each record within the dataset. I only required the one field and hardcoded the schema name as it was declared in the ControlManifest.Input.xml
file as required.
const records = context.parameters.dataset.records ;
const ids = context.parameters.dataset.sortedRecordIds;const data = ids.map(id => records[id].getFormattedValue(
"field_schema_name"
));
Once I had the browser API prototype and a basic sub-grid component working, I began merging the two. I selected React as my framework of choice which nearly works perfectly. Within the index.ts
file was the implemented ComponentFramework.StandardControl
class which orchestrated the component in gathering the data, establishing some state, then passing that to a separate React component class to handle the rendering. Unfortunately, the PCF only accepts .ts
files in the manifest, not .tsx
so the classic methods must be used to render.
ReactDom.render(React.createElement(App, props), this.container);
At this stage, I swapped out the browser API calls for dummy data and took a CSV extract of example data for the sub-grid to test the component in the local harness [more details]. Here I was able to play with the CSS and React component to firm up the visual design.
I was done and it was time to deploy. Firstly I reverted the changes applied to mock the browser and followed the docs to package and publish it. To me, this is a bit of a hassle to set up a solution and publish that for every update, which I discarded from the environment once moved into the main solution. Unlike WebResources that can be published from the command-line using spkl, this requires a solution to carry it into the environment. I overcame this in day-to-day development with the use of the testing harness but better still a Fiddler Auto-Responder rule which served my local bundle instead of the server. This is the same as regular Web-Resources which Scott Durow has a great article on.
When it came to updating my component, I would follow the same steps but it would never render the new version despite clearing the cache. It turned out that the version number must be incremented for the platform to notice a change. I don’t believe the docs made that clear when I first read them but it does state that now here.
I hope you found my experience helpful in some way. Please feel free to comment your thoughts or questions. I have including some additional links below to help you get started.
Love Power Apps? Join the Capgemini Microsoft team — open roles here.