Editable Grid Component

Editable Grid

Function: a!gridLayout()

Arranges interface components in a tabular layout to provide quick inline editing of fields.

See also: Paging Grid

Parameters

Name Keyword Type Description
Label label Text Optional text to display as the grid layout's label.
Label Position labelPosition Text Optional text to determine where the label appears. Valid values include
  • "ABOVE" (default) Displays the label above the component.
  • "ADJACENT" Displays the label to the left of the component.
  • "COLLAPSED" Hides the label. The label will still be read by screen readers; see accessibility considerations for more information.
  • "JUSTIFIED" Aligns the label alongside the component starting at the edge of the page.
Instructions instructions Text Optional text displayed below the field's label.
Help Tooltip helpTooltip Text Displays a help icon with the specified text as a tooltip. The tooltip displays a maximum of 500 characters. The help icon does not show when the label position is "COLLAPSED".
Total Count totalCount Integer Total number of rows of data in the grid.
Empty Grid Message emptyGridMessage Text Text to display in the grid when no data is available. Default is "No items available".
Header Cells headerCells Header Cell Array Each grid layout header cell corresponds to a column header. Configured using a!gridLayoutHeaderCell(label, align, helpTooltip).
Column Configurations columnConfigs Column Configurations Array Each column configuration corresponds to a column. Configured using a!gridLayoutColumnConfig(width)
Rows rows Row Array Rows that are displayed in the grid. Configured using a!gridRowLayout(contents, id, selectionDisabled). Every row in the grid must have the same number of cells. The number of cells in each row must also match the number of header cells.
Selectable selectable Boolean Determines if the selection column is displayed. Default is false.
Selection Required selectionRequired Boolean Determines if at least one row must be selected. Default is false. When selection is enabled, the checkbox in the header selects all rows that are not disabled.
Selection Disabled selectionDisabled Boolean Determines if selection is disabled in all rows. Default is false. Components configured with required set to true do not display the red asterisk. The required validation message is diplayed when submitting or selecting a validating button and the component's value is null.
Selected Rows selectionValue Any Type Array The ids of the rows that are selected.
Save Selection To selectionSaveInto Save Array One or more variables that are updated with the selected identifiers when the user changes selections. Use a!save(target, value) to save a modified or alternative value to a variable.
Add Row Link addRowLink Dynamic Link Link for adding a row to the grid. Create link using a!dynamicLink().
Validations validations Text or Validation Message Array Validation errors to be displayed below the field. Configured using a text array or an array with a mix of text and Validation Message using a!validationMessage(message, validateAfter).
Validation Group validationGroup Text When present, this field is only validated when a button in the same validation group is clicked.
Visibility showWhen Boolean Determines whether the component is displayed on the interface. When set to false, the component is hidden and is not evaluated. Default: true.
Spacing spacing Text Determines the spacing within grid cells. Valid values: "STANDARD" (default), "DENSE".
Height height Text Determines the height of the grid. Valid values: "SHORT", "MEDIUM", "TALL", "AUTO" (default). When set to SHORT, MEDIUM, or TALL, the header is frozen.
Border Style borderStyle Text Determines the style of the grid border. Valid values: "STANDARD" (default), "LIGHT".
Shade Alternate Rows shadeAlternateRows Boolean Determines whether alternate rows are shaded. Default: true.
Selection Style selectionStyle Text Determines the style when a row is selected. Valid values: "CHECKBOX" (default), "ROW_HIGHLIGHT".
Row Header rowHeader Integer Index of the column to be used as the row header. Screen readers will announce the value in each row header when navigating to other cells within that row. Used only for accessibility; produces no visible change.

Notes

  • Performance is affected by the number of components in an interface. If your interface contains an editable grid with many cells, the interface may feel slow. See also: Interface Evaluation Lifecycle
  • When using a datasubset obtained using a!queryEntity() as the source of data for the editable grid, make sure to set its fetchTotalCount parameter to true. Otherwise, the totalCount field on the resulting datasubset may be invalid (i.e. set to -1). This is relevant for the totalCount parameter on the editable grid. See also: a!queryEntity() Function.
  • When an interface component is placed into a grid cell, the following component configurations are ignored: label, instructions, label position. Validation messages on the component within the grid cell are displayed when the component's value is not null.
  • The components placed within a grid cell can be different across the rows in one column.
  • The Grid Text Column and Grid Image Column components cannot be used in an editable grid.
  • When placing an Image Field component inside an editable grid, there are some additional restrictions that do not apply when an image component is outside of a grid.
  • The image component behaves slightly differently when it is placed inside of a Grid Layout component than when it is outside of a Grid Layout.
    • Inside an editable grid, the image size FIT is not allowed.
    • Inside an editable grid, the default size for an image component is ICON.
    • Inside an editable grid, you can only have 0 or 1 images in an image component. Multiple images in the same grid cell are not supported.
    • All image components in the same column must have the same size.
  • For accessibility purposes, every grid should have a row header configured. The first column containing text is usually the correct choice for row header. See the UX Design Guide for more information.
  • Grid heights behave as a fixed height on web but a maximum height on mobile.
  • Setting the height of the grid to SHORT, MEDIUM, or TALL will freeze the grid's header and footer. See the Short Editable Grid with Weighted Columns example below.

