Add a Custom Page Modal Dialog to Your Model-Driven App
Learn how to build a modal input dialog using Power Apps custom pages, letting users edit a field on a form without navigating away from the model-driven app context.
Custom pages in Power Apps let you embed canvas app functionality directly into a model-driven app. One common need is a modal dialog that appears on top of a form to collect or display extra information. In this article, you’ll learn how to create such a dialog with a custom page, wire it to a Dataverse record, and trigger it from a command bar button using a small JavaScript snippet.
Scenario: Quick Resolution Notes for Support Tickets
Consider a help desk app built on Dataverse. Agents work on Customer Issues records and need to update the Resolution Notes field. Instead of navigating to another tab or form, they can click a toolbar button, enter the notes in a pop-up dialog, and save – all while staying on the same record.
The custom page will:
- Receive the current issue’s unique ID from the model-driven app.
- Load the record and display the existing resolution notes.
- Provide a text input for editing and two buttons (OK and Cancel).
- Save changes and close the dialog on OK, or simply close on Cancel.
Building the Custom Page
Start inside your model-driven app solution. From the maker portal, edit the app in the modern designer.
1. Add a Custom Page
Click Add Page, select Custom page, then choose Create new custom page. Give it a name like ResolutionDialog. After creation, the custom page opens in the canvas designer.
2. Adjust Display Settings
A custom page normally opens full‑screen. To make it a small dialog, set the following in the Advanced Settings > Display:
- Width:
600 - Height:
300 - Scale to fit: off
Also change the screen’s MinScreenWidth and MinScreenHeight to 200 so it stays compact on smaller devices.
3. Capture the Record ID from the URL
The model-driven app will pass the record ID as a query parameter (?recordId={GUID}). Add the following code to the App object’s OnStart property. It strips the curly braces and converts the value to a GUID, then fetches the record.
Set(
gblRecordId,
GUID(
Mid(
Param("recordId"),
2,
Len(Param("recordId")) - 2
)
)
);
Set(
gblRecordData,
LookUp(
'Customer Issues',
'Customer Issue' = gblRecordId
)
)
Make sure the Customer Issues table is added as a data source (via the Data pane).
4. Design the Modal Layout
Add a Vertical container as the main holder. Set its X and Y to 0, Width to Parent.Width, and Height to Parent.Height. Use these layout properties:
- LayoutJustifyContent:
LayoutJustifyContent.Start - LayoutAlignItems:
LayoutAlignItems.Stretch
Inside the container, add a Text input (modern control). Set its Value property to display the current resolution notes when the app is in Play mode:
gblRecordData.ResolutionNotes
Ensure the text input’s Align in container is “Set by container” so it stretches to fill available space.
5. Add OK and Cancel Buttons
Below the text input, add a Horizontal container for the buttons. Set its Justify to End and Align to Top.
Add a button labeled OK. In its OnSelect, save the text input value back to the record and close the dialog:
Patch(
'Customer Issues',
gblRecordData,
{ResolutionNotes: ResolutionInput.Text}
);
Back()
Add a second button labeled Cancel. Its OnSelect simply closes the dialog without saving:
Back()
Save and publish the custom page, then close the canvas designer.
Connecting the Model-Driven Form
Now you’ll add a command bar button that opens the custom page as a modal dialog.
6. Edit the Form Command Bar
In the model‑driven app designer, find the Customer Issues table. Click the three dots next to it and select Edit command bar. Choose Main form.
In the ribbon designer, click New > Command. Give the command a label (e.g., “Update Resolution”) and pick an icon.
7. Add a JavaScript Web Resource
Set the Action dropdown to Run JavaScript, then click Add Library. Choose New web resource and create a JavaScript file (extension .js). Name it something like crXXX_openResolutionDialog.js.
Replace the placeholder content with the code below. Update the name property to your custom page’s logical name (found in the Default Solution under Pages – look for the Name column).
function openResolutionDialog(primaryControl, firstSelectedItemId, selectedEntityTypeName) {
var pageInput = {
pageType: "custom",
name: "crXXX_resolutiondialog_XXXXX", // Replace with your custom page logical name
entityName: selectedEntityTypeName,
recordId: firstSelectedItemId
};
var navigationOptions = {
target: 2,
position: 1,
height: { value: 260, unit: "px" },
width: { value: 50, unit: "%" },
title: "Update Resolution Notes"
};
Xrm.Navigation.navigateTo(pageInput, navigationOptions).then(
function () {
primaryControl.data.refresh();
}
).catch(
function (error) {
console.error(error);
}
);
}
Save and publish the web resource. Back in the command properties, select the function openResolutionDialog and map the required parameters (firstSelectedItemId and selectedEntityTypeName – the designer usually provides them). Save the command and publish the model‑driven app.
Security & Performance Considerations
- Data access – The custom page runs under the current user’s identity. Dataverse roles and privileges apply automatically to
LookUpandPatchoperations. - Performance – Keep the custom page lightweight. Avoid heavy formulas and complex controls inside the modal to ensure it opens quickly.
- Delegation –
LookUpis delegable for exact match lookups by GUID; no delegation issues here because you’re filtering on the primary key.
Common Mistakes & Troubleshooting
| Problem | Likely Cause | Solution |
|---|---|---|
| Dialog doesn’t open | Wrong logical name in pageInput.name | Double‑check the custom page’s logical name in the solution’s Pages list. |
| “Record not found” error | Table not added as a data source in the custom page | Open the custom page and add the table via the Data pane. |
| Curly braces remain in the GUID | Param("recordId") not properly parsed | Verify the Mid formula in OnStart – it should strip the first and last characters. |
| Form doesn’t refresh after closing | Missing primaryControl.data.refresh() or promise not executed | Ensure the then callback contains the refresh call. |
Final Recommendation
Custom page modal dialogs are a powerful way to keep users inside the context of a model-driven form while providing focused, transactional input. The pattern works for any scenario where you need a simple pop‑up: editing a single field, confirming an action, or displaying extra details. Keep the dialog small, the code clean, and always test with a variety of screen sizes.
References
- Original article: Power Apps Custom Page Modal Dialog For Model-Driven Apps by Matthew Devaney
- Microsoft Learn: Add a custom page to a model-driven app
- Microsoft Learn: Xrm.Navigation.navigateTo
Learn how small coding practices—like using descriptive names, flattening conditions, and simplifying logic—can make your apps easier to update and less error-prone.
Move past the gallery and discover the hidden patterns for validation, navigation, and smart submission handling in your data entry forms.
PowerShell unlocks admin capabilities that the Power Platform admin center simply doesn’t offer—from recovering deleted apps to blocking trial licenses. Here’s how to wield them safely.