Skip to content

Table panel

Purpose and problems it solves

The table panel presents a flat collection as typed, selectable rows. It owns data-source routing, response projection, columns, query state, empty/loading presentation, selection events, pagination, sorting, filters, panel actions, and row actions.

Use it for object lists, linked children, method-produced rows, or a simple custom HTTP collection without building a variant-specific UI.

When to use it

Use Table when users need to scan, compare, sort, filter, select, or act on rows. Use Tree when parent-child expansion is the primary model. Use Attributes for one selected object's detail fields.

Choose the request strategy before configuring columns:

NeedRequest discriminatorQuery work
Objects of one meta typemetaTypeNameServer
Objects linked to a selected parentgetSelectedObjectForEventId + linkNameServer
Objects whose attribute points to a selected parentevent + metaTypeName + linkedAttributeServer
Rows returned by a meta methodmethodNameServer/runtime method adapter
A non-MES JSON collectioncustomRequestFetch the full array; sort/page in the client

Do not mix strategies to create fallback behavior. The runtime resolves customRequest, then methodName, then linked-attribute, linked, and standard requests. A higher strategy wins when incompatible discriminator fields are combined.

Prerequisites, inputs, and ownership

Every table needs a unique id, type: "table", a request, and columns. Standard/linked/method strategies depend on current backend type, link, attribute, and method names. Linked strategies also depend on a stored selection event.

The panel request runtime owns transport and response normalization. Attribute metadata owns the type of normal MES values; columns[].dataType is primarily the explicit type override for custom-request rows, where metadata is absent.

For linked rows, remember that the response contains a link entity and a linked object. Use link.<attribute> for projected/displayed link attributes.

Capability map

Request strategies and projection

Standard meta-type request

json
{
  "id": "orders",
  "type": "table",
  "request": {
    "metaTypeName": "ProductionOrder"
  },
  "columns": [
    {
      "attribute": "itemId",
      "label": "Заказ"
    }
  ]
}

Linked-object request

The source panel must store order-selected with addSelectedObjectToRequest.

json
{
  "id": "order-operations",
  "type": "table",
  "request": {
    "getSelectedObjectForEventId": "order-selected",
    "linkName": "OrderOperation",
    "reverse": false
  },
  "columns": [
    {
      "attribute": "name",
      "label": "Операция"
    },
    {
      "attribute": "link.sequence",
      "label": "Позиция",
      "dataType": "Integer"
    }
  ]
}

Linked-attribute request

This requests the target meta type and adds an equality filter for linkedAttribute using the selected parent GUID.

json
{
  "id": "order-materials",
  "type": "table",
  "request": {
    "metaTypeName": "MaterialRequirement",
    "getSelectedObjectForEventId": "order-selected",
    "linkedAttribute": "productionOrder"
  },
  "columns": [
    {
      "attribute": "material.name",
      "label": "Материал"
    }
  ]
}

Runtime method request

The shared request router supports this strategy, although the local TableRequest union still omits it. Treat that mismatch as declared/runtime drift. The executor resolves objects[] through the same endpoint resolver as runMethod actions, sends parent, methodName, and the resulting GUIDs to POST /MetaMethods/Run, and treats the returned value as an object-response shape. The executor does not add Table filters, sorting, or pagination to the method call.

json
{
  "id": "local-shared-config-method-request",
  "type": "table",
  "request": {
    "methodName": "FindLocalOrdersForAnchor",
    "parent": "LocalOrder",
    "objects": [
      {
        "objectId": {
          "objectGuid": "local-order-001"
        }
      }
    ]
  },
  "columns": [
    {
      "attribute": "code",
      "label": "Код"
    },
    {
      "attribute": "product",
      "label": "Изделие"
    },
    {
      "attribute": "status",
      "label": "Статус"
    },
    {
      "attribute": "priority",
      "label": "Приоритет"
    }
  ]
}

This checked-in fixture resolves the fixed objectId to "local-order-001". Its local method handler requires the matching parent/method/object context and returns the shared three-row local-order dataset as ObjectsResponse. It is a fixture-owned MetaMethod contract, not a claim that production provides a method with this name.

