> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.itential.com/itential-platform/6/developer-guide/table-control/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.itential.com/_mcp/server.
# Table control
> How to create and configure the Table Control PHUI class to display, sort, filter, and manage entity data in Itential Platform pages.
Table Control is a PHUI class used to create tables that list data. It is useful when you want to list a body of entities (fewer than 100) with multiple properties, or when a data set is too large to display all at once.
Table Control offers several helpful features:
* Actions on a single entity
* Actions on multiple entities at the same time
* Filtering on a specific field or fields
* Sorting on a specific field or fields
## Create a table control
The render of a Table Control is handled by the browser's JavaScript engine. The page can be as simple as the following:
```html
content
title-bar
title Device Management
workspace
```
The table appends to the `devices-table` div. Obtain the context of the table using JavaScript:
```javascript
var context = document.getElementById('devices-table');
```
Table Control creates a table in the context once you provide additional data about how you want the table to behave. You define this in an object called `tableSeed`.
For example, to create a table representing a list of horses — where each horse is an entity and you want to show its name, color, and age — create your `tableSeed` object as follows:
```javascript
var tableSeed = {
fields: {
name: {
displayName: 'Name'
},
color: {
displayName: 'Color',
},
age: {
displayname: 'Age',
}
}
};
```
This is the simplest valid `tableSeed` object. A `tableSeed` must have a `fields` property where you define the properties to display as columns. At minimum, specify a `displayName` — a human-readable description of the field. The key for each field maps to the properties of the horse objects.
Now construct the table:
```javascript
var horseTable = new PHUI.Table(context, tableSeed);
```
You should see a table with columns but no rows. Keep the `horseTable` instance — you will need it later.
## Add entities
### Add the first entity
Add static data to your table by creating your first horse as a plain JavaScript object:
```javascript
var myFirstHorseJohn = {
name: 'John',
color: 'Green',
age: '42'
};
```
Add the horse to the table with the `pushEntity` method:
```javascript
horseTable.pushEntity(myFirstHorseJohn);
```
Once pushed, the table immediately creates the row, mapping `name`, `color`, and `age` to the fields defined in `tableSeed`.
### Table methods
There are three basic methods for adding entities to a table:
| Method | Description |
| ---------------------------------- | --------------------------------------------------- |
| `myTable.pushEntity(entity)` | Add a single entity to the table. |
| `myTable.concatEntities(entities)` | Add an array of entities to the table. |
| `myTable.setEntities(entities)` | Reset the table to display a new array of entities. |
Additional methods exposed by Table Control:
| Method | Description |
| ----------------------- | --------------------------------------------------------------------------- |
| `myTable.getEntities()` | Returns an array of all objects the table currently knows about. |
| `myTable.getMeta()` | Returns an object with `numEntities`, a filter object, and a sorter object. |
| `myTable.remove()` | Removes the table from the DOM. |
Table Control automatically handles sorting and filtering as you add entities. Even when concatenating horses, they appear in the appropriate row based on the current sorter and are hidden if filtered. You can concatenate an array of entities at once:
```javascript
var newHorses = [
{
name: 'Sally',
age: 24,
breed: 'Mustang',
gender: 'Female'
},
{
name: 'Rebecca'
}
];
horseTable.concatEntities(newHorses);
```
Note that the object properties do not need to match the fields exactly — `Rebecca` has no age, and `Sally` has a `breed` and `gender` that are not defined as table fields.
## Update entities
When an object is added to a table, it inherits a Table Control super class. Table Control stores objects by reference and handles view updates behind the scenes. This means you can modify the original object and Table Control will update the table accordingly.
You can also use `table.getEntities()` to retrieve all entity objects.
To remove `John` from the table:
```javascript
myFirstHorseJohn.removeFromTable();
```
The object still exists; the table simply no longer tracks it. The `removeFromTable` method is added to every object passed through `pushEntity`, `concatEntities`, or `setEntities`.
You can also update an entity property by reassignment, and it takes effect in the table immediately:
```javascript
// Note that you still have newHorses.
var sally = newHorses[0];
sally.color = 'black';
// Color updates from "N/A" to "black" in the cell immediately.
setTimeout(function() {
sally.color = 'white';
// Color updates in the view immediately again.
}, 5000);
```
## Sort and filter
Enable sorting and filtering by setting `canSort` and `canFilter` to `true` on specific fields in your `tableSeed`:
```javascript
var tableSeed = {
fields: {
name: {
displayName: 'Name',
canSort: true,
canFilter: true
},
color: {
displayName: 'Color',
canSort: false,
canFilter: false
},
age: {
displayname: 'Age',
canSort: true,
canFilter: false
}
}
};
```
If no field is marked as `defaultSort`, the first field marked `canSort` is used as the default sorter.
To configure a specific field as the default sorter:
```javascript
var tableSeed = {
age: {
displayname: 'Age',
canSort: true,
defaultSort: -1,
canFilter: false
}
};
```
The `defaultSort` property accepts `1` (ascending) or `-1` (descending).
## Define row actions
The `rowActions` function operates at the entity level and allows users to perform an action on a single entity. For example, to allow editing and selling horses:
```javascript
var sellHorse = function(horse, event) {
if (window.confirm('Are you sure you want to sell ' + horse.name + '?')) {
horse.removeFromTable();
}
};
var tableSeed = {
fields: {
name: {
displayName: 'Name',
canSort: true,
canFilter: true
},
color: {
displayName: 'Color',
canSort: false,
canFilter: false
},
age: {
displayname: 'Age',
canSort: true,
canFilter: false
}
},
rowActions: {
edit: {
action: function(horse, event) {
// ...open modal to get newColor from user...
horse.color = 'newColor';
},
class: 'edit',
primary: false,
disabled: false
},
sell: {
action: sellHorse,
class: 'sell',
primary: true,
disabled: false
}
}
};
```
The `class` property is a CSS class set on the button for the action, which appears in a new cell on the right-hand side of the row. Each action's `action` property is a function that receives the entity itself.
The second parameter is the DOM event from the button click, as you would normally expect from `onclick` actions in JavaScript. The `primary` property specifies which action triggers on row click. At most, one primary action may be specified. If `primary` is not explicitly set, the first action is primary by default.
## Define batch actions
To allow users to perform the same action on multiple rows simultaneously, define `batchActions` in your `tableSeed`:
```javascript
var brandHorses = function(horses) {
for (var i = 0; i < horses.length; i++) {
horses[i].color = 'red'; // reflected in the table immediately
}
};
var tableSeed = {
batchActions: {
'Brand Horses': {
action: brandHorses,
class: 'brand',
disabled: false
}
}
};
```
Table Control creates the interface in the DOM. Users can then check multiple rows and select the **Brand Horses** button at the bottom of their viewport.
## Select entities
To programmatically select certain entities — for example, pre-checking rows for horses the user already owns — set the `selected` flag on the entity:
```javascript
var someHorse;
someHorse.selected = true;
```
This causes the horse to appear with its checkbox already checked when pushed to the table.
The `selected` flag behaves like other entity properties and can be reset in real time:
```javascript
setTimeout(function() {
horsesTable.getEntities()[0].selected = true;
}, 5000);
```
After five seconds, the first horse becomes selected instantly.
## Update action properties and override actions
Row actions or batch actions often need to change at runtime — for example, disabling an action for specific entities or setting a `processing` class during an API call. Each entity object has an `actions` property, which you can set in two ways:
1. Define it yourself when adding entities via `pushEntities`, `concatEntities`, or `setEntities`.
2. Let Table Control set it automatically.
If you allow Table Control to set the `actions` property, all horses will have it assigned with `disabled: false` by default (for example, `horses[i].actions.edit.disabled = false`).
Regardless of how `actions` is set, you can modify it at runtime and changes take effect immediately:
```javascript
var currentHorses = horseTable.getEntities();
// Disable the edit action on the first horse.
currentHorses[0].actions.edit.disabled = true;
// Update editHorse to show a processing icon during a server call.
var editHorse = function(horse) {
horse.actions.edit.class = 'processing';
utilities.http('POST', '/horses/sell/' + horse.name)
.then(function() {
horse.actions.edit.class = 'edit';
});
};
```
You can also replace entire actions:
```javascript
someHorse.actions.edit = {
action: function() {
console.log('edit has changed');
},
class: 'new class'
}
```
## Advanced techniques
### Parcel data
Table Control supports chunking data via AJAX calls for lists too large to store in browser memory. To enable this, add the `fetch` function to your `tableSeed`:
```javascript
var tableSeed = {
parcelControl: {
fetch: function(tableData, isAppend, callback) {
/**
* @param {Object} tableData
* @param {number} tableData.numEntities - The number of entities currently shown.
* @param {Object} tableData.sorter - Used when sorting is enabled.
* @param {number} tableData.sorter.direction - 1 = descending, -1 = ascending.
* @param {string} tableData.sorter.field - The field name used for sorting.
* @param {Object} tableData.filter - Key-value pairs where the key is a field name
* and the value is a string to match.
* @param {boolean} isAppend - true = append data; false = replace data.
* @param {fetch~requestCallback} callback - Handles the returned data.
*/
/**
* @callback fetch~requestCallback
* @param {Object[]} newEntities - The response data.
* @param {boolean} exhausted - false if more data is available; true when all data returned.
*/
var options = {
start: isAppend ? tableData.numEntities : 0,
limit: 25,
sort: tableData.sorter,
entity: 'device'
};
if (!isEmpty(tableData.filter)) {
options.filter = tableData.filter;
}
utilities.http('POST', '/device_management/devices/filtered', {
options: options
})
.then(function(res) {
if (!res) return callback([], true);
if (res.total <= res.list.length) return callback(res.list || [], true);
return callback(res.list || [], false);
})
.catch(handleError);
}
}
};
```
A `fetch` call is made whenever Table Control needs more entities. The table uses lazy loading (infinite scrolling) to display large amounts of data without making calls with huge payloads or overwhelming browser memory. `fetch` is called repeatedly as the user scrolls. If the user changes the sorter or filter, `fetch` is called again to reset the table data with the new parameters.
If a table uses parcelling, it must sort and filter by making calls to the server. If it does not use parcelling, Table Control handles sorting and filtering on the client. Never sort or filter data client-side that you know to be a subset of a larger server-side list.
There are two reasons `fetch` is called:
* To append to the list on scroll.
* To reset entities due to a filter or sort change.
The `isAppend` flag indicates which case applies. When not appending, start from `0` to ensure the list resets on a filter or sort change.
The callback requires two arguments:
| Argument | Description |
| ---------- | ---------------------------------------------------------------------------- |
| Argument 1 | A list of new entities to append or set on the table. |
| Argument 2 | Whether the list is exhausted (for example, `numEntities >= totalEntities`). |
When Argument 2 is `true`, infinite scrolling stops calling `fetch`.
### Style and size columns
Styling can be done with CSS class modifiers. Table Control adds a class to all `` and ` | ` tags corresponding to the field name. For example, a ` | ` element for the Name field has the class `name`. This makes it possible to set the width of a specific column using CSS.
> How to create and configure the Table Control PHUI class to display, sort, filter, and manage entity data in Itential Platform pages.
|