Appearance
Table panel
Purpose and problems it solves
The table panel displays data in a table. Users can select rows, sort and filter data, move between pages, and run configured actions. The configuration defines the data source, requested attributes, columns, and responses to events. The panel indicates when data is loading or a request returns no rows.
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:
| Need | Request discriminator | Query work |
|---|---|---|
| Objects of one meta type | metaTypeName | Server |
| Objects linked to a selected parent | getSelectedObjectForEventId + linkName | Server |
| Objects whose attribute points to a selected parent | event + metaTypeName + linkedAttribute | Server |
| Rows returned by a meta method | methodName | Server/runtime method adapter |
| A non-MES JSON collection | customRequest | Fetch the full array; sort/page in the client |
Configure one way to retrieve data. If you configure several, the panel chooses the first matching option in this order: customRequest, methodName, a request through an attribute referencing the parent, a request through Link, then a standard request by meta type. If the request fails, the panel does not switch to the next option.
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. Select the parent in the source panel first: Table sends no POST /Object/find for this request while that selection is absent. Switching or deselecting the parent immediately clears the previous child rows and their selection, including the selection event used by actions and dependent panels. Select a row under the new parent before using actions that require a selected child. Table starts the new parent at the first page, preserving page size, sorting, and applied filters. An earlier request cannot restore the old parent's rows after this change.
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:
request.attributeList, when explicitly present;- 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.
sortableenables a header sort control except for method requests. For those requests, Table preserves the order returned by the method and does not offer a sort control.dataTypesupplies value type where metadata is absent, especially custom rows.maxRowsconstrains multiline renderer output.maxHeightconstrains 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 filters (findAttributes) belong to the registry. Before the first object query, Table copies request.findAttributes into that state only if no filter state exists for the panel. The query uses those defaults immediately; opening or applying the filter dialog is not a prerequisite. Table leaves the configuration unchanged.
Each configured condition uses field, compareType, and value, for example { "field": "isTemplate", "compareType": "EQUAL", "value": "true" }. Values are strings or null. See initial filter configuration for a complete example, the API request body, and linked-field rules.
filter.fields configures the fields users can edit in the filter dialog. The dialog copies the current registry filters into a draft. Apply replaces the applied registry filters, preserves page size, resets offset to 0, and triggers a query. Reset clears only the draft and sends no request. Click Apply after Reset to write [] to the registry and query without the editable filters. Cancel or closing the dialog before Apply discards the draft and preserves the applied filters.
Paging, sorting, refetching, and remounting reuse the applied state, including [] after Reset followed by Apply; they do not restore the configured defaults. EMPTY and NOT_EMPTY accept null without a comparison value. The comparison control remains usable for these operators, so you can choose another comparison without resetting the field. Its accessible name identifies the field whose comparison it changes.
When you reopen Table without changing its parent, Table restores its current page and sort state. If the parent changed while Table was closed, opening Table or running Reload Entities starts that parent's first page and clears the former child's selection. Both routes preserve applied filters, including [] after Reset and Apply. The column header shows the restored first sort rule when it matches a sortable column.
These filters are editable defaults. The linked-attribute strategy separately adds its required selected-parent equality filter to every request, so Reset followed by Apply does not remove the parent restriction. request.filter is declared but inert. request.sortParams supplies 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 configures the page-size menu; its first value supplies the initial request limit when no page state has been restored. Without this configuration or restored state, the first object request omits pagination. The renderer uses [3, 5, 10, 15] for the menu and 3 as a display fallback, but uses response pagination when the API supplies it. These display defaults do not impose a server limit. Set rowsPerPage explicitly for a known initial request limit.
Before changing the page, sort, filter, selected parent, or application, the application checks affected object editors. If an editor has unsaved changes, it asks whether to stay or discard them. Staying or closing the warning keeps the current context and draft; save or cancel the edits in the editor before trying again. Discarding clears the affected drafts and permits the requested transition. While an affected editor is loading or saving, the application blocks the transition and asks you to wait; retry after it finishes. This protection also applies without SignalR. A background SignalR event does not ask to discard changes: the editor keeps the draft and defers the affected data refresh until editing has finished.
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
actionsas anonButtonClickevent; openSchedule;selectObject;runMethod;http.
The overflow button has an accessible menu name and reports whether its row's menu is open.
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. Table renders its header and empty row area even before the first response. While the request is running, the DataTable receives its loading state and the empty area shows the shared loading text. A successful response replaces that area with the returned rows or the empty-state message.
After a successful empty response:
- the paginator stays available if the API reports a positive total record count, so you can leave an empty later page; otherwise it is hidden;
ui.emptyMessageis used when supplied;- otherwise the shared object-empty translation is used.
If an ordinary object search, linked-attribute search, or linked-object request fails, Table clears the previous rows and their selection. Actions therefore cannot keep using those previously selected rows. The empty area shows Could not load the table and Retry; the header and filter controls remain available. Click Retry to repeat the request with the current applied filters, pagination, and selected parent. Loading replaces the error during the retry; success replaces it with the returned rows or empty-state message. An older request that fails after a newer request starts does not replace the newer result with an error.
This error message is built in, not a configurable Table parameter. Method, single-object, and custom HTTP requests do not have this error/retry behavior.
Width fields:
| Field | Behavior |
|---|---|
fixedWidth | Fixed CSS width. Header/body content is truncated with an ellipsis; the table uses fixed layout when any effective fixed column exists. |
minWidth | Minimum useful width. The table may grow beyond its panel and the container scrolls horizontally. |
width | Legacy 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
- Resolve one request strategy from the panel request.
- Initialize configured filters only when registry filter state is absent, then resolve current pagination, sort, and filter state.
- Build response projection from explicit
attributeListor columns. - Fetch/normalize rows and mirror response/query metadata into the registry.
- Format rows with typed
attributesMapentries. - Render columns, current selection, and the applicable action surfaces.
- On page/sort/filter changes, update query state and fetch again (or process cached raw custom data).
- On linked-parent changes or deselection, clear old rows and their selection, reset the offset, and use the new parent GUID in query identity. Without a parent, wait for a selection before requesting children.
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 request limit: restored page state > first configured
rowsPerPage; without either, object requests omit pagination. - Row action visibility: configured
itemActionsMaxVisible>2. - Column width:
minWidth>fixedWidth> legacywidth> 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
TableRequesttype union. - The method executor does not forward Table filters, sorting, or pagination to the method call.
request.filteris inert; userequest.findAttributesfor initial object filters.- Initial filters are editable; they do not enforce permanent restrictions.
- 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
localeComparefor strings, numeric subtraction for numbers, and string conversion otherwise. It does not reproduce backend-specific or domain-specific ordering. - The error/retry message is built in for ordinary, linked-attribute, and linked-object collection requests; it is not configurable and does not cover method, single-object, or custom HTTP requests. There is no separate first-load skeleton.
widthcannot 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 sourcesOpen 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.
Initial filters and recovery
Open Table filters and recoveryWith project dependencies installed, run this command from the repository root:
bash
NUXT_PUBLIC_API_PREFIX=/local-api NUXT_LOCAL_FIXTURE_API_ENABLED=true pnpm -C apps/panel devSign in with any non-empty credentials and open Фильтры таблицы и восстановление (Table filters and recovery) from the application list. The demo link above opens the Russian interface. For English labels and help, open /en/app/local-table-request-lifecycle-demo on the same panel host by replacing the path in your browser's address bar.
The application help contains the same steps. All identifiers below belong to this local dataset. The local API supplies the objects and simulated responses; this demo does not establish what an external API returns for isTemplate or nested projections.
- On Initial filter, Table requests objects with the configured flat filter
{ "field": "isTemplate", "compareType": "EQUAL", "value": "true" }before you open the filter dialog. ExpectTEMPLATE-001andTEMPLATE-002and a filter count of1. In the filter dialog, click Reset, then Submit:OPERATION-001andOPERATION-002also appear. Reset alone edits the draft; Cancel keeps the applied filter. To repeat, set isTemplate to Equaltrueand click Submit. - On Parent and child rows, select
PARENT-A. In the child table, filter serialNumber with ContainKEEP, then click Submit. Expect six matching rows across two pages at five rows per page:KEEP-A-01throughKEEP-A-06. TheOTHER-Arows and allBrows are excluded. SelectPARENT-B: only the sixKEEP-Brows match. Reset and Submit remove the number condition while preserving the selected-parent restriction. Deselect the parent to clear the child rows and their selection. - The child table's unit column shows
шт.from the local API's nested object attributesmaterialDefinition→uom→designatorNational. The normal Table renderer displays that value from the response. - Select
PARENT-A, apply serialNumber ContainKEEPagain, sort the Number column, and select a child row. On the parent panel, click Fail next load and wait for its notification. Then click the child table's refresh control. The local API fails that request once; Table clears the old rows and selection and shows Retry. Click Retry: the rows return with the same parent and applied filter, without restoring the old child selection. - Return to the first child page with five rows per page and the same filter. Click Empty next page, wait for the notification, then advance one child page. The local API returns no rows once but keeps the total at six. Use the paginator to return to the previous page; the matching rows return with the applied filter and sort unchanged.
Reset simulation cancels pending simulated responses; it does not reset filters or pagination. Each simulation is consumed once and can be armed again. Pending simulations belong to this browser session, including its other tabs, and affect only this demo's child requests. Reset the simulation before starting another sequence, then select the parent and apply the filter described above.
Exact parameter reference
Table panel:
table.columns[].attributetable.columns[].labeltable.columns[].widthtable.columns[].fixedWidthtable.columns[].minWidthtable.columns[].maxHeighttable.columns[].sortabletable.columns[].maxRowstable.columns[].dataTypetable.selectionModetable.filter.fields[]table.pagination.rowsPerPage[]table.panelActions[]table.itemActions[]table.itemActionsMaxVisible
Requests and query state:
request.attributeList[]request.filter[]request.findAttributes[]request.sortParams[]request.metaTypeNamerequest.getSelectedObjectForEventIdrequest.linkNamerequest.reverserequest.linkedAttributerequest.customRequest.methodrequest.customRequest.urlrequest.customRequest.idFieldrequest.customRequest.dataPathrequest.methodNamerequest.parentrequest.objects[]findAttributes[].fieldfindAttributes[].compareTypefindAttributes[].valuesortParams[].attributesortParams[].objsortParams[].linksortParams[].sortDirection
Related guides and next task
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.
Live table updates
The notification hub currently accepts anonymous connections. Login in the panel does not protect hub access; the API owner must confirm access isolation before enabling it. The panel disables SignalR when its API uses /local-api, even when a hub URL is set. Use the real API to check live updates.
After login, the table reads its current page through REST and subscribes to changes to those records through SignalR. An event makes the table repeat its REST request and display the new values while retaining the filter, sort, and selected parent. Unsaved edits remain in the editor; save or cancel them to resume the deferred refresh.
The table stays subscribed only while it holds an accepted response of its current source. If a read fails, the table keeps showing the previous values but receives no live updates until its regular refresh succeeds; use the table refresh to restore them.
To enable this, the person deploying the panel obtains the full hub URL from the API owner, verifies connectivity and access isolation, and sets NUXT_PUBLIC_REALTIME_HUB_URL in the panel process environment. Use an absolute HTTP(S) URL including /hubs/notifications, without credentials, query parameters, or fragments. Restart the panel process after changing the value; for a static build, set it during the build and republish the output. An empty value disables SignalR. Application JSON does not need to change.
The API owner must enable event publication for the relevant attributes through notifyEventType in the type metadata. The panel variable does not enable server events. After saving such an attribute, the server sends an event and the table repeats its request.
Entity subscription. JoinGroup(entityType, entityId) subscribes the client to changes to one known entity. How its type and identifier were obtained does not restrict the subscription itself.
Table data refresh. The Table adapter observes records on the current page and repeats its query after their events. The Table strategies method and custom are not yet integrated with this automatic reread.
List membership. An entity subscription is not a subscription to a query result. Use the table's regular refresh to discover new records or changes to which records match the filter.
Other views. Trees, BPMN, Gantt, and text editors are not yet integrated with automatic data refresh. No notification inbox is added.