Custom HTTP request

customRequest.method is required and supports GET, POST, PUT, PATCH, and DELETE. The current Table custom source forwards only URL and method; it does not accept headers or a request body. dataPath is a dot-separated path to the array, not JSONPath. idField defaults to id.

json
{
  "id": "service-health",
  "type": "table",
  "request": {
    "customRequest": {
      "method": "GET",
      "url": "/monitoring/services",
      "idField": "serviceId",
      "dataPath": "data.items"
    }
  },
  "pagination": {
    "rowsPerPage": [10, 25]
  },
  "columns": [
    {
      "attribute": "serviceId",
      "label": "ID",
      "dataType": "String",
      "fixedWidth": "10rem"
    },
    {
      "attribute": "name",
      "label": "Сервис",
      "dataType": "String",
      "sortable": true
    },
    {
      "attribute": "healthy",
      "label": "Работает",
      "dataType": "Boolean",
      "sortable": true
    }
  ]
}

Custom rows are normalized to object-like rows. When dataType is absent, runtime infers only string, integer, double, and boolean; null, arrays, objects, and other values become dtUndefine. An explicit column type wins over inference.

For standard and linked requests, response projection is:

  1. request.attributeList, when explicitly present;
  2. otherwise every columns[].attribute.

The explicit list replaces, rather than extends, the column-derived list. Include hidden attributes needed by actions. Object projections discard link.*; linked-object projections convert link.attribute to link:attribute on the wire.

Typed columns and value presentation

Each column binds attribute to a localized label.

  • sortable enables a header sort control.
  • dataType supplies value type where metadata is absent, especially custom rows.
  • maxRows constrains multiline renderer output.
  • maxHeight constrains the cell content box and allows inner scrolling.
  • Typed rendering is shared with other panel surfaces; Table does not implement separate date, object, Boolean, JSON, or color formatting.

Query state, filters, sorting, and pagination

Table keeps pagination and sortParams in its table store and mirrors them to the panel-response registry. Applied filter state (findAttributes) is owned by the registry. Remounting can restore those values rather than returning to page one.

filter.fields configures fields in the filter modal. Applying a filter writes cleaned findAttributes, preserves the current page size, resets offset to 0, and invalidates the Table query. EMPTY and NOT_EMPTY do not require a comparison value.

Current drift matters:

  • request.filter is declared but not consumed by the request builder.
  • A static request.findAttributes is not seeded into the current Table query state. Use the filter UI/current registry flow for interactive filters; do not claim a JSON-only initial filter without runtime verification.
  • request.sortParams is used as the initial server sort when no interactive sort overrides it.

For standard, linked, and linked-attribute requests, sort and pagination are sent to the server. The method adapter currently ignores filter, sort, and pagination state even though the shared request config can carry it; the method must return the usable collection. For custom requests, the whole array is fetched, then the current sort and page slice are applied in memory. Sorting always resets the current offset to zero.

pagination.rowsPerPage is both the page-size menu and a precedence list: its first value is the initial limit. If pagination is omitted, the current runtime uses [3, 5, 10, 15] and starts with 3.

Selection and events

selectionMode accepts single or multiple and defaults to single. Selection is stored by panel ID. An onObjectSelect handler can publish the last selected row through addSelectedObjectToRequest, set variables, or run other supported event actions.

Unselecting clears the stored selection event. Linked consumers and objectSelected conditions therefore react to selection and deselection. Multiple selection still exposes the last selected object as event context for cross-panel requests.

Panel and row actions

filter or panelActions activates the header action area. Panel actions use the common action dispatcher and may be a legacy action array or grouped actions. Keep action methods aligned with the actual common handler.

itemActions renders a row action column. itemActionsMaxVisible controls how many actions appear inline before overflow. The Table row handler currently implements:

  • update;
  • deleteObject;
  • deleteLink;
  • nested actions as an onButtonClick event;
  • openSchedule;
  • selectObject;
  • runMethod;
  • http.