Examples

Copy and paste an example into the INTERFACE DEFINITION in EXPRESSION MODE to see it displayed.

Editable Grid with 2 Rows

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
=load(
  local!items: {
    {item: "Item 1", qty: 1, unitPrice: 10},
    {item: "Item 2", qty: 2, unitPrice: 20}
  },
  a!gridLayout(
    label: "Products",
    instructions: "Update the item name, quantity, or unit price.",
    headerCells: {
      a!gridLayoutHeaderCell(label: "Item"),
      a!gridLayoutHeaderCell(label: "Qty", align: "RIGHT"),
      a!gridLayoutHeaderCell(label: "Unit Price", align: "RIGHT"),
      a!gridLayoutHeaderCell(label: "Total", align: "RIGHT")
    },
    rows: {
      a!gridRowLayout(
        contents: {
            a!textField(
            value: local!items[1].item,
            saveInto: local!items[1].item
          ),
          a!integerField(
            value: local!items[1].qty,
            saveInto: local!items[1].qty,
            align: "RIGHT"
          ),
          a!floatingPointField(
            value: local!items[1].unitPrice,
            saveInto: local!items[1].unitPrice,
            align: "RIGHT"
          ),
          a!textField(
            value: dollar(tointeger(local!items[1].qty) * todecimal(local!items[1].unitPrice)),
            readOnly: true,
            align: "RIGHT"
          )
        }
      ),
      a!gridRowLayout(
        contents: {
          a!textField(
            value: local!items[2].item,
            saveInto: local!items[2].item
          ),
          a!integerField(
            value: local!items[2].qty,
            saveInto: local!items[2].qty,
            align: "RIGHT"
          ),
          a!floatingPointField(
            value: local!items[2].unitPrice,
            saveInto: local!items[2].unitPrice,
            align: "RIGHT"
          ),
          a!textField(
            value: dollar(tointeger(local!items[2].qty) * todecimal(local!items[2].unitPrice)),
            readOnly: true,
            align: "RIGHT"
          )
        }
      )
    },
    rowHeader: 1
  )
)

Displays the following:

Editable Grid with 2 Rows Using Looping

See Add, Edit, and Remove Data in an Inline Editable Grid recipe for more help using a!forEach with the editable grid.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
=load(
  local!items: {
    {item: "Item 1", qty: 1, unitPrice: 10},
    {item: "Item 2", qty: 2, unitPrice: 20}
  },
  a!gridLayout(
    label: "Products",
    instructions: "Update the item name, quantity, or unit price.",
    headerCells: {
      a!gridLayoutHeaderCell(label: "Item"),
      a!gridLayoutHeaderCell(label: "Qty", align: "RIGHT"),
      a!gridLayoutHeaderCell(label: "Unit Price", align: "RIGHT"),
      a!gridLayoutHeaderCell(label: "Total", align: "RIGHT")
    },
    rows: a!forEach(
      items: local!items,
      expression: a!gridRowLayout(
        contents: {
            a!textField(
            value: fv!item.item,
            saveInto: fv!item.item
          ),
          a!integerField(
            value: fv!item.qty,
            saveInto: fv!item.qty,
            align: "RIGHT"
          ),
          a!floatingPointField(
            value: fv!item.unitPrice,
            saveInto: fv!item.unitPrice,
            align: "RIGHT"
          ),
          a!textField(
            value: dollar(tointeger(fv!item.qty) * todecimal(fv!item.unitPrice)),
            readOnly: true,
            align: "RIGHT"
          )
        }
      )  
    ),
     rowHeader: 1
  )
)

Displays the following:

