Hi,
I have editable grid having two columns one is readonly field for showing label and in the second column the user will enter the data. The data for this columns is getting from a local variable . The user can delete the rows and he add row upto 5 rows.
Now I have a cdt with fields like r1, r2, r3, r4, r5. Now I want to map the data entered by the user to cdt.
Discussion posts and replies are publicly visible
This is a fairly straightforward case for an editable grid. If you're using 17.2 or above, you can also do it without even needing to declare a sub-rule (at least for starters). See below example.
load( local!data: { { id: 1, label: "r1", value: "" }, { id: 2, label: "r2", value: "" } }, a!sectionLayout( label: "Test Editable Grid", contents: { a!paragraphField( label: "DEBUG", showWhen: false(), value: local!data, disabled: true() ), a!gridLayout( headerCells: { a!gridLayoutHeaderCell(label: "Label"), a!gridLayoutHeaderCell(label: "Value") }, rows: a!forEach( local!data, a!gridRowLayout( contents: { a!textField( value: fv!item.label, readOnly: true() ), a!textField( value: fv!item.value, saveInto: fv!item.value ) } ) ), addRowLink: if( length(local!data) < 5, a!dynamicLink( label: "Add Row", saveInto: a!save( local!data, append( local!data, { id: max(local!data.id)+1, label: "r" & max(local!data.id)+1, value: null() } ) ) ), null() ) ) } ) )
My requirement is to create dynamic sections with grid layout based on the dropdown selection.Columns in grid:- Question(Read Only)- Order No (Editable) - On load ,the order no field is disabled.Once the row is selected, the order no field will be enabled and the value will be saved in cdt variable.
I am facing issues when saving the order no values into cdt. Each row's order number field value overwrites when other row is selected.
Thanks in advance!
Interesting use case. Some follow-up questions from me:
Hi Mike,
Thanks for your reply. Please find the answers below:
Thanks for your reply - before I dig into your code I have a suggestion/request - maybe you could edit your post and put your sample code in a Code box - use the "insert" tool on the toolbar of the editor then choose "insert code", and paste your code there instead - that allows it to maintain formatting as well as not extending your comment window to extreme lengths. Thanks!
load( local!selectedSections: { { "sectionId": 1, "sectionName": "Section1" }, { "sectionId": 2, "sectionName": "Section2" } }, local!selectedSectionQuestions: { { "pId": 1, "question": "e1", "orderNo": null, "refSectionId": 1 }, { "pId": 2, "question": "e2", "orderNo": null, "refSectionId": 1 }, { "pId": 3, "question": "p1", "orderNo": null, "refSectionId": 2 }, { "pId": 4, "question": "p2", "orderNo": null, "refSectionId": 2 }, { "pId": 5, "question": "p3", "orderNo": null, "refSectionId": 2 } }, local!selectedQuestions:type!question_cdt(), with( {a!forEach( items: local!selectedSections, expression: rule!TEST_uiDisplayQuestion( questions: local!selectedSectionQuestions, showWhen: 1, sections: fv!item, selectedSectionQuestions: local!selectedQuestions ) )} ) ) -------------- TEST_uiDisplayQuestion load( local!questions: index( ri!questions, wherecontains( tointeger( ri!sections.sectionId ), tointeger( ri!questions.refSectionId ) ), {} ), local!sequence: a!forEach( items: local!questions, expression: null ), local!selectedIndices: tointeger( {} ), { a!sectionLayout( label: ri!sections.sectionName, contents: { a!gridLayout( label: "", headerCells: { a!gridLayoutHeaderCell( label: "Available Questions" ), a!gridLayoutHeaderCell( label: "Order No" ) }, columnConfigs: { a!gridLayoutColumnConfig( width: "DISTRIBUTE", weight: 1 ), a!gridLayoutColumnConfig( width: "NARROW" ) }, rows: a!forEach( items: local!questions, expression: a!gridRowLayout( id: fv!item.pId, contents: { a!paragraphField( label: "available questions", labelPosition: "ABOVE", value: fv!item.question, readOnly: true ), a!integerField( label: "orderNo", labelPosition: "ABOVE", value: fv!item.orderNo, saveInto: { fv!item.orderNo, local!sequence[fv!index] }, disabled: rule!APN_isBlank( wherecontains( tointeger( fv!item.pId ), tointeger( local!selectedIndices ) ) ), required: if( contains( tointeger( local!selectedIndices ), wherecontains( local!selectedIndices, tointeger( fv!item.pId ) ) ), true, false ) ) } ) ), selectionValue: local!selectedIndices, selectionSaveInto: { local!selectedIndices, a!save( local!selectedIndices, reject( fn!isnull( _ ), save!value ) ), a!forEach( items: local!questions, expression: { a!save( local!sequence[wherecontains( tointeger( fv!item.pId ), tointeger( local!selectedIndices ) )], if( contains( tointeger( local!selectedIndices ), wherecontains( tointeger( fv!item.pId ), tointeger( local!selectedIndices ) ) ), local!sequence[wherecontains( tointeger( fv!item.pId ), tointeger( local!selectedIndices ) )], null ) ) } ), a!save( ri!selectedQuestions, index( local!questions, wherecontains( tointeger( local!selectedIndices ), tointeger( local!questions.pId ) ), {} ) ) }, selectable: true, selectionRequired: true ) } ) } )
BTW, I have reproduced a partially-simplified version of your use case using my own new code. In the case of this sample code it seems to work when I enter order numbers. Please try this out and see if it helps you with your specific implementation.
load( local!groceryList: { { id: 1, name: "Milk", orderNum: null(), category: "Food" }, { id: 2, name: "Tide Pods", orderNum: null(), category: "Cleaning Supplies" }, { id: 3, name: "Eggs", orderNum: null(), category: "Food" }, { id: 4, name: "Paper Towels", orderNum: null(), category: "Cleaning Supplies" } }, local!categories: union(local!grocerylist.category, local!groceryList.category), local!selectedItems: {}, a!sectionLayout( contents: { a!forEach( local!categories, with( local!currentCategory: fv!item, a!gridLayout( label: fv!item, headerCells: { a!gridLayoutHeaderCell(label: "Item"), a!gridLayoutHeaderCell(label: "Order") }, selectable: true(), selectionSaveInto: { local!selectedItems }, selectionValue: local!selectedItems, /*selectionStyle: "ROW_HIGHLIGHT",*/ rows: a!forEach( local!groceryList, if( fv!item.category = local!currentCategory, a!gridRowLayout( id: fv!item.id, contents: { a!richTextDisplayField( label: "Item Name Display", value: a!richTextItem( text: fv!item.name ) ), a!textField( label: "Order Number Editor", value: fv!item.orderNum, saveInto: { fv!item.orderNum }, disabled: not( contains( local!selectedItems, fv!item.id ) ) ) } ), {} ) ) ) ) ), a!boxLayout( label: "DEBUG", style: "INFO", isCollapsible: true(), isInitiallyCollapsed: true(), contents: { a!paragraphField( value: "Selections: " & local!selectedItems & char(10) & "CDT Data: " & local!groceryList ) } ) } ) )
Thanks!I tried the new code but grid selection is not retained when i select multiple sections.
Did you change anything from my example? I have to double check but it seemed to work for me no matter which section I selected items from. What happens wrong exactly? Also can you confirm what Appian version you're on?
I am on Appian version 18.4.I was getting this error ( Expression evaluation error at function 'contains' [line 67]: Invalid types, can only act on data of the same type (Any Type, Number (Integer)). So did casting.There were two sections Food and Cleaning supplies.First I selected all items under food Section and enter the order no .When I selected the second section Cleaning supplies , the selection made in Food section lost and only the selection in second grid retained.
load( local!groceryList: { { id: 1, name: "Milk", orderNum: null(), category: "Food" }, { id: 2, name: "Tide Pods", orderNum: null(), category: "Cleaning Supplies" }, { id: 3, name: "Eggs", orderNum: null(), category: "Food" }, { id: 4, name: "Paper Towels", orderNum: null(), category: "Cleaning Supplies" } }, local!categories: union(local!grocerylist.category, local!groceryList.category), local!selectedItems: {}, a!sectionLayout( contents: { a!forEach( local!categories, with( local!currentCategory: fv!item, { a!gridLayout( label: fv!item, headerCells: { a!gridLayoutHeaderCell(label: "Item"), a!gridLayoutHeaderCell(label: "Order") }, selectable: true(), selectionSaveInto: { local!selectedItems }, selectionValue: local!selectedItems, selectionStyle: "ROW_HIGHLIGHT", rows: a!forEach( local!groceryList, if( fv!item.category = local!currentCategory, a!gridRowLayout( id: fv!item.id, contents: { a!richTextDisplayField( label: "Item Name Display", value: a!richTextItem( text: fv!item.name ) ), a!textField( label: "Order Number Editor", value: fv!item.orderNum, saveInto: { fv!item.orderNum }, disabled: not( contains( tointeger(local!selectedItems), tointeger(fv!item.id) ) ) ) } ), {} ) ) ), a!buttonLayout( secondaryButtons: { a!buttonWidget( label:"Select All", saveInto: a!save( local!selectedItems, append( local!selectedItems, index( local!groceryList.id, wherecontains(local!currentCategory,touniformstring(property(local!groceryList, "category", {}))), {} ) ) ) ), a!buttonWidget( label:"Deselect All", saveInto: a!save( local!selectedItems, remove( local!selectedItems, wherecontains( index( local!groceryList.id, wherecontains(local!currentCategory,touniformstring(property(local!groceryList, "category", {}))), {} ), local!selectedItems ) ) ) ) } ) } ) ), a!boxLayout( label: "DEBUG", style: "INFO", isCollapsible: true(), isInitiallyCollapsed: true(), contents: { a!paragraphField( value: "Selections: " & local!selectedItems & char(10) & "CDT Data: " & local!groceryList ) } ) } ) )
chandhinir One alternative solution to this issue is to add & change the selectionStyle to 'ROW_HIGHLIGHT" and add SELECT/DE-SELECT ALL buttons below the grid(s) using the design Mike has already provided (which includes my changes - see code above). With the addition of the buttons, Mike's solution worked fine for me.It may also be helpful to add instructions so that users are made aware to literally select the rows they want to enable.I didn't see any viable solutions (in terms of complexity) that would work out when the checkbox to select all rows is de-selected, so I figured the above solution would be fine.One quirk you'd have to address though:1. When the user accidentally clicks the select all button but all the available rows, for the given grid, have already been selected (you'll understand if you refer to the debug field)
Note: Just so you're aware, I am also using 18.4
Thanks Reggie!
Yeah, it turns out things get a little more tricky if you start using the "select all" checkboxes, which I was not originally testing as I was selecting individual rows by default.
Please note that using the "select all" checkbox in one grid seems to deselect all items from the other grid as they both use a shared selected items list. But manually checking individual items, it should work fine to select different items from different sections. The following revision of my original code from above does some more careful typecasting on the indexes to avoid the error message you had reported even when the Select All boxes are utilized.
load( local!groceryList: { { id: 1, name: "Milk", orderNum: null(), category: "Food" }, { id: 2, name: "Tide Pods", orderNum: null(), category: "Cleaning Supplies" }, { id: 3, name: "Eggs", orderNum: null(), category: "Food" }, { id: 4, name: "Paper Towels", orderNum: null(), category: "Cleaning Supplies" } }, local!categories: union(local!grocerylist.category, local!groceryList.category), local!selectedItems: tointeger({}), a!sectionLayout( contents: { a!forEach( local!categories, with( local!currentCategory: fv!item, a!gridLayout( label: fv!item, headerCells: { a!gridLayoutHeaderCell(label: "Item"), a!gridLayoutHeaderCell(label: "Order") }, selectable: true(), selectionSaveInto: { a!save( local!selectedItems, a!forEach( save!value, if(isnull(fv!item), {}, fv!item) ) ) }, selectionValue: local!selectedItems, /*selectionStyle: "ROW_HIGHLIGHT",*/ rows: a!forEach( local!groceryList, if( fv!item.category = local!currentCategory, a!gridRowLayout( id: tointeger(fv!item.id), contents: { a!richTextDisplayField( label: "Item Name Display", value: a!richTextItem( text: fv!item.name ) ), a!textField( label: "Order Number Editor", value: fv!item.orderNum, saveInto: { fv!item.orderNum }, disabled: not( contains( tointeger(local!selectedItems), tointeger(fv!item.id) ) ) ) } ), {} ) ) ) ) ), a!boxLayout( label: "DEBUG", style: "INFO", isCollapsible: true(), isInitiallyCollapsed: true(), contents: { a!paragraphField( value: "Selections: " & local!selectedItems & char(10) & "CDT Data: " & local!groceryList ) } ) } ) )