Other declared global action methods are not automatically supported in the Table row context. Row actions operate on the row's normalized object selector; custom-request IDs must therefore be stable and meaningful for any action that contacts object APIs.

UI states and width behavior

ui.title is rendered in the Table header. While an existing result is being refetched, the DataTable receives its loading state. Before the first response, the outer renderer may be blank because formatted rows are still undefined.

After a successful empty response:

  • the paginator is hidden;
  • ui.emptyMessage is used when supplied;
  • otherwise the shared object-empty translation is used.

There is no dedicated configured error state. Standard fetch errors are logged and custom-query errors are exposed internally, but this component does not render an error message.

Width fields:

FieldBehavior
fixedWidthFixed CSS width. Header/body content is truncated with an ellipsis; the table uses fixed layout when any effective fixed column exists.
minWidthMinimum useful width. The table may grow beyond its panel and the container scrolls horizontally.
widthLegacy auto-layout hint. Content, padding, and sort controls may override it.

When both fixedWidth and minWidth occur on one column, minWidth wins and the panel logs a configuration error. The table minimum width is the sum of all effective constraints, plus the action column when needed, but never less than the panel width. Leave at least one descriptive column flexible when using fixed widths.

Behavioral model and interactions

  1. Resolve one request strategy from the panel request.
  2. Resolve current pagination, sort, and filter state.
  3. Build response projection from explicit attributeList or columns.
  4. Fetch/normalize rows and mirror response/query metadata into the registry.
  5. Format rows with typed attributesMap entries.
  6. Render columns, current selection, and the applicable action surfaces.
  7. On page/sort/filter changes, update query state and fetch again (or process cached raw custom data).
  8. On linked-parent selection changes, use the parent GUID in query identity; clearing the parent clears stale rows.

Filters, sorting, and pagination are not independent decorations: each forms part of request state. Changing one may reset offset or change query identity. Projection and actions also interact: an action cannot use an attribute that the response never included.

Alternatives, defaults, ordering, and precedence

  • Request strategy: custom > method > linked-attribute > linked > standard.
  • Projection: explicit attributeList > column attributes.
  • Custom value type: column dataType > limited value inference.
  • Selection: configured mode > single.
  • Initial page size: first configured rowsPerPage > runtime default 3.
  • Row action visibility: configured itemActionsMaxVisible > 2.
  • Column width: minWidth > fixedWidth > legacy width > flexible.
  • Empty copy: localized ui.emptyMessage > shared translation.

Columns and actions render in configured array order. One interactive sort column produces one current sort entry.

Limitations and declaration/runtime drift

  • Method requests work through the shared runtime router but are missing from the local TableRequest type union.
  • The method executor does not forward Table filters, sorting, or pagination to the method call.
  • request.filter is inert in the current builder, and static request.findAttributes is not initialized by the current Table query.
  • Custom requests do not forward body or headers and require the endpoint to return the full collection used for client-side operations.
  • Custom sorting uses browser localeCompare for strings, numeric subtraction for numbers, and string conversion otherwise. It does not reproduce backend-specific or domain-specific ordering.
  • There is no Table-configured error message or first-load skeleton.
  • width cannot guarantee an observable width.
  • Fixed widths intentionally truncate nested renderer content.
  • Row-action support is a context-specific subset of the global action union.

Minimal valid configuration

json
{
  "id": "materials",
  "type": "table",
  "request": {
    "metaTypeName": "MaterialDefinition"
  },
  "columns": [
    {
      "attribute": "name",
      "label": "Материал"
    }
  ]
}

Realistic end-to-end scenarios

Master table with selection, filter, sort, page state, and actions

The API must expose ProductionOrder, the listed attributes, object editing, and deletion. Selecting a row publishes production-order-selected for a linked consumer.

json
{
  "id": "production-orders",
  "type": "table",
  "ui": {
    "title": {
      "ru": "Производственные заказы",
      "en": "Production orders"
    },
    "emptyMessage": {
      "ru": "Заказы не найдены",
      "en": "No orders found"
    }
  },
  "request": {
    "metaTypeName": "ProductionOrder",
    "attributeList": [
      "itemId",
      "name",
      "status",
      "plannedStart"
    ],
    "sortParams": [
      {
        "attribute": "plannedStart",
        "sortDirection": "ASC"
      }
    ]
  },
  "filter": {
    "fields": [
      {
        "attribute": "itemId",
        "label": "Номер"
      },
      {
        "attribute": "status",
        "label": "Статус"
      }
    ]
  },
  "pagination": {
    "rowsPerPage": [10, 25, 50]
  },
  "selectionMode": "single",
  "events": [
    {
      "id": "production-order-selected",
      "name": "onObjectSelect",
      "actions": [
        {
          "name": "addSelectedObjectToRequest"
        }
      ]
    }
  ],
  "columns": [
    {
      "attribute": "itemId",
      "label": "Номер",
      "sortable": true,
      "fixedWidth": "9rem"
    },
    {
      "attribute": "name",
      "label": "Наименование",
      "sortable": true,
      "minWidth": "18rem"
    },
    {
      "attribute": "status",
      "label": "Статус",
      "sortable": true,
      "fixedWidth": "10rem"
    },
    {
      "attribute": "plannedStart",
      "label": "Начало",
      "sortable": true,
      "fixedWidth": "12rem"
    }
  ],
  "itemActionsMaxVisible": 1,
  "itemActions": [
    {
      "id": "edit-order",
      "method": "update",
      "label": "Редактировать",
      "icon": "pencil",
      "editor": {
        "mode": "modal",
        "fields": [
          {
            "attribute": "name",
            "label": "Наименование"
          },
          {
            "attribute": "plannedStart",
            "type": "DateTime",
            "label": "Начало"
          }
        ]
      }
    },
    {
      "id": "delete-order",
      "method": "deleteObject",
      "label": "Удалить",
      "icon": "trash"
    }
  ]
}

Linked child table

Place this beside the master table above. It remains without a usable linked request until production-order-selected exists, then sends sort and page state to the linked-objects endpoint.

json
{
  "id": "production-order-operations",
  "type": "table",
  "request": {
    "getSelectedObjectForEventId": "production-order-selected",
    "linkName": "OrderOperation",
    "reverse": false
  },
  "pagination": {
    "rowsPerPage": [10, 25]
  },
  "columns": [
    {
      "attribute": "link.sequence",
      "label": "№",
      "sortable": true,
      "fixedWidth": "5rem"
    },
    {
      "attribute": "name",
      "label": "Операция",
      "sortable": true,
      "minWidth": "20rem"
    },
    {
      "attribute": "durationMinutes",
      "label": "Минуты",
      "dataType": "Integer",
      "sortable": true,
      "fixedWidth": "8rem"
    }
  ]
}

The custom-request example in the capability map is a complete second data journey. It deliberately omits row mutations because custom IDs and arbitrary HTTP data do not establish MES object mutation semantics.

Verified demos

Open a standard object table with sorting and paging Open master selection, linked-attribute loading, filter state, and pagination Open fixed, minimum, flexible, and legacy width cases Open typed columns, constrained content, and selection-driven details Open runtime method and custom-request Table data sources

Open the Method request tab to traverse the actual shared request router, fixed-object endpoint resolver, and local POST /MetaMethods/Run handler. The three returned local orders prove that the method response becomes Table rows. The visible success toast comes from the shared runMethod lifecycle.

Then open the Custom request tab. The Table fetches another fixture application's JSON, extracts the nested panel.panels array through dataPath, uses type as its non-default idField, and renders explicit String and JSON column types. Sort the ID or type columns and switch between page sizes 1 and 2; those operations run against the fetched array in the client.

Exact parameter reference

Table panel:

Requests and query state:

Use the public guides for requests, filters, sorting, and pagination, values and renderers, panel events, and actions for their shared contracts. Then configure Attributes or Condition as a selection consumer.