Editable Grid with Validations

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
=load(
  local!items: {
    {item: "Item 1", qty: 10, unitPrice: 10},
    {item: "Item 2", qty: 2,  unitPrice: 20}
  },
  a!gridLayout(
    label: "Products",
    instructions: "Update the item name, quantity, or unit price.",
    headerCells: {
      a!gridLayoutHeaderCell(label: "Item"),
      a!gridLayoutHeaderCell(label: "Qty", align: "RIGHT"),
      a!gridLayoutHeaderCell(label: "Unit Price", align: "RIGHT"),
      a!gridLayoutHeaderCell(label: "Total", align: "RIGHT")
    },
    rows: {
      a!gridRowLayout(
        contents: {
            a!textField(
            value: local!items[1].item,
            saveInto: local!items[1].item
          ),
          a!integerField(
            value: local!items[1].qty,
            saveInto: local!items[1].qty,
            align: "RIGHT"
          ),
          a!floatingPointField(
            value: local!items[1].unitPrice,
            saveInto: local!items[1].unitPrice,
            align: "RIGHT"
          ),
          a!textField(
            value: dollar(tointeger(local!items[1].qty) * todecimal(local!items[1].unitPrice)),
            readOnly: true,
            align: "RIGHT"
          )
        }
      ),
      a!gridRowLayout(
        contents: {
          a!textField(
            value: local!items[2].item,
            saveInto: local!items[2].item
          ),
          a!integerField(
            value: local!items[2].qty,
            saveInto: local!items[2].qty,
            align: "RIGHT"
          ),
          a!floatingPointField(
            value: local!items[2].unitPrice,
            saveInto: local!items[2].unitPrice,
            align: "RIGHT"
          ),
          a!textField(
            value: dollar(tointeger(local!items[2].qty) * todecimal(local!items[2].unitPrice)),
            readOnly: true,
            align: "RIGHT"
          )
        }
      )
    },
    validations: {
      if(
        sum(tointeger(local!items.qty) * todecimal(local!items.unitPrice))>100,
        "Total must not exceed $100",
        null
      ),
      if(
        length(local!items)<3,
        a!validationMessage(
          message: "Enter at least 3 items",
          validateAfter: "SUBMIT"
        ),
        null
      )
    },
    rowHeader: 1
  )
)

Displays the following:

Editable Grid with Selection

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
=load(
  local!items: {
    {item: "Item 1", qty: 1, unitPrice: 10},
    {item: "Item 2", qty: 2, unitPrice: 20}
  },
  local!selected: tointeger({}),
  a!gridLayout(
    label: "Products",
    instructions: "Selected: " & local!selected,
    headerCells: {
      a!gridLayoutHeaderCell(label: "Item"),
      a!gridLayoutHeaderCell(label: "Qty", align: "RIGHT"),
      a!gridLayoutHeaderCell(label: "Unit Price", align: "RIGHT"),
      a!gridLayoutHeaderCell(label: "Total", align: "RIGHT")
    },
    rows: {
      a!gridRowLayout(
        id: 1,
        contents: {
            a!textField(
            value: local!items[1].item,
            saveInto: local!items[1].item
          ),
          a!integerField(
            value: local!items[1].qty,
            saveInto: local!items[1].qty,
            align: "RIGHT"
          ),
          a!floatingPointField(
            value: local!items[1].unitPrice,
            saveInto: local!items[1].unitPrice,
            align: "RIGHT"
          ),
          a!textField(
            value: dollar(tointeger(local!items[1].qty) * todecimal(local!items[1].unitPrice)),
            readOnly: true,
            align: "RIGHT"
          )
        }
      ),
      a!gridRowLayout(
        id: 2,
        contents: {
          a!textField(
            value: local!items[2].item,
            saveInto: local!items[2].item
          ),
          a!integerField(
            value: local!items[2].qty,
            saveInto: local!items[2].qty,
            align: "RIGHT"
          ),
          a!floatingPointField(
            value: local!items[2].unitPrice,
            saveInto: local!items[2].unitPrice,
            align: "RIGHT"
          ),
          a!textField(
            value: dollar(tointeger(local!items[2].qty) * todecimal(local!items[2].unitPrice)),
            readOnly: true,
            align: "RIGHT"
          )
        }
      )
    },
    selectable: true,
    selectionValue: local!selected,
    selectionSaveInto: local!selected,
    rowHeader: 1
  )
)

Displays the following:

Editable Grid with Weighted Columns

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
=load(
  local!items: {
    {item: "Item 1", qty: 10, unitPrice: 10},
    {item: "Item 2", qty: 2,  unitPrice: 20}
  },
  a!gridLayout(
    label: "Products",
    instructions: "This is a grid layout with column weights: 5, 1, 1, 2",
    headerCells: {
      a!gridLayoutHeaderCell(label: "Item"),
      a!gridLayoutHeaderCell(label: "Qty", align: "RIGHT"),
      a!gridLayoutHeaderCell(label: "Unit Price", align: "RIGHT"),
      a!gridLayoutHeaderCell(label: "Total", align: "RIGHT")
    },
    columnConfigs: {
      a!gridLayoutColumnConfig(width: "DISTRIBUTE", weight: 5),
      a!gridLayoutColumnConfig(width: "DISTRIBUTE"),
      a!gridLayoutColumnConfig(width: "DISTRIBUTE"),
      a!gridLayoutColumnConfig(width: "DISTRIBUTE", weight: 2)
    },
    rows: {
      a!gridRowLayout(
        id: 1,
        contents: {
            a!textField(
            value: local!items[1].item,
            saveInto: local!items[1].item
          ),
          a!integerField(
            value: local!items[1].qty,
            saveInto: local!items[1].qty,
            align: "RIGHT"
          ),
          a!floatingPointField(
            value: local!items[1].unitPrice,
            saveInto: local!items[1].unitPrice,
            align: "RIGHT"
          ),
          a!textField(
            value: dollar(tointeger(local!items[1].qty) * todecimal(local!items[1].unitPrice)),
            readOnly: true,
            align: "RIGHT"
          )
        }
      ),
      a!gridRowLayout(
        id: 2,
        contents: {
          a!textField(
            value: local!items[2].item,
            saveInto: local!items[2].item
          ),
          a!integerField(
            value: local!items[2].qty,
            saveInto: local!items[2].qty,
            align: "RIGHT"
          ),
          a!floatingPointField(
            value: local!items[2].unitPrice,
            saveInto: local!items[2].unitPrice,
            align: "RIGHT"
          ),
          a!textField(
            value: dollar(tointeger(local!items[2].qty) * todecimal(local!items[2].unitPrice)),
            readOnly: true,
            align: "RIGHT"
          )
        }
      )
    },
    rowHeader: 1
  )
)

Displays the following:

Short Editable Grid with Weighted Columns

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
=load(
  local!items: {
    {item: "Item 1", qty: 1, unitPrice: 10},
    {item: "Item 2", qty: 2, unitPrice: 20},
    {item: "Item 3", qty: 3, unitPrice: 30},
    {item: "Item 4", qty: 4, unitPrice: 40},
    {item: "Item 5", qty: 5, unitPrice: 50},
    {item: "Item 6", qty: 6, unitPrice: 60},
    {item: "Item 7", qty: 7, unitPrice: 70},
    {item: "Item 8", qty: 8, unitPrice: 80},
    {item: "Item 9", qty: 9, unitPrice: 90},
    {item: "Item 10", qty: 10, unitPrice: 100}
  },
  a!gridLayout(
    label: "Products",
    instructions: "Update the item name, quantity, or unit price.",
    headerCells: {
      a!gridLayoutHeaderCell(label: "Item"),
      a!gridLayoutHeaderCell(label: "Qty", align: "RIGHT"),
      a!gridLayoutHeaderCell(label: "Unit Price", align: "RIGHT"),
      a!gridLayoutHeaderCell(label: "Total", align: "RIGHT")
    },
    columnConfigs: {
      a!gridLayoutColumnConfig(width: "DISTRIBUTE", weight: 5),
      a!gridLayoutColumnConfig(width: "DISTRIBUTE"),
      a!gridLayoutColumnConfig(width: "DISTRIBUTE"),
      a!gridLayoutColumnConfig(width: "DISTRIBUTE", weight: 2)
    },
    rows: a!forEach(
      items: local!items,
      expression: a!gridRowLayout(
        contents: {
            a!textField(
            value: fv!item.item,
            saveInto: fv!item.item
          ),
          a!integerField(
            value: fv!item.qty,
            saveInto: fv!item.qty,
            align: "RIGHT"
          ),
          a!floatingPointField(
            value: fv!item.unitPrice,
            saveInto: fv!item.unitPrice,
            align: "RIGHT"
          ),
          a!textField(
            value: dollar(tointeger(fv!item.qty) * todecimal(fv!item.unitPrice)),
            readOnly: true,
            align: "RIGHT"
          )
        }
      )  
    ),
    height: "SHORT",
    rowHeader: 1
  )
)

Displays the following:

FEEDBACK