Nested Grid

Hi Experts,

I have a user requirement for which I need to create a Nested Grid. Structure is as given below

{
{
Index:1,
{ id: 1, text: "abc", fee:123},
{ id: 2, text: "def", fee:1123},
},

{
Index:2,
{ id: 1, text: "xyz", fee:123}
{ id: 2, text: "xxx", fee:112},
{ id: 3, text: "yyy", fee:133}
}
}

For this I have created

CDT 1 have fields as below:

where field "grid" is of type cdt 2 which have structure as below

  

Now I have to deign the grid in such a way that I can add multiple editable grid and store the data entered in grid respectively.

I'm trying to use below code snippet but not able to store the values entered in the grid.

a!localVariables(
  local!counter: 0,
  local!grid: ri!multiFeeGrid.grid,
  {
    a!columnsLayout(
      columns: {
        a!columnLayout(
          contents: {
            a!linkField(
              label: "",
              links: a!dynamicLink(
                label: "Add Amendments",
                saveInto: {
                  a!save(local!counter, local!counter + 1),
                  a!save(
                    ri!multiFeeGrid,
                    append(
                      ri!multiFeeGrid,
                      'type!{urn:com:appian:types:CS}CS_multiLicenseFee'(
                        index:local!counter,
                        grid:local!grid
                      )
                    )
                  )
                }
              )
            )
          }
        ),
        a!columnLayout(
          contents: {
            a!linkField(
              label: "",
              links: a!dynamicLink(
                label: "Remove Amendments",
                saveInto: { 
                  a!save(local!counter, local!counter - 1),
                }
              )
            )
          }
        )
      }
    ),
    a!forEach(
      items: enumerate(local!counter),
      expression: {
        a!cardLayout(
          contents: {
            a!sideBySideLayout(
              items: {
                a!sideBySideItem(
                  width: "MINIMIZE",
                  item: a!richTextDisplayField(
                    value: { 
                      a!richTextItem(
                        text: "The fees payable by the Licensee as at the Commencement Date are as follows: ", 
                      )            
                    }
                  )
                )
              }
            ),
            a!columnsLayout(
              columns: {
                a!columnLayout(
                  width: "WIDE",
                  contents: a!gridLayout(
                    label: "",
                    labelPosition: "ABOVE",
                    emptyGridMessage: "No Data Available",
                    headerCells: {
                      a!gridLayoutHeaderCell(label: "Items"),
                      a!gridLayoutHeaderCell(label: "Fee (in $ per year)"),
                      a!gridLayoutHeaderCell(label: ""),

                    },
                    columnConfigs: {
                      a!gridLayoutColumnConfig(width: "DISTRIBUTE", weight: 1),
                      a!gridLayoutColumnConfig(width: "DISTRIBUTE", weight: 1),
                      a!gridLayoutColumnConfig(width: "ICON")
                    },
                    rows: {
                      a!forEach(
                        items: local!grid,
                        expression: a!gridRowLayout(
                          contents: {
                            a!textField(
                              value: fv!item.text,
                              saveInto: { 
                                fv!item.text ,
                                a!save(
                                  ri!multiFeeGrid,
                                  'type!{urn:com:appian:types:CS}CS_multiLicenseFee'(
                                    index:local!counter,
                                    grid:local!grid
                                  )
                                )
                              },
                              refreshAfter: "KEYPRESS",
                              required: true(),

                            ),
                            a!floatingPointField(
                              value: fv!item.fee,
                              saveInto: {                                
                                fv!item.fee,
                                a!save(
                                  ri!multiFeeGrid,
                                  'type!{urn:com:appian:types:CS}CS_multiLicenseFee'(
                                    index:local!counter,
                                    grid:local!grid
                                  )
                                )
                              },
                              refreshAfter: "KEYPRESS",
                              required: true()
                            ),
                            a!richTextDisplayField(
                              value: {
                                a!richTextIcon(
                                  icon: "close",
                                  link: {
                                    a!dynamicLink(
                                      value: fv!index,
                                      saveInto: {
                                        a!save(
                                          local!grid,
                                          remove(local!grid, save!value),                                  
                                        )                         
                                      }
                                    )
                                  },
                                  linkStyle: "STANDALONE",

                                )
                              }
                            )
                          }
                        )
                      )
                    },
                    selectionSaveInto: {},
                    addRowlink: a!dynamicLink(
                      label: "Add Item",
                      value: {
                        'type!{urn:com:appian:types:TCA}LicenseFees'(text: "", fee: null)
                      },
                      saveInto: {
                        a!save(
                          local!grid,
                          append(local!grid, save!value)
                        ),
                        a!save(
                          ri!multiFeeGrid,
                          'type!{urn:com:appian:types:CS}CS_multiLicenseFee'(
                            index:local!counter,
                            grid:local!grid
                          )
                        )
                      }
                    ),
                    validations: {},
                    shadeAlternateRows: true
                  )
                )
              }
            ),
            a!sideBySideLayout(
              items: {
                a!sideBySideItem(
                  width: "MINIMIZE",
                  item: a!richTextDisplayField(
                    value: {
                      "Total License Fees payable annually at the Commencement Date: ",
                      a!richTextItem(
                        text: dollar(
                          if(
                            rule!APN_isBlank(local!grid),
                            0,
                            sum(local!grid.fee)
                          )
                        ),
                        style: "STRONG"
                      ),
                      a!richTextItem(
                        text:", or payment of equal monthly instalments of ",            
                      ),
                      a!richTextItem(
                        text: dollar(
                          if(
                            rule!APN_isBlank(local!grid),
                            0,
                            sum(local!grid.fee)/12
                          )
                        ),
                        style: "STRONG"
                      )
                    }
                  )
                )
              }
            )            
          }
        )
      }
    )
  }
)

  Discussion posts and replies are publicly visible

  • 0
    Certified Lead Developer

    Theoretically this should work (though I wouldn't use the term "nested grid" as it will give people the wrong idea, what you really seem to be after is an array of grids based on the inner contents of a nested CDT).  However I'm really confused by what you're doing with your "ri!multiFeeGrid" when clicking the "Add Amendments" link.  This is weirdly recursive and presumably doesn't account for whether "ri!multiFeeGrid" is an Array (i assume it is?) - in essence it looks as if local!grid is being set to the value of ri!multiFeeGrid but then upon link click you're appending a new copy of the CDT at the end of the array but with the entire contents of the prior value of "local!grid", which at best won't work, and at worst will break horribly. 

    I suggest you take a step back here and analyze what you actually intend this link to do, and retry.   From the looks of it, you should probably totally remove "local!grid" and rework all the places you're trying to use that.

  • +1
    Certified Lead Developer

    I've whipped up this quick and very-slightly-simplified demo that should show how to structure this in general.  The main change you'd need to make is swapping back in your actual nested CDT, instead of my fake local variable (but this way anyone can copy/paste and run this code).

    I believe i've identified a primary potential point of confusion which is, whenever you save into one of the lower row values, you'll need to save it up to the main original CDT value instead of the local copy.  This gets tricky with a nested a!forEach() call, but this can be solved with strategic use of placeholder local variables within each loop.

    a!localVariables(
      
      local!nestedCdt: { /* you will swap out this value with your nested CDT rule input */
        {
          index: 1,
          grid: {
            {id: 1, text: "abc"},
            {id: 2, text: "qwer"}
          }
        },
        {
          index: 2,
          grid: {
            {id: 1, text: "def"},
            {id: 2, text: "zxcv"},
            {id: 3, text: "12345"}
          }
        }
      },
      local!currentCount: max(local!nestedCdt.index), /* this variable should mainly be informative, probably not the active element you loop over */
      local!maxGrids: 6,
      local!fullGrids: local!currentCount >= local!maxGrids,
      
      a!sectionLayout(
        label: "Demonstration Multi-Grid for Nested CDT",
        contents: {
          a!richTextDisplayField(  /* use this instead of a!linkField() */
            value: a!richTextItem(
              text: {
                a!richTextIcon(icon: "plus-circle"),
                " Add Grid " & local!currentCount + 1
              },
              size: "MEDIUM",
              linkStyle: "STANDALONE",
              style: if(local!fullGrids, "EMPHASIS", null()),
              color: if(local!fullGrids, "SECONDARY", null()),
    
              link: a!dynamicLink(
                showWhen: not(local!fullGrids),
                saveInto: {
                  a!save(
                    local!nestedCdt,
                    append(
                      local!nestedCdt,
                      {
                        index: local!currentCount + 1,
                        grid: {}
                      }
                    )
                  )
                }
              )
            )
          ),
          
          a!forEach(
            local!nestedCdt,
            a!localVariables(
              local!currentEntry: fv!item,
              local!parentIndex: fv!index,
              a!gridLayout(
                label: "Grid for Index " & local!currentEntry.index,
                headerCells: {
                  a!gridLayoutHeaderCell(label: "Id"),
                  a!gridLayoutHeaderCell(label: "Text")
                },
                rows: a!forEach(
                  local!currentEntry.grid,
                  a!gridRowLayout(
                    id: local!currentEntry.index,
                    contents: {
                      a!richTextDisplayField(
                        value: fv!item.id
                      ),
                      a!textField(
                        label: "Text Column",
                        value: fv!item.text,
                        saveInto: {
                          /*fv!item.text*/
                          /* the SaveInto here will need to save straight to the parent CDT, not the local copy */
                          a!save(
                            local!nestedCdt[local!parentIndex].grid[fv!index].text,
                            save!value
                          )
                        }
                      )
                    }
                  )
                ),
                addRowLink: a!dynamicLink(
                  label: "Add Row",
                  saveInto: {
                    a!save(
                      fv!item.grid,
                      append(
                        fv!item.grid,
                        {
                          id: if(
                            isnull(property(fv!item.grid, "id", null())),
                            1,
                            max(fv!item.grid.id) + 1
                          ),
                          text: "[new row added]"
                        }
                      )
                    )
                  }
                )
              )
            )
          )
        }
      )
    )