Separating rows with specific data

Certified Associate Developer

Hi Team,

We have a requirement from the regarding a read-only grid. The grid is sorted first by Meeting Name, and then by Supplier Name. The client expects a visual indicator (like a bottom line or separator) between different suppliers to help users easily identify when the data switches from one supplier to another. For example, after all rows for Supplier A, there should be a highlighted row or separator before rows for Supplier B begin. Is this kind of visual separation possible in Appian grids?

Meeting Name Supplier Name Data
Meeting A Supplier A 23
Meeting A Supplier A 87
     
Meeting A Supplier B 56
Meeting A Supplier B 7
Meeting B Supplier A 45
Meeting B Supplier A 456
     
Meeting B Supplier B 56
Meeting B Supplier B 56

  Discussion posts and replies are publicly visible

  • 0
    Certified Lead Developer

    You can achieve this with multiple approach i tried 2 approach.
    Let's have a look at it.

    a!localVariables(
      /* Sample data matching your structure */
      local!data: {
        a!map(meetingName: "Meeting A", supplierName: "Supplier A", data: 23),
        a!map(meetingName: "Meeting A", supplierName: "Supplier A", data: 87),
        a!map(meetingName: "Meeting A", supplierName: "Supplier B", data: 56),
        a!map(meetingName: "Meeting A", supplierName: "Supplier B", data: 7),
        a!map(meetingName: "Meeting B", supplierName: "Supplier A", data: 45),
        a!map(meetingName: "Meeting B", supplierName: "Supplier A", data: 456),
        a!map(meetingName: "Meeting B", supplierName: "Supplier B", data: 56),
        a!map(meetingName: "Meeting B", supplierName: "Supplier B", data: 56)
      },
    
      /* Sort data by Meeting Name, then Supplier Name */
      local!sortedData: todatasubset(
        local!data,
        a!pagingInfo(
          startIndex: 1,
          batchSize: -1,
          sort: {
            a!sortInfo(field: "meetingName", ascending: true),
            a!sortInfo(field: "supplierName", ascending: true)
          }
        )
      ).data,
    
      /* Add index and previous supplier info to each row */
      local!dataWithIndex: a!forEach(
        items: local!sortedData,
        expression: a!map(
          meetingName: fv!item.meetingName,
          supplierName: fv!item.supplierName,
          data: fv!item.data,
          index: fv!index,
          isNewSupplier: or(
            fv!index = 1,
            fv!item.supplierName <> index(local!sortedData, fv!index - 1, {}).supplierName
          )
        )
      ),
    
      {
        a!sectionLayout(
          label: "",
          contents: {
            /* Approach 1: Using Horizontal Line Separators */
            a!cardLayout(
              contents: {
                a!richTextDisplayField(
                  label: "Approach 1: Line Separators",
                  labelPosition: "ABOVE",
                  value:{}
                ),
                a!gridField(
                  label: "",
                  labelPosition: "COLLAPSED",
                  data: local!dataWithIndex,
                  columns: {
                    a!gridColumn(
                      label: "Meeting Name",
                      value: a!richTextDisplayField(
                        value: {
                          if(
                            and(
                              fv!row.index > 1,
                              fv!row.isNewSupplier
                            ),
                            {
                              a!richTextItem(
                                text: "────────────────────",
                                color: "SECONDARY",
                                size: "SMALL"
                              ),
                              a!richTextItem(
                                text: char(10),
                                size: "SMALL"
                              )
                            },
                            {}
                          ),
                          a!richTextItem(
                            text: fv!row.meetingName,
                            style: if(
                              fv!row.isNewSupplier,
                              "STRONG",
                              "PLAIN"
                            )
                          )
                        }
                      ),
                      width: "AUTO"
                    ),
                    a!gridColumn(
                      label: "Supplier Name",
                      value: a!richTextDisplayField(
                        value: {
                          if(
                            and(
                              fv!row.index > 1,
                              fv!row.isNewSupplier
                            ),
                            {
                              a!richTextItem(
                                text: "────────────────────",
                                color: "SECONDARY",
                                size: "SMALL"
                              ),
                              a!richTextItem(
                                text: char(10),
                                size: "SMALL"
                              )
                            },
                            {}
                          ),
                          if(
                            fv!row.isNewSupplier,
                            {
                              a!richTextItem(
                                text: "▼ ",
                                color: "ACCENT",
                                size: "MEDIUM"
                              ),
                              a!richTextItem(
                                text: fv!row.supplierName,
                                style: "STRONG",
                                color: "ACCENT"
                              )
                            },
                            a!richTextItem(
                              text: "   " & fv!row.supplierName,
                              size: "STANDARD"
                            )
                          )
                        }
                      ),
                      width: "AUTO"
                    ),
                    a!gridColumn(
                      label: "Data",
                      value: a!richTextDisplayField(
                        value: {
                          if(
                            and(
                              fv!row.index > 1,
                              fv!row.isNewSupplier
                            ),
                            {
                              a!richTextItem(
                                text: "────────",
                                color: "SECONDARY",
                                size: "SMALL"
                              ),
                              a!richTextItem(
                                text: char(10),
                                size: "SMALL"
                              )
                            },
                            {}
                          ),
                          a!richTextItem(
                            text: fv!row.data,
                            style: if(
                              fv!row.isNewSupplier,
                              "STRONG",
                              "PLAIN"
                            )
                          )
                        }
                      ),
                      width: "AUTO",
                      align: "END"
                    )
                  },
                  rowHeader: 1,
                  height: "AUTO",
                  borderStyle: "LIGHT",
                  shadeAlternateRows: false,
                  spacing: "DENSE"
                )
              },
              style: "NONE",
              padding: "STANDARD",
              marginBelow: "STANDARD"
            ),
    
            /* Approach 2: Using Colored Text and Icons */
            a!cardLayout(
              contents: {
                a!richTextDisplayField(
                  label: "Approach 2: Colored Indicators",
                  labelPosition: "ABOVE",
                  value: {}
                ),
                a!gridField(
                  label: "",
                  labelPosition: "COLLAPSED",
                  data: local!dataWithIndex,
                  columns: {
                    a!gridColumn(
                      label: "",
                      value: a!richTextDisplayField(
                        value: {
                          a!richTextItem(
                            text: if(
                              fv!row.isNewSupplier,
                              "●",
                              ""
                            ),
                            color: "POSITIVE",
                            size: "LARGE",
                            style: "STRONG"
                          )
                        }
                      ),
                      width: "ICON"
                    ),
                    a!gridColumn(
                      label: "Meeting Name",
                      value: a!richTextDisplayField(
                        value: a!richTextItem(
                          text: fv!row.meetingName,
                          style: if(
                            fv!row.isNewSupplier,
                            "STRONG",
                            "PLAIN"
                          ),
                          size: if(
                            fv!row.isNewSupplier,
                            "MEDIUM",
                            "STANDARD"
                          )
                        )
                      ),
                      width: "AUTO"
                    ),
                    a!gridColumn(
                      label: "Supplier Name",
                      value: a!richTextDisplayField(
                        value: {
                          if(
                            fv!row.isNewSupplier,
                            {
                              a!richTextItem(
                                text: "► ",
                                color: "POSITIVE",
                                style: "STRONG",
                                size: "MEDIUM"
                              ),
                              a!richTextItem(
                                text: fv!row.supplierName,
                                style: "STRONG",
                                color: "POSITIVE",
                                size: "MEDIUM"
                              )
                            },
                            a!richTextItem(
                              text: fv!row.supplierName,
                              color: "SECONDARY"
                            )
                          )
                        }
                      ),
                      width: "AUTO"
                    ),
                    a!gridColumn(
                      label: "Data",
                      value: a!richTextDisplayField(
                        value: a!richTextItem(
                          text: fv!row.data,
                          style: if(
                            fv!row.isNewSupplier,
                            "STRONG",
                            "PLAIN"
                          ),
                          size: if(
                            fv!row.isNewSupplier,
                            "MEDIUM",
                            "STANDARD"
                          )
                        )
                      ),
                      width: "AUTO",
                      align: "END"
                    )
                  },
                  rowHeader: 1,
                  height: "AUTO",
                  borderStyle: "LIGHT",
                  shadeAlternateRows: true,
                  spacing: "DENSE"
                )
              },
              style: "NONE",
              padding: "STANDARD",
              marginBelow: "STANDARD"
            ),
    
          }
        )
      }
    )

  • 0
    Certified Lead Developer

    There’s always an ongoing debate between using customization versus out-of-the-box (OOTB) features. In my opinion, customization is sometimes necessary. it can enhance usability and make the application more intuitive and visually appealing for the end user.

    However, we must also consider important factors such as scalability and maintainability. For example, if you implement a custom feature in one place, users may start expecting the same functionality across all similar components, like every grid in the application. This can quickly become difficult to manage and maintain.

    If there’s no OOTB feature that exactly meets the requirement, I would generally recommend avoiding customization unless absolutely necessary. Instead, consider workarounds. For instance, you could use a rich text display field to show the supplier name and apply distinct colors to each supplier to make them visually distinguishable. This approach keeps things simple, avoids heavy customization, and still improves the user experience.