PracticeVideosBlogFlashcards

10.1 Read-Only Grids and Editable Grid Layouts

Key Takeaways

  • a!gridField and a!gridLayout serve different read-only and editable use cases.

  • An a!forEach expression building editable rows provides fv!item and fv!index.

  • Selected identifiers are distinct from array positions; remove() needs a valid position.

  • Editing or removing a draft array item does not itself update or delete a database row.

Last updated: October 2026

Read-Only and Editable Grids

Appian provides different components for different tabular tasks. a!gridField() is the read-only grid used to browse, sort, page, and select data. a!gridLayout(), with a!gridRowLayout(), provides an editable grid containing input components. The distinction is practical: reviewing thousands of orders and entering a handful of draft order lines require different data and state-management designs.

The components have not been merged into a universal editable a!gridField. An input field placed into an unsupported read-only grid column does not become a supported editable spreadsheet. Recognize the correct component before attempting to configure row editing or diagnose an invalid component error.

RequirementComponent and pattern
Search and page through persisted customer recordsRecord-backed a!gridField
Select record identifiers for an authorized bulk actionselectionValue and selectionSaveInto on a read-only grid
Edit several unsaved line items on one forma!gridLayout with input components in rows
Edit one item from a large listRead-only grid linking to an edit form or record action

Configure a Read-Only Grid

A record-backed grid can use a record type as its data source. Columns use a!gridColumn, and a row's data is available through fv!row. Set a sort field that corresponds to the displayed value, choose a suitable page size, and enable only the search, filters, and actions that the reader needs.

The example uses shortened object references. Select the actual Order record type and fields through expression-editor typeahead in your environment.

a!gridField(
  label: "Orders",
  data: recordType!Order,
  columns: {
    a!gridColumn(
      label: "Order Number",
      sortField: recordType!Order.fields.orderNumber,
      value: fv!row[recordType!Order.fields.orderNumber]
    ),
    a!gridColumn(
      label: "Status",
      sortField: recordType!Order.fields.status,
      value: fv!row[recordType!Order.fields.status]
    )
  },
  pageSize: 10
)

Record-backed paging operates over the matching record dataset. A grid over an in-memory array is limited to the data already supplied. If a query returns only fifty items, passing that array to a grid cannot expose the rest of the database. Choose automatic record-backed access or deliberately wire query paging and sorting.

Selection Is Separate from Editing

When selectable is enabled, selectionValue holds selected identifiers and selectionSaveInto stores changes to that selection. Identifiers identify records; they are not necessarily positions in an array. A database identifier of 48102 is not the forty-eight-thousandth row of a ten-row interface list.

Use the documented selection-style and maximum-selection settings for the installed component version. Do not invent a selectionMode argument. Before executing a bulk action, recheck authorization and current business state for selected records. A checkbox chosen before a record was closed should not authorize an invalid approval later.

Construct an Editable Grid

An editable grid builds rows explicitly. Each row contains compatible interface components in the same order as the headers. A common pattern iterates over a small draft collection using a!forEach. Inside that loop, fv!item supplies the current item and fv!index supplies its one-based position.

a!localVariables(
  local!items: {
    a!map(description: "Monitor", quantity: 2),
    a!map(description: "Keyboard", quantity: 3)
  },
  a!gridLayout(
    label: "Draft Items",
    headerCells: {
      a!gridLayoutHeaderCell(label: "Description"),
      a!gridLayoutHeaderCell(label: "Quantity")
    },
    rows: a!forEach(
      items: local!items,
      expression: a!gridRowLayout(
        contents: {
          a!textField(
            value: fv!item.description,
            saveInto: local!items[fv!index].description,
            required: true
          ),
          a!integerField(
            value: fv!item.quantity,
            saveInto: local!items[fv!index].quantity,
            required: true,
            validations: if(
              fv!item.quantity < 1,
              "Quantity must be at least one.",
              {}
            )
          )
        }
      )
    ),
    rowHeader: 1
  )
)

This is a self-contained state demonstration. In an actual task form, expose the collection through a typed rule input and map it through the task's node input to a process variable. Local edits alone do not persist into a process or database. Add an appropriate submit or save operation after validating the collection.

Add and Remove Draft Rows

Append a newly initialized item to the backing collection when the user chooses Add Item. Supply consistent field names and types; an empty dictionary with missing fields may behave differently from a typed CDT or record value. Configure the add control so it does not accidentally submit the whole form.

To remove a draft item, save remove(ri!items, fv!index) back to ri!items from a delete link inside the a!forEach row expression. The loop provides that index. Read-only grid column contexts do not automatically supply the same iteration variables. Keep the displayed item and the indexed save target synchronized if rows are filtered or reordered.

Removing a draft row from an interface array is not a database delete. If an existing persisted item is removed, track its identifier and deliberately perform the authorized delete or status change on submission. Otherwise it may reappear on the next query. If the user cancels, determine whether the business requires discarding draft removals.

Validate and Bound the Work

Validate required descriptions, positive quantities, duplicates, and collection-level rules such as at least one item. Check both empty and multiple-row states. A field can be valid individually while the overall order violates a total-quantity rule.

Editable grids do not provide native paging. Keep the number of editable cells manageable. For a large dataset, use a read-only grid with row-specific edit actions. Avoid querying related data in every cell: use a configured relationship or load supporting choices once outside the loop. This addresses the number of repeated operations, not merely the appearance of the grid.

Worked Purchase Order Check

A buyer enters two monitors at 180 each and three keyboards at 25 each. The line totals are 360 and 75, producing a total of 435 before taxes or shipping. Changing monitor quantity to zero should show a validation error. Deleting the keyboard row should leave one draft line and a total of 360. Cancelling should follow the form's explicit discard policy.

Test add, edit, remove, empty state, invalid data, and final persistence separately. A visually updated cell is evidence of interface state; verifying the saved row requires reading the persistence result.

Takeaway: Use a read-only grid for discovery and actions, an editable grid for bounded data entry, and explicit saves for durable changes.

Sources checked 2026-10-18: editable grid, editable row, and editable-grid recipe.

Test Your Knowledge

An editable grid is built using a!forEach(items: ri!items). Which value identifies the current array position for remove(ri!items, ...)?

A

fv!index from the a!forEach expression

B

The database primary key regardless of position

C

The entire fv!item map

D

The selected record type object

Test Your Knowledge

A developer is building a read-only grid using a!gridField() to display customer records. They notice that the interface renders very slowly. Upon inspection, they find that an expression rule querying a secondary database table is called inside the value parameter of an a!gridColumn(). What is the root cause of the performance bottleneck?

A

Grids cannot render more than 5 columns without severe browser rendering latency.

B

The expression rule inside the column executes once for every visible row on the page, multiplying database queries.

C

The a!gridColumn() value parameter does not support rule references and causes automatic page refreshes.

D

The selectable parameter must be set to true whenever child queries are invoked inside a grid.

Test Your Knowledge

When enabling user row selection in a!gridField(), which two parameters work in tandem to store the selected row identifiers and update that selection array when a user clicks the checkboxes?

A

showWhen and saveInto

B

choiceValues and choiceLabels

C

selectionValue and selectionSaveInto

D

batchSize and sort

Sections you finish are checked off in the contents.