Component Inputs Outputs and Datasets
Complete the full lesson to earn 25 points
Work through each section, then tap “Mark as Complete” on the last one.
Module: Extend the User Experience
Lesson: Component Inputs, Outputs, and Datasets
Introduction: Why Interactivity Matters
When building custom components for Power Apps using the Power Apps Component Framework (PCF), you are essentially creating a bridge between raw data and the user interface. While a simple static component might display a piece of text or a button, truly useful components are those that react to the data within the system and, in turn, provide feedback to the host environment. Understanding how to manage inputs, outputs, and datasets is the cornerstone of professional PCF development.
If you think of a PCF component as a black box, inputs are the "parameters" you pass into the box from the Power Apps Studio or model-driven app configuration. Datasets are the "streams" of information that provide complex, tabular data for your component to render. Outputs are the "signals" your component sends back to the host, such as a value change or a user interaction. Mastering these three concepts allows you to build components that feel like native parts of the platform rather than isolated islands of code. This lesson will guide you through the technical implementation, architectural patterns, and best practices for managing this data flow.
Understanding the Control Manifest (ControlManifest.Input.xml)
Before writing any TypeScript code, you must define how your component communicates with the outside world. This happens in the ControlManifest.Input.xml file. This file acts as a contract between your component and the Power Apps framework. If a property is not defined here, the Power Apps Studio will not know it exists, and you will have no way to bind it to a field or a data source.
Property Types
When defining properties, you must specify the type. The framework supports several primitive types, such as Whole.None, Decimal, Text, TwoOptions, and Enum. By explicitly defining these types, you ensure that the component receives data in a format it can handle.
Callout: The Contractual Nature of the Manifest Think of the
ControlManifest.Input.xmlas an API specification. Just as you wouldn't send a string to an endpoint expecting an integer, you must be precise with your manifest definitions. The framework uses this file to generate the property types in your TypeScriptIInputsinterface, which is the primary object you use to read data within your component's lifecycle methods.
Defining Datasets
A dataset is more complex than a standard property. While a property represents a single value (like a phone number or a status), a dataset represents a collection of records, similar to a SQL result set or an array of objects. When your component needs to display a list, a gallery, or a complex grid, you must declare a data-set element in your manifest.
<data-set name="sampleDataSet" display-name-key="Sample_Dataset_Name">
<property-set name="sampleProperty" display-name-key="Property_Name" of-type="SingleLine.Text" usage="bound" />
</data-set>
By adding a data-set, the framework provides you with an IDataSet object in your updateView method. This object contains methods to fetch data, handle pagination, and manage selection states.
Working with Inputs: The IInputs Interface
The IInputs interface is generated automatically by the PCF CLI based on your manifest file. Every property you define in the manifest appears as a property on the context.parameters object. Accessing these values is straightforward, but there are nuances regarding how the framework handles updates.
Reading Input Values
In your updateView method, you typically read the current value of a property like this:
public updateView(context: ComponentFramework.Context<IInputs>): void {
const myValue = context.parameters.myProperty.raw;
if (myValue !== null) {
this.renderMyComponent(myValue);
}
}
The .raw property returns the underlying value. It is important to check for null or undefined values, as the framework may call updateView before the data has fully loaded from the server, especially in asynchronous scenarios.
Note: The Lifecycle of
updateViewTheupdateViewmethod is the heart of your component. It is called every time a property changes, the dataset updates, or the window is resized. You must ensure that your rendering logic is idempotent—meaning it should produce the same output regardless of how many times it is called with the same input.
Working with Outputs: Communicating Back to the Host
Outputs are the mechanism by which your component notifies the Power Apps framework that a value has changed. This is critical for form fields where you want to update a Dataverse record when a user interacts with your custom UI.
The notifyOutputChanged Callback
You do not update the data directly. Instead, you call a function provided by the framework called notifyOutputChanged. When you call this, the framework triggers a cycle that eventually calls your getOutputs method.
- User Interaction: The user changes a value in your component.
- Notification: Your component calls
this.notifyOutputChanged(). - Framework Request: The framework calls your
getOutputs()method to retrieve the new values. - Update: The framework updates the underlying data source (e.g., the Dataverse field).
Implementing getOutputs
public getOutputs(): IOutputs {
return {
myProperty: this.currentValue
};
}
This method must return an object that matches the IOutputs interface defined in your manifest. If you have multiple properties that need to be updated, you include them all in this returned object.
Deep Dive into Datasets: The IDataSet Object
Datasets are the most powerful feature of PCF. They allow you to build custom grids, specialized visualizations, or complex data manipulation tools. The IDataSet object provides access to the records, the paging state, and the column metadata.
Fetching Data
Data in a dataset is not always loaded in its entirety. To prevent performance issues, the framework uses a paging model. You can access the records through context.parameters.myDataSet.records. However, you should always check the paging property of the dataset to see if there are more records available.
const dataset = context.parameters.sampleDataSet;
const recordIds = dataset.sortedRecordIds;
for (const id of recordIds) {
const record = dataset.records[id];
const value = record.getValue("fieldname");
console.log(value);
}
Handling Paging
If you are building a list component, you will eventually need to load more data. You can use the paging.loadNextPage() method to request additional records. It is good practice to display a "Load More" button or implement "infinite scroll" logic by monitoring the scroll position of your container element.
Warning: Performance Pitfalls Do not attempt to fetch all records at once if your dataset is large. Always rely on the built-in paging functionality of the
IDataSetinterface. Attempting to bypass this by writing custom FetchXML queries to load data outside of the provided dataset will break the platform's security model and lead to sluggish performance.
Comparison: Property vs. Dataset
It is crucial to choose the right tool for the job. Misusing a dataset when a single property is sufficient will add unnecessary complexity to your component, while trying to fit a collection of data into a single property will lead to data truncation and loss of functionality.
| Feature | Property | Dataset |
|---|---|---|
| Data Scope | Single value (string, number, boolean) | Collection of records (rows and columns) |
| Use Case | Form fields, configuration settings, toggles | Grids, galleries, charts, custom lists |
| Complexity | Low; easy to bind in Studio | High; requires handling paging/sorting |
| Binding | Binds to a single field | Binds to a view or data source |
Best Practices for Architecture
When developing components, keep your logic organized. A common mistake is putting all the UI rendering logic, data processing, and event handling into the main control file. As your component grows, this file becomes unreadable and difficult to maintain.
Separation of Concerns
- State Management: Keep your state (the current value of inputs, the current page of the dataset) in a dedicated object.
- Rendering Logic: Use a framework like React or simply separate your DOM manipulation into helper functions or classes.
- Data Transformation: Create mapping functions to convert
IDataSetrecords into a format that your UI components can easily consume (e.g., a simple array of objects).
Handling Asynchronous Updates
The framework is inherently asynchronous. When the user changes a filter or sorts a column, the updateView method will be called again with the updated dataset. Ensure that your component clears out old data before rendering the new data to avoid "ghost" entries or flickering.
Tip: Use
context.mode.trackContainerResizeAlways enable container resizing in yourinitmethod. This allows your component to respond to changes in the UI layout. If your component is placed inside a flexible layout or a sidebar that can be collapsed, the host will notify your component of the new dimensions, allowing you to adjust your rendering accordingly.
Common Pitfalls and How to Avoid Them
1. Blocking the Main Thread
Because PCF components run in the browser's main thread, long-running calculations within updateView or event handlers will freeze the entire Power Apps interface. If you need to perform heavy data processing, use Web Workers or break the work into small chunks using setTimeout or requestAnimationFrame.
2. Ignoring context.updatedProperties
In your updateView method, you have access to context.updatedProperties. This is an array of strings representing which properties have actually changed since the last update. You should use this to optimize your rendering. If only a minor property changed, you might not need to re-render the entire dataset.
3. Forgetting to Clean Up
If you attach event listeners to the DOM (e.g., window.addEventListener('resize', ...)), you must remove them in the destroy method. Failure to do so will cause memory leaks, especially in model-driven apps where the user might navigate between different forms without a full page refresh.
4. Hardcoding Data Types
Never assume the data type of an input. Even if you configured it as a Whole.None in the manifest, always perform a check before using it in a calculation. The framework's flexibility means that configuration can change, and your code should be resilient to those changes.
Step-by-Step: Implementing a Simple Data-Bound Component
To solidify these concepts, let's look at the steps required to build a simple component that displays a list of names from a dataset.
Step 1: Define the Manifest
In ControlManifest.Input.xml, define the dataset:
<data-set name="peopleList" display-name-key="People_List">
<property-set name="fullName" display-name-key="Full_Name" of-type="SingleLine.Text" usage="bound" />
</data-set>
Step 2: Initialize the Component
In your init method, store a reference to the container:
public init(context: ComponentFramework.Context<IInputs>, notifyOutputChanged: () => void, state: ComponentFramework.Dictionary, container: HTMLDivElement): void {
this.container = container;
this.notifyOutputChanged = notifyOutputChanged;
}
Step 3: Handle Data in updateView
Clear the container and loop through the dataset records:
public updateView(context: ComponentFramework.Context<IInputs>): void {
const dataset = context.parameters.peopleList;
this.container.innerHTML = ""; // Clear existing
if (dataset.loading) return; // Wait for data to load
dataset.sortedRecordIds.forEach(id => {
const record = dataset.records[id];
const name = record.getFormattedValue("fullName");
const div = document.createElement("div");
div.innerText = name;
this.container.appendChild(div);
});
}
Step 4: Cleanup
public destroy(): void {
// Perform cleanup here
}
Advanced Concepts: Interacting with Selected Records
One of the most requested features for custom grids is the ability to select records. The IDataSet object makes this easy. You can call dataset.setSelectedRecordIds(ids) to programmatically select records, or listen for selection changes using the dataset.getSelectedRecordIds() method.
When a user clicks on an item in your custom list, you should update the selection state in the dataset. This ensures that other components on the page (like a command bar or a dashboard) are aware of what the user has selected.
private onRowClick(id: string): void {
this.context.parameters.peopleList.setSelectedRecordIds([id]);
this.notifyOutputChanged();
}
This interaction demonstrates the true power of the PCF framework: your custom component is not just a display tool, but an active participant in the Power Apps data ecosystem.
FAQ: Common Questions
Q: Can I use external libraries like jQuery or React? A: Yes, you can include external libraries in your PCF component. However, be mindful of the bundle size. Large libraries will increase the initial load time of your app. For most use cases, modern vanilla JavaScript or React is sufficient.
Q: How do I handle date/time formatting?
A: Never manually format dates. Always use the getFormattedValue method on the dataset record or the property. This ensures that the date is displayed according to the user's regional settings and language preferences defined in their Power Apps profile.
Q: Why is my updateView being called multiple times?
A: The framework calls updateView whenever the context changes. This includes property changes, dataset updates, user interactions, or window resizing. This is expected behavior. If you are doing expensive operations, use a flag to track if the data has actually changed before re-processing.
Q: What is the difference between bound and input usage?
A: bound properties are two-way; your component can read the value and the framework will write it back to the data source. input properties are one-way; your component can read the value provided by the host, but you cannot push changes back to the host.
Key Takeaways
- Manifest is the Foundation: Your
ControlManifest.Input.xmlis the contract that enables communication. Precision here prevents runtime errors and unexpected behavior. - Lifecycle Management: Master the lifecycle methods (
init,updateView,getOutputs,destroy). Understanding when these are called is the difference between a high-performance component and one that crashes the browser. - Use Datasets for Collections: Never try to force multiple records into a single property. Use the
IDataSetinterface to handle tabular data, which provides built-in support for paging, sorting, and selection. - Performance Matters: Always be mindful of the main thread. Avoid heavy operations in
updateView, and always implement proper cleanup in thedestroymethod to prevent memory leaks. - User Experience Consistency: Use
getFormattedValueto ensure that data displays correctly according to the user's locale. Your custom component should respect the look and feel of the platform. - Idempotency is Key: Ensure that your rendering logic can be executed multiple times with the same input without causing side effects. This is crucial for the stability of your component within the Power Apps framework.
- Respect the Architecture: Follow the separation of concerns. Keep your data logic separate from your UI logic to make your code easier to debug, test, and maintain as your component grows in complexity.
By following these principles, you move beyond simple UI customization and into the realm of building professional-grade, scalable extensions that empower users to interact with their data in meaningful, efficient ways. The PCF framework is a powerful tool, and with a solid grasp of inputs, outputs, and datasets, you are fully equipped to build components that solve real-world business problems.
Continue the course
Enjoying the courses?
Everything stays free. Pro shows fewer ads, doubles your daily points limit so you progress twice as fast, and lets you read each lesson on one page.
- ✓ Fewer advertisements
- ✓ 2× daily points limit
- ✓ Distraction-free lessons