We recently created a Power App with a complex tree view control. Because Power Apps does not provide an out of the box tree view control, we investigated different ways to build one.
There are several ways a tree view can be implemented in Power Apps. After investigating many approaches, we found these patterns work the best.
- Nested flexible-height Gallery controls
- Single flexible-height Gallery control
- Custom Power Apps PCF control (TypeScript)
Nested flexible-height Gallery controls
In this approach we use 2 nested galleries corresponding to the parent and child node levels in the tree view. In this approach, the parent gallery must be a flexible-height gallery. The child gallery is a fixed-height gallery.
Here you can see what the data looks like in the collection bound to the parent Gallery control. This data comes from a CDS entity named Demo Objectives. You will see this entity name in subsequent sections in this article.
Id | Title |
0001 | Create an Awesome Customer Experience |
0002 | Delight our customers |
Here you can see what the data looks like in the collection bound to the child Gallery control. This data comes from a CDS entity named Demo Key Results. You will see this entity name in subsequent sections in this article.
Id | Title | Objective (Lookup) |
0001 | Improve Net Promoter Score from X to Y. | 0001 |
0002 | Increase Repurchase Rate from X to Y. | 0001 |
0003 | Maintain Customer Acquisition cost under Y. | 0001 |
0004 | Reduce revenue churn (cancellation) from X% to Y%. | 0002 |
0005 | Increase Net Promoter Score from X to Y. | 0002 |
0006 | Improve average weekly visits per active user from X to Y. | 0002 |
0007 | Increase non-paid (organic) traffic to from X to Y. | 0002 |
0008 | Improve engagement (users that complete a full profile) from X to Y. | 0002 |
Here are the important parent gallery settings.
Property Name | Property Value | Notes |
Items | ‘Demo Objectives’ | Bind parent collection. |
Here are the important child gallery settings.
Property Name | Property Value | Notes |
Items | Filter([@’Demo Key Results’], Objective.Id = ThisItem.Id) | Filter the child collection based on the parent id. |
TemplateSize | 35 | Row height for each item. |
TemplatePadding | 0 | Set padding to 0 to avoid potential scroll bar inside the child gallery. |
Height | CountRows(galChildGallery.AllItems) * galChildGallery.TemplateHeight | Dynamic height for child gallery based on the items count. |
Notes about this approach:
- Only supports displaying items 2 levels deep.
- All items are the same fixed height.
- In Power Apps design mode, each item in the parent gallery might show a gap below the child gallery. It depends on the TemplateSize settings of the parent gallery. This isn’t an issue and the gap will disappear when the Power App is previewed or run.
Single flexible-height Gallery control
In this approach we added Path, Level and Visible metadata columns to the tree view data source so we can organize the nested items by sorting by the Path. We then calculate the indent for each item with the Level, and expand/collapse the child items with the Path and Visible columns.
Here you can see what the data looks like in the collection bound to the Gallery control. Notice in our example here we added the metadata columns to the same sample data source as the previous example.
Id | Title | Path | Level | Visible |
0001 | Create an Awesome Customer Experience | 0001 | 0 | true |
0001 | Improve Net Promoter Score from X to Y. | 0001.0001 | 1 | true |
0002 | Increase Repurchase Rate from X to Y. | 0001.0002 | 1 | true |
0003 | Maintain Customer Acquisition cost under Y. | 0001.0003 | 1 | true |
0002 | Delight our customers | 0002 | 0 | true |
0004 | Reduce revenue churn (cancellation) from X% to Y%. | 0002.0004 | 1 | true |
0005 | Increase Net Promoter Score from X to Y. | 0002.0005 | 1 | true |
0006 | Improve average weekly visits per active user from X to Y. | 0002.0006 | 1 | true |
0007 | Increase non-paid (organic) traffic to from X to Y. | 0002.0007 | 1 | true |
0008 | Improve engagement (users that complete a full profile) from X to Y. | 0002.0008 | 1 | true |
It’s up to you where you store this data. You can store it directly in the source where your tree data comes from (a view in SQL server, a CDS Entity, etc.). Or, you can query the data for your tree from one place and the data for the path and level in another, then merge them into a single collection in the Power App. That’s up to you to decide what works best for your scenario.
To calculate the indent we use Level * [Step].
ClearCollect(
DemoKeyResultsExtended,
AddColumns(
'Demo Key Results',
"ParentId",
Objective.Id /* workaround to get the parent id first as the associated object could only be loaded in this way */
)
);
/* generate tree nodes hierarchy */
Clear(TreeNodes);
ForAll(
'Demo Objectives',
/* append level 0 nodes */
Collect(
TreeNodes,
{
Level: 0,
Path: Id,
Id: Id,
Title: Title,
Visible: true /* expand by default */
}
);
/* append level 1 nodes */
ForAll(
DemoKeyResultsExtended,
If(
ParentId = 'Demo Objectives'[@Id],
Collect(
TreeNodes,
{
Level: 1,
Path: ParentId & "." & Id,
Id: Id,
Title: Title,
Visible: true /* expand by default */
}
)
)
)
);
/* sort nodes by path */
ClearCollect(
TreeNodes,
Sort(
TreeNodes,
Path,
Ascending
)
);
To expand/collapse the items in the tree we compare the path prefix when the user clicks one of the images associated with a node in the tree, like this:
UpdateIf(
TreeNodes,
Path <> ThisItem.Path && StartsWith(
Path,
ThisItem.Path
),
{
Visible: !Visible
}
);
Notes:
- Supports displaying items more than 2 levels deep.
- All items are the same fixed height.
Custom Power Apps PCF Control (TypeScript)
In this approach we made a PCF Control to read data from the data source and render the tree control.
Notes:
- Supports displaying items more than 2 levels deep.
- In this approach you have complete control of everything and you can create the tree to look however you like.
- You must keep in mind how much data you load and the depth of your tree to ensure it performs well.