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

  • Sure thing!  Feel free to mark the thread as answered if these setups work for you :)

  • 0
    Certified Lead Developer
    in reply to Chris

    The above logic works good  but there is lots of alignment issue when grid is viewed across different environment screen like wide desktop, mobile, etc.

    The column widths in read only grid changes on different screens which most of the times cross the limits of above cards, for example - I have one main header "A" under this I have two headers "AA", "AB" and one more main header "B", have only one under it  "BA", when I change screen to wide the width changes and unaligns. Mostly, I see columns which should only be under "A", some part of those columns move under "B" header.

    Also, with paging grid, it is getting difficult to align the width of grid columns/ headers as per card columns width.

    I have something similar requirement. Can you please help me how do I modify your above code to achieve the requirement below with proper and auto alignment. If you can help me with some code that would great. Thanks

    P.S. I need to use paging grid rather than Editable. 

     

Reply Children
No Data