Skip to main content
Known Participant
Apr 28, 2021 6:12 PM UTC
Solved

Merged or Nested column headers in an editable grid

Has anyone figured out a good way to emulate nested/merged column headers in an editable grid?  I have attached an image to show what I mean.

As far as I know, there is no OOTB way to do this, but it must be a relatively common requirement.  Any suggestions would be greatly appreciated!

csteward
cstewardAnswer
Brainy
May 13, 2021 5:47 PM UTC

You can try playing around with cards inside a!columnsLayout() above the grid, this will get you closest while still using the standard editable grid component:

a!localVariables(
  local!data: {
    {id: 1, text1: null, text2: null, text3: null, text4: null, text5: null, text6: null, text7: null, text8: null},
    {id: 2, text1: null, text2: null, text3: null, text4: null, text5: null, text6: null, text7: null, text8: null},
    {id: 3, text1: null, text2: null, text3: null, text4: null, text5: null, text6: null, text7: null, text8: null}
  },
  {
    a!columnsLayout(
      marginBelow: "NONE",
      spacing: "NONE",
      columns: {
        a!forEach(
          items: 1+enumerate(3),
          expression: 
          a!columnLayout(
            contents: {
              a!cardLayout(
                style: "INFO",
                contents: {
                  a!richTextDisplayField(
                    labelPosition: "COLLAPSED", 
                    align: "CENTER",
                    value: a!richTextItem(
                      text: concat("Header ",fv!item),
                      style: "STRONG"
                    )
                  )
                }
              )
            }
          )
        )
      }
    ),
    a!gridLayout(
      labelPosition: "COLLAPSED",
      headerCells: a!forEach(
        items: 1+enumerate(6),
        expression: a!gridLayoutHeaderCell(label: concat("Column ",fv!item), align: "CENTER")
      )
    )
  }
)

8 replies

csteward
Brainy
Apr 28, 2021 8:17 PM UTC

You are correct there is no way to achieve this within the standard grid components OOTB.  However, you can create a custom grid by combining other components, such as below.

a!localVariables(
  local!data: {
    {id: 1, text1: null, text2: null, text3: null, text4: null, text5: null, text6: null, text7: null, text8: null},
    {id: 2, text1: null, text2: null, text3: null, text4: null, text5: null, text6: null, text7: null, text8: null},
    {id: 3, text1: null, text2: null, text3: null, text4: null, text5: null, text6: null, text7: null, text8: null}
  },
  {
    a!columnsLayout(
      marginBelow: "NONE",
      spacing: "NONE",
      columns: {
        a!columnLayout(
          contents: {
            a!cardLayout(
              style: "INFO",
              contents: {
                a!richTextDisplayField(
                  align: "CENTER",
                  labelPosition: "COLLAPSED",
                  value: a!richTextItem(
                    text: "Header 1",
                    style: "STRONG"
                  )
                )
              }
            )
          }
        ),
        a!columnLayout(
          contents: {
            a!cardLayout(
              style: "INFO",
              contents: {
                a!richTextDisplayField(
                  align: "CENTER",
                  labelPosition: "COLLAPSED",
                  value: a!richTextItem(
                    text: "Header 2",
                    style: "STRONG"
                  )
                )
              }
            )
          }
        )
      }
    ),
    a!columnsLayout(
      marginBelow: "NONE",
      spacing: "NONE",
      columns: {
        a!columnLayout(
          contents: {
            a!cardLayout(
              style: "WARN",
              contents: {
                a!richTextDisplayField(
                  align: "CENTER",
                  labelPosition: "COLLAPSED",
                  value: a!richTextItem(
                    text: "Sub Header A",
                    style: "STRONG"
                  )
                )
              }
            )
          }
        ),
        a!columnLayout(
          contents: {
            a!cardLayout(
              style: "WARN",
              contents: {
                a!richTextDisplayField(
                  align: "CENTER",
                  labelPosition: "COLLAPSED",
                  value: a!richTextItem(
                    text: "Sub Header B",
                    style: "STRONG"
                  )
                )
              }
            )
          }
        ),
        a!columnLayout(
          contents: {
            a!cardLayout(
              style: "WARN",
              contents: {
                a!richTextDisplayField(
                  align: "CENTER",
                  labelPosition: "COLLAPSED",
                  value: a!richTextItem(
                    text: "Sub Header C",
                    style: "STRONG"
                  )
                )
              }
            )
          }
        ),
        a!columnLayout(
          contents: {
            a!cardLayout(
              style: "WARN",
              contents: {
                a!richTextDisplayField(
                  align: "CENTER",
                  labelPosition: "COLLAPSED",
                  value: a!richTextItem(
                    text: "Sub Header D",
                    style: "STRONG"
                  )
                )
              }
            )
          }
        ),
      }
    ),    
    a!forEach(
      items: local!data,
      expression: 
      a!columnsLayout(
        marginBelow: "NONE",
        spacing: "NONE",
        columns: {
          a!columnLayout(
            contents: {
              a!textField(
                labelPosition: "COLLAPSED",
                value: fv!item.text1,
                saveInto: local!data[fv!index].text1
              )
            }
          ),
          a!columnLayout(
            contents: {
              a!textField(
                labelPosition: "COLLAPSED",
                value: fv!item.text2,
                saveInto: local!data[fv!index].text2
              )
            }
          ),
          a!columnLayout(
            contents: {
              a!textField(
                labelPosition: "COLLAPSED",
                value: fv!item.text3,
                saveInto: local!data[fv!index].text3
              )
            }
          ),
          a!columnLayout(
            contents: {
              a!textField(
                labelPosition: "COLLAPSED",
                value: fv!item.text4,
                saveInto: local!data[fv!index].text4
              )
            }
          ),
          a!columnLayout(
            contents: {
              a!textField(
                labelPosition: "COLLAPSED",
                value: fv!item.text5,
                saveInto: local!data[fv!index].text5
              )
            }
          ),
          a!columnLayout(
            contents: {
              a!textField(
                labelPosition: "COLLAPSED",
                value: fv!item.text6,
                saveInto: local!data[fv!index].text6
              )
            }
          ),
          a!columnLayout(
            contents: {
              a!textField(
                labelPosition: "COLLAPSED",
                value: fv!item.text7,
                saveInto: local!data[fv!index].text7
              )
            }
          ),
          a!columnLayout(
            contents: {
              a!textField(
                labelPosition: "COLLAPSED",
                value: fv!item.text8,
                saveInto: local!data[fv!index].text8
              )
            }
          )
        }
      )
    ),
    a!richTextDisplayField(
      labelPosition: "COLLAPSED",
      value: {
        a!richTextIcon(
          icon: "plus",
          color: "ACCENT"
        ),
        a!richTextItem(
          text: "Add a Row",
          link: a!dynamicLink(
            label: "Add a Row",
            value: {id: count(local!data)+1, text1: null, text2: null, text3: null, text4: null, text5: null, text6: null, text7: null, text8: null},
            saveInto: {
              a!save(
                local!data,
                append(
                  local!data,
                  save!value
                )
              )
            }
          )
        )
      }
    )
  }
),

Known Participant
May 13, 2021 5:32 PM UTC

Thank you for the suggestion!  I am a bit worried about performance because the grid will have many rows.  Do you know of any way that I could use a normal editable grid, but somehow line up a card above a specific number of the columns?  Or another elegant way to denote that columns are grouped?

csteward
cstewardAnswer
Brainy
May 13, 2021 5:47 PM UTC

You can try playing around with cards inside a!columnsLayout() above the grid, this will get you closest while still using the standard editable grid component:

a!localVariables(
  local!data: {
    {id: 1, text1: null, text2: null, text3: null, text4: null, text5: null, text6: null, text7: null, text8: null},
    {id: 2, text1: null, text2: null, text3: null, text4: null, text5: null, text6: null, text7: null, text8: null},
    {id: 3, text1: null, text2: null, text3: null, text4: null, text5: null, text6: null, text7: null, text8: null}
  },
  {
    a!columnsLayout(
      marginBelow: "NONE",
      spacing: "NONE",
      columns: {
        a!forEach(
          items: 1+enumerate(3),
          expression: 
          a!columnLayout(
            contents: {
              a!cardLayout(
                style: "INFO",
                contents: {
                  a!richTextDisplayField(
                    labelPosition: "COLLAPSED", 
                    align: "CENTER",
                    value: a!richTextItem(
                      text: concat("Header ",fv!item),
                      style: "STRONG"
                    )
                  )
                }
              )
            }
          )
        )
      }
    ),
    a!gridLayout(
      labelPosition: "COLLAPSED",
      headerCells: a!forEach(
        items: 1+enumerate(6),
        expression: a!gridLayoutHeaderCell(label: concat("Column ",fv!item), align: "CENTER")
      )
    )
  }
)