Power Apps Custom Page Study Guide
All articles from this blog can ONLY be redistributed on an Attribution-NonCommercial-NoDerivs basis. Please credit the source, thank you.
Twitter:@kelvinshen
Blog:Kelvin Shen's Blog
What Is a Power Apps Custom Page?
Custom pages in model-driven apps provide the convergence of Canvas app with MDAs.
It maybe not a perfect analogy, but sometimes I think it is a low-code alternative to traditional web resources, enabling more flexible and capability to user experience.
Custom Pages vs Embeded Canvas App
Custom pages are generally recommended for new developments within model-driven apps due to their enhanced integration, performance benefits, loose coupling, and Application Lifecycle Management (ALM) compatibility. While they do not outright replace embedded canvas apps, custom pages offer significant advantages for new projects.
Frontend
UI Integration
You can open a custom page as an inline page, a centered dialog, a side dialog.
Layout
Containers in custom pages function similarly to “div” elements in HTML. There are three types of containers:
Styles
Styling options for custom pages are currently limited, but improvements are anticipated. While working on a custom page, I notice the theme tab, and I will explore it in future blog posts.
Custom Page Life Cycle
App.OnStart
The app-level OnStart event occurs at the very start of loading the custom page, making it an ideal place for parsing input parameters.
Set(RecordItem, If(IsBlank(Param("recordId")),
First(Accounts),
LookUp(Accounts, accountid = GUID(Param("recordId"))))
)
Screen.OnVisible
Screen.OnVisible (the per screen level) is more performant than App.OnStart (app level).
Power Fx
While Power Apps formulas and Power Fx share the same underlying syntax and functionality, Power Fx is the formal, standardized, and open-source version of the formula language. Power Fx aims to extend the capabilities and consistency of the formula language across the entire Power Platform, making it a core component of Microsoft’s low-code strategy.
Null Checking
-
IsBlank: Check if a single value variable is empty.
-
ISEmpty: Check if a table has no rows.
String Manipulation
DataTime value to string
Text(varNextEvent.'Scheduled Start', "dd/mm/yyyy"))
String Replacement
Substitute(varSelectedTemplateText, "[Next Event Duration]", varNextEvent.Duration)
Special Data Types
Choice
How to display a Choice data field as text?
varEventRecord.'Event Status'; // 'Event Status' is an option field.
How to render a Choice data field as a radio button group or dropdown list?
rdgEventStatus.Item = Choices('Events'.'Event Status')
Reference: https://powerusers.microsoft.com/t5/Building-Power-Apps/Global-Option-Set-on-Custom-Pages/td-p/2085404
How to filter base on a dropdown selection?
Notify(LookUp(Choices('Events'.'Event Status'), Value = 545360001).Value);
Data Operations
- Filter Function: Finds records in a table that satisfy a formula, discarding non-matching records. For example,
Filter(IceCream, OnOrder > 0)
returns records where OnOrder is greater than zero.
-
LookUp Function: Retrieves the first record that meets specified criteria.
-
Search Function: Identifies records containing a string within one of their columns, using case-insensitive matching.
- Delegation Power Apps may delegate filter and sort operations to the data source, improving app start time and handling large data sets.
Search within a Complex Data Structure
You can combine collections to create more complex data structures:
ClearCollect(ContactsCollection, Contacts);
ClearCollect(CompaniesCollection, Companies);
ClearCollect(CasesCollection,
AddColumns(
Cases,
"ParentCompanyCountry",
LookUp(CompaniesCollection, CompanyID = LookUp(ContactsCollection, ContactID = ThisItem.ContactID).ParentCompanyID).Country
)
);
Limitations
So for, PowerFx doesn’t support FetchXML data operations in Custom Pages.
Data Table Operations
AddColumn
Use this to add calculated columns, similar to a table join.
ShowColumns
Use the ShowColumns function to limit the amount of columns returned. This is good for performance as well.
ClearCollect(
colParties,
Filter(
ShowColumns(
'Event parties',
Name,
Event,
),
ThisRecord.kys_event.'Event (kys_eventid)' = varEvent.'Event (kys_eventid)' && !IsBlank(ThisRecord.kys_eventid)
)
);
Refresh
The Refresh function is important to keep your data up to date. However, it will NOT refresh your data collection. For that, you will need to use ClearCollection function.
Data Table Identifiers
The “@” character
The @ character in Power Apps is used for disambiguation. It’s used when you want to refer to a specific data source or table, especially when there’s a column in the current context with the same name.
For example, [@’Investment Outcomes’] is used to explicitly refer to the ‘Investment Outcomes’ data source or table, not a column.
Field Identifiers
PowerFx can use both logic names or display names as field identifiers. Since display names are not unique, the same display name may appear more than once in the same entity. When this happens, the logical name will be added to the end of the display name in parentheses for one of more of the conflicting names, for example, ‘Custom Field (cra3a_customfieldalt)’.
Variables
Power Fx has two types of variables:
- Global Variable: Holds values, established using the Set function.
- Collection: Holds tables, controlled with Collect and ClearCollect functions.
Looping
Use ForAll
for looping through records.
JSON String Parsing
You can use the ParseJSON
function to convert a JSON string to untyped object.
To covert an object to a JSON string, you will have to resort to the Text
function.
Error Handling
IfError
IfError(
Collect(MyCollection, Filter(MyDataSource, Condition)),
Notify("An error occurred while collecting data", NotificationType.Error)
)
Combine IfError and Logging
Set(OperationSuccessful, true);
Set(LogMessage, "Starting data collection");
IfError(
ClearCollect(MyCollection, Filter(MyDataSource, Condition)),
Set(OperationSuccessful, false);
Set(LogMessage, "Error during data collection");
Notify("An error occurred while collecting data", NotificationType.Error)
);
If(
OperationSuccessful,
// Proceed with next steps
Set(LogMessage, "Data collection successful");
Notify("Data collection successful", NotificationType.Success),
// Handle failure
Set(LogMessage, "Stopping due to error")
);
Notification and Validation
Notify("Notification Message", NotificationType.Information);
Navigation
You can open a custom page with or without a record context. For example, to open it from a model driven app with a record context using D365 client SDK
// Inline Page
var pageInput = {
pageType: "custom",
name: "<logical name of the custom page>",
entityName: "<logical name of the table>",
recordId: "<record id>",
};
var navigationOptions = {
target: 1
};
Xrm.Navigation.navigateTo(pageInput, navigationOptions)
.then(
function () {
// Called when page opens
}
).catch(
function (error) {
// Handle error
}
);
Inside the custom pages,
Set(RecordItem,
If(IsBlank(Param("recordid")),
Blank(),
LookUp(Accounts, Account = GUID(Param("recordid")))
)
)
Backend > Connectors
Power Platform has a raft of connector for data integration with various systems.
Debugging and Monitoring
Pop-up
Using Notify formula is a quick way to peek into values in power page run time.
Logging
If you want to do something similar to console.log(“log a message”) in the custom page world, you can log information in either a variable or a collection. Then their value change will be monitored in Power Apps Monitor. Of course, you can also add an UI component to display the logs.
OnSelect = Collect(DebugLog, {Message: "Button clicked", Detail: MyVariable, Timestamp: Now()})
Tracing
Power Apps provides a Monitor tool to help troubleshoot and debug apps by capturing events and data operations.
Code Reuseability
While custom pages do not support traditional function calls for code reusability, you can trigger other button actions using the Select
function. For example:
radiobuttonCalculatorMode.OnChange = Select(btnShowCalculator)
Please note that the Select
function is excuted asynchronously. So, if execution order is important, you need to control it inside the button.
Tip: You can keep the btnShowCalculator button hidden and use it solely for encapsulating logic.
Publish
Publish the hosting model driven app is not not enough. You must publish the custom page independently.
Case studies
Consider a recruitment company conducting speed-dating style interviews requiring a session-based application. Custom pages can offer a more tailored UI than standard model-driven apps.
Another example is a client seeking a re-imagined business process flow progress bar with custom visual indicators.
Reference
- https://learn.microsoft.com/en-us/power-apps/maker/model-driven-apps/page-powerfx-in-model-app
- https://learn.microsoft.com/en-us/power-platform/power-fx/operators