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!

  Discussion posts and replies are publicly visible

Parents
  • 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
                    )
                  )
                }
              )
            )
          }
        )
      }
    ),
    

  • 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?

  • 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")
          )
        )
      }
    )

Reply
  • 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")
          )
        )
      }
    )

Children