Interface: =load( local!items: { {id: null, summary: "Item 1", qty: 1, unitPrice: 10, dept: "Sales", due: today() - 10}, {id: null, summary: "Item 2", qty: 2, unitPrice: 20, dept: "Finance", due: today() + 20}, {id: null, summary: "Item 3", qty: 3, unitPrice: 30, dept: "Sales", due: today() + 30} }, /* Needed when adding or removing items via a!applyComponents `*/ local!itemsToken: updatearray({},1+enumerate(length(local!items)),null), with( a!formLayout( /*label: local!itemsToken,*/ firstColumnContents: { a!textField( label:local!itemsToken, readOnly:true() ), a!gridLayout( headerCells: { a!gridLayoutHeaderCell(label: "Summary"), a!gridLayoutHeaderCell(label: "Qty", align: "RIGHT"), a!gridLayoutHeaderCell(label: "U/P", align: "RIGHT"), a!gridLayoutHeaderCell(label: "Amount", align: "RIGHT"), a!gridLayoutHeaderCell(label: "Department"), a!gridLayoutHeaderCell(label: "Due", align: "RIGHT"), a!gridLayoutHeaderCell(label: "Select", align: "RIGHT"), /* For the "Remove" column */ a!gridLayoutHeaderCell(label: "") }, /* Only needed when some columns need to be narrow */ columnConfigs: { a!gridLayoutColumnConfig(width: "DISTRIBUTE"), a!gridLayoutColumnConfig(width: "NARROW"), a!gridLayoutColumnConfig(width: "NARROW"), a!gridLayoutColumnConfig(width: "DISTRIBUTE"), a!gridLayoutColumnConfig(width: "DISTRIBUTE"), a!gridLayoutColumnConfig(width: "DISTRIBUTE"), a!gridLayoutColumnConfig(width: "NARROW"), a!gridLayoutColumnConfig(width: "NARROW") }, rows: a!applyComponents( function: rule!MPAT_testItemRowEach( items: local!items, index: _, itemsToken: local!itemsToken ), array: if(or(isnull(local!items), count(local!items) < 1), {}, 1+enumerate(count(local!items))), arrayVariable: local!items ) ), a!linkField( label: "Add Link", labelPosition: "COLLAPSED", links: a!dynamicLink( label: "+Add Item", /* * For your use case, set the value to a blank instance of your CDT using * the type constructor, e.g. type!PurchaseRequestItem(). Only specify the field * if you want to give it a default value e.g. due: today()+1. */ value: {due: today() + 1}, saveInto: { a!save(local!items, append(local!items, save!value)), /*` When modifying the size of the array used in a!applyComponents, */ /* make the same change in the "token" array variable */ a!save(local!itemsToken, append(local!itemsToken, null)) } ) ) }, buttons: a!buttonLayout( primaryButtons: a!buttonWidgetSubmit( label: "Submit" ) ) ) ) ) Expression: =a!gridRowLayout( contents: { a!textField( /* Labels are not visible in grid cells but are necessary to meet accessibility requirements `*/ label: "summary " & ri!index, value: ri!items[ri!index].summary, saveInto: ri!items[ri!index].summary, required: true ), a!integerField( label: "qty " & ri!index, value: ri!items[ri!index].qty, saveInto: ri!items[ri!index].qty, validations: if(tointeger(ri!items[ri!index].qty) < 1, "Quantity must be greater than 0", null), align: "RIGHT" ), a!floatingPointField( label: "unitPrice " & ri!index, value: ri!items[ri!index].unitPrice, saveInto: ri!items[ri!index].unitPrice, validations: if(todecimal(ri!items[ri!index].unitPrice) < 1, "Unit price must be greater than 0", null), align: "RIGHT" ), a!textField( label: "amount " & ri!index, value: if( or(isnull(ri!items[ri!index].qty), isnull(ri!items[ri!index].unitPrice)), null, dollar(tointeger(ri!items[ri!index].qty) * todecimal(ri!items[ri!index].unitPrice)) ), readOnly: true, align: "RIGHT" ), a!dropdownField( label: "dept " & ri!index, placeholderLabel: "--Select-- ", choiceLabels: {"Finance", "Sales"}, choiceValues: {"Finance", "Sales"}, value: ri!items[ri!index].dept, saveInto: ri!items[ri!index].dept ), a!dateField( label: "due " & ri!index, value: ri!items[ri!index].due, saveInto: ri!items[ri!index].due, validations: if(todate(ri!items[ri!index].due) < today(), "The due date cannot be in the past", null), align: "RIGHT" ), a!radioButtonField( label: "select " & ri!index, choiceLabels: {""}, choiceValues: {true()}, value: ri!itemsToken[ri!index], saveInto: { a!save(ri!itemsToken, updatearray({},1+enumerate(length(ri!itemsToken)),null)), a!save(ri!itemsToken[ri!index],save!value) } ), a!linkField( label: "delete " & ri!index, links: a!dynamicLink( label: char(10005), value: ri!index, saveInto: { a!save(ri!items, remove(ri!items, save!value)), /*` When modifying the size of the array used in a!applyComponents, */ /* make the same change in the "token" array variable */ a!save(ri!itemsToken, remove(ri!itemsToken, save!value)) } ) ) }, id: ri!index )