View this page in the latest version of Appian. Expand/Collapse Rows in a Tree Grid Share Share via LinkedIn Reddit Email Copy Link Print On This Page Tip: Interface patterns give you an opportunity to explore different interface designs. Be sure to check out How to Adapt a Pattern for Your Application. Goal Create a grid that shows hierarchical data and allows users to dynamically expand and collapse rows within the grid. This scenario demonstrates: How to use the rich text display component inside an editable grid to create a tree grid. How to use a rich text display component to create a dynamic link used to expand and collapse the rows in an editable grid. How to create a rich text bulleted list within a rich text display component inside each collapsible row in the tree grid. Setup For this recipe, you'll need two Data Store Entities that are populated with data: Create a custom data type called PurchaseRequest with the following fields: id (Number (Integer)) summary (Text) Designate the id field as the primary key and set to generate value. See also: Primary Keys Save and publish the CDT. Create a custom data type called PurchaseRequestItem with the following fields: id (Number (Integer)) summary (Text) qty (Number (Integer)) unitPrice (Number (Decimal)) purchaseRequest (PurchaseRequest) Designate the id field as the primary key and set to generate value. Save and publish the CDT. Create a Data Store called "Purchase Request" with two entities, one of each data type that was just created: PurchaseRequests (PurchaseRequest) PurchaseRequestItems (PurchaseRequestItem) Insert the following values into PurchaseRequest: id summary 1 PR 1 2 PR 2 Insert the following values into PurchaseRequestItem: id summary qty unitPrice purchaseRequest.id 1 Item 1 2 10 1 2 Item 2 3 50 1 3 Item 3 1 100 1 4 Item 4 3 75 2 5 Item 5 10 25 2 Now that we have the data, let's create a couple of supporting constants: PR_ENTITY: A constant of type Data Store Entity with value PurchaseRequests. PR_ITEM_ENTITY: A constant of type Data Store Entity with value PurchaseRequestItems. Now that we have created all of the supporting objects, let's move on to the main expression. Expression 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 a!localVariables( local!prs: a!queryEntity( entity: cons!PR_ENTITY, query: a!query( /* To return all fields, leave the selection parameter blank. `*/ /*`If you are not displaying all fields, use the selection `*/ /*` parameter to only return the necessary fields */ pagingInfo: a!pagingInfo(startIndex: 1, batchSize: -1) ) ).data, local!items: a!queryEntity( entity: cons!PR_ITEM_ENTITY, query: a!query( pagingInfo: a!pagingInfo(startIndex: 1, batchSize: -1) ) ).data, a!gridLayout( headerCells: { a!gridLayoutHeaderCell(label: "Summary"), a!gridLayoutHeaderCell(label: "Qty", align: "RIGHT"), a!gridLayoutHeaderCell(label: "Unit Price", align: "RIGHT"), a!gridLayoutHeaderCell(label: "Total Price", align: "RIGHT") }, columnConfigs: { a!gridLayoutColumnConfig(width: "DISTRIBUTE", weight: 4), a!gridLayoutColumnConfig(width: "DISTRIBUTE"), a!gridLayoutColumnConfig(width: "DISTRIBUTE", weight: 2), a!gridLayoutColumnConfig(width: "DISTRIBUTE", weight: 2) }, rowHeader: 1, rows: a!forEach( items: local!prs, expression: a!localVariables( local!expanded: false, local!itemsForPr: index( local!items, /* Must cast to integer because a!queryEntity() returns a dictionary */ wherecontains(tointeger(fv!item.id), local!items.purchaseRequest.id), {} ), local!totalPrice: sum( a!forEach( items: local!itemsForPr, expression: tointeger(fv!item.qty) * todecimal(fv!item.unitPrice) ) ), { a!gridRowLayout( contents: { a!richTextDisplayField( label: "Summary " & fv!index, value: { if( length(local!itemsForPr)=0, fv!item.summary, a!richTextItem( text: if(local!expanded, "-", "+") &" "& fv!item.summary, link: a!dynamicLink( value: not(local!expanded), saveInto: local!expanded ) ) ) } ), a!textField( label: "Qty " & fv!index, readOnly: true ), a!textField( label: "Unit Price " & fv!index, readOnly: true ), a!textField( label: "Total Price " & fv!index, value: dollar(local!totalPrice), readOnly: true, align: "RIGHT" ) } ), if( local!expanded, a!forEach( items: local!itemsForPr, expression: a!gridRowLayout(contents: { a!richTextDisplayField( label: "Item Summary " & fv!index, value: a!richTextBulletedList( items: fv!item.summary ) ), a!integerField( label: "Item Qty " & fv!index, value: fv!item.qty, readOnly: true, align: "RIGHT" ), a!textField( label: "Item Unit Price " & fv!index, value: dollar(fv!item.unitPrice), readOnly: true, align: "RIGHT" ), a!textField( label: "Item Total Price " & fv!index, value: dollar(tointeger(fv!item.qty) * todecimal(fv!item.unitPrice)), readOnly: true, align: "RIGHT" ) }) ), {} ) } ) ) ) ) Test it out Click on + PR 1 in the Summary column to expand to show the item rows corresponding to PR 1. Click on - PR 1 to hide the item rows for PR 1 again. The same can be done for PR 2. Notable implementation details Notice that we used a rich text display component to create a dynamic link used to expand and collapse the item rows for each purchase request. Alternatively, we could have used a link component containing the same dynamic link. The rich text display component would be useful here if a rich text style (e.g. underline) needed to be applied to the purchase request summary or if the summary needed to be a combination of links and normal text. The bullet appearing in front of each item summary is made possible by using a rich text bulleted list within a rich text display component. See also: Rich Text We left the selection parameter blank in our a!query()function because we wanted to return all fields of the entities that we were querying. Feedback Was this page helpful? SHARE FEEDBACK Loading...