Table
This widget shows a tabular view of data.
Table Data
Refer to the generic widget page for a description of the general principles governing the treatment of data retrieved from a dataSource
Data from Table Holder / KPI Table
The widget fetches the table data from the system and does auto discovering of the header columns.
{
"dataSource": {
"type": "read",
"path": "/System/Core/Examples/WebStudio/Tables/SalesOrders"
}
}
The table widget can display data from any table property in the back-end, not just those in table-holders or KPI tables. This is achieved by including the table property name in the path.
|
By using a "read-write" action in the dataSource
pipeline the data will be saved back to the source table when the user clicks the "Save" toolbar button, without the need to implement explicit "onSave" logic.
The path in this case must also contain the name of the object’s table property, which in the case of a table holder is called .TableData.
{
"dataSource": {
"type": "read-write",
"path": "/System/Core/Examples/WebStudio/Tables/SalesOrders.TableData"
},
}
Options
The options section is used to control various aspects of the appearance and behavior of the table.
{
"options": {
"allowSorting": false,
"alternateColumnColoring": true,
"alternateRowColoring": true,
"editable": false,
"multi": true,
"multiMax": 3,
"multiMin": 1,
"pageSize": 20,
"pagination": true,
"showHoverHighLight": true,
"showRefreshButton": true,
"showSelectedRow": false,
"showToolbar": true,
"showRowNumbers": true,
"submitButton": {
"label" : "CLICK ME"
},
"style": {
"backgroundColor": "Gainsboro",
"color": "blue",
"fontFamily": "\"Courier New\", Courier, sans-serif",
"fontWeight": "bold",
"fontSize": "20px",
"textAlign": "left",
"whiteSpace": "pre-line"
},
"header": {
"style": {
"backgroundColor": "DimGrey",
"color": "white",
"fontWeight": "bold"
}
},
"refreshInterval": 30
}
}
name | description |
---|---|
|
When editable is set to true the cell values can be modified. Note that this only affects the original dataset if the table is saved and the user has the permissions to overwrite data. |
|
Can disable sorting icons and sorting functionality. |
|
Apply alternating light and dark column backgrounds. |
|
Apply alternating light and dark row backgrounds. Can be used together with |
|
Used to define |
|
Allow selection of multiple rows. When
The |
|
Minimal needed selected rows before the table onSubmit action can be triggered. |
|
Maximum allowed selected rows. |
|
When set to |
|
With |
|
Array of conditional formatting rules. See below |
|
Enable/disable hover effect on the table rows. |
|
Background color will change when a table row is clicked. Defaults to true when |
|
Set this to |
|
When set to |
|
Use to hide the complete toolbar. |
|
Property used to define static style settings for the table header and body. See Table styles for more details. |
|
Property used to define static style settings for the table based on the active theme. For example:
|
|
The text-label of the submit-button, which appears in the bottom right corner of the table when an
|
|
Refresh with an interval in seconds. |
Table Styles
There are a number of properties available in the model which allow the table header and content to be styled. The order of precedence in which styles are applied depends on where they are declared, as shown here from lowest to highest:
-
options
: For all widgets, overall style settings can be defined in theoptions
property, as shown above. -
options.rules
: Conditional stylingrules
declared in theoptions
property provide a means to control the appearance of matching rows. Rules don’t affect the header appearance. -
schema
: Column specific styles can be defined in theschema
section of the model. These affect both the table header and data. -
options.header
: Used to explicitly control the appearance of the header row. When set it takes precedence over the style settings in both theoptions.style
andschema.style
sections. -
schema
specificheader
: The appearance of a column header can be further customized on a column by column basis using theheader
element in theschema
section. -
schema
specificrules
: Conditional styling defined in theschema
section affects only the data cells for which therule
conditions match, as opposed to those in theoptions
, which affect the row as a whole.
For each of the properties listed style
and/or styleByTheme
can be defined. styleByTheme
settings override style
settings at the same level.
Conditional Styling Using Rules
The appearance of individual data rows and/or cells can be conditionally set using rules
defined inside the options
property, for rows, and in the schema
elements for cells.
The rules
property is an array, allowing multiple rules
to be applied to the same or different fields.
When value
or range
properties are used as the rule condition, each rule refers to a single field in the data for input. Which data
field is used can either be explicitly specified using the name
property, or implicitly by virtue of the schema element the rule resides in. When the data field value matches the rule value
or range
, the style
/styleByTheme
settings are applied.
{
"options": {
"rules": [
{
"name": "temperature",
"value": 20,
"style": {
"backgroundColor": "green",
"fontSize": "15px",
"fontWeight": "normal"
}
},
{
"name": "temperature",
"range": {
"from": 22,
"to": 26
},
"style": {
"fontWeight": "bold"
}
}
]
}
}
name | description |
---|---|
|
A
This second example shows how a
|
|
Name of the data field the rule condition refers to. For rules in the model |
|
Used to define a numeric |
|
Define the appearance of the row or cell when the rule condition is met. |
|
Can be one of the following options:
|
|
The exact value to match the data field to. |
Schema
The table schema
is used to control which fields in the data to show, how they are shown and in what order.
The schema can be statically defined in the model but can also be provided by a dataSource . If this is the case, the model schema should be omitted, since it takes precedence over any returned by the data source UNLESS the dataSource contains a type field set to "table" in which case the schema in the dataSource is used!. (Also refer to the widget dataSource rules.)
|
{
"schema": [
{
"name": "id",
"hidden": true
},
{
"title": "Population",
"name": "population",
"filter": "select",
"type": "number",
"editable": true,
"value": 100
},
{
"title": "Value",
"name": "value",
"filter": "slider",
"type": "number",
"numberOfDecimals": 2,
"sort": "desc"
},
{
"title": "Fixed column",
"value": "Click here",
"actions": {
"onClick": {
"type": "notify",
"text": "Fixed column cell clicked!"
}
}
}
]
}
name | description |
---|---|
|
Instead of making every cell editable with setting |
|
The |
|
Used to define which input control is shown when the table filter is activated. Options are:
|
|
When type is |
|
Used to define |
|
Property used to control column visibility (optional) |
|
When column names are repeated in the schema section, WebStudio will automatically generated a unique |
|
This option can be used for hierarchical data structures, it enables collapsing
and expanding table rows. Set |
|
Cell value can be selected from a dropdown list containing predefined values. |
|
Key name of the data field displayed in this column. The name is also used by default to uniquely identify columns in the table. If the same named data field needs to appear in the table multiple times an |
|
This property only applies when the field is of type |
|
Custom styling rules. |
|
Property to define default sorting on the column. Accepted values: |
|
Optional styling for the column. |
|
Optional theme specific styling for the column. |
|
Column title. |
|
Tooltip text to display when the mouse hovers over the table column header or a table cell. Depending on how the property is configured either a header or a cell tooltip will be shown. The options are as follows:
|
|
The
|
|
A default field value can optionally be provided. It is shown when the |
|
Column |
Multiple schema items with the same name
To illustrate the use of repeated names in the schema, consider an example where the data rows include a "timestamp" field expressed as an epoch integer and we want to show the raw number as well as a formatted version.
The data could be something like:
{
"data": [
{
"event": "machine start",
"timestamp": 1635408094000
},
{
"event": "machine stop",
"timestamp": 1635416051000
}
]
}
In the schema the timestamp appears twice. First formatted as a number and then as a date:
{
"schema": [
{
"name": "event",
"title": "Event Description"
},
{
"name": "timestamp",
"type": "number",
"title": "raw timestamp"
},
{
"name": "timestamp",
"type": "date",
"title": "Date-Time",
"format": "YYYY-MM-DD HH:mm:ss"
}
]
}
Note: Looking at the work-model for this table, we see that an id
= "timestamp_2" was automatically generated in the
schema for the second occurrence of the column. It can also be explicitly added to the initial model, in which case we
are free to choose the id
value:
{
"schema": [
{ "name": "event", /* etc ... */ },
{ "name": "timestamp", /* etc ... */ },
{
"name": "timestamp",
"id" : "formatted_timestamp",
"type": "date",
"title": "Date-Time",
"format": "YYYY-MM-DD HH:mm:ss"
}
]
}
Dealing with composite data
In most cases the data presented to the table is likely to be in the form of a flat array where each element contains a set of named fields and their values. Something like:
{
"data": [
{
"value": 26,
"location": "Eindhoven"
},
{
"value": 18,
"location": "Cologne"
}
]
}
Mapping these to columns in the table is simply a matter of setting the name
field in the schema
definitions to the corresponding field in the data… like so:
"schema": [
{
"name": "location",
"title": "City",
},
{
"name": "value",
"title": "Temperature"
}
],
Occasionally, you may need to deal with data that has composite values as shown:
{
"data": [
{
"temperature": {
"wetBulb": 22,
"dryBulb": 26
},
"location": "Eindhoven"
},
{
"temperature": {
"wetBulb": 17,
"dryBulb": 18
},
"location": "Cologne"
}
]
}
One way to deal with this would be to transform the data by moving all the fields to the root object. Fortunately this is not necessary since you can bind to nested fields directly. Simply assign the dot-notation property key to the name
parameter in the schema
.
"schema": [
{
"name": "location",
"title": "City"
},
{
"name": "temperature.wetBulb",
"title": "Wet Bulb Temperature"
},
{
"name": "temperature.dryBulb",
"title": "Dry Bulb Temperature"
}
],
Grouped columns
{
"schema": [
{
"title": "Employee",
"columns": [
{
"title": "Name",
"name": "firstName"
},
{
"title": "Last Name",
"name": "lastName"
},
{
"title": "Age",
"name": "age",
"filter": "slider"
}
]
},
{
"title": "Address",
"columns": [
{
"title": "Street",
"name": "street",
"filter": "text"
},
{
"title": "City",
"name": "city"
},
{
"title": "Country",
"name": "country",
"filter": "select"
}
]
}
]
}
Dropdown selection
Select a predefined input value from a dropdown. Inputs need to be defined in the items
array. This can be an array of strings or objects containing a label
and value
. The latter can be used when the text to show should be different from the value
.
Example items
as string array:
{
"schema": [
{
"name": "day",
"title": "Day of week",
"items": [
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Saturday",
"Sunday"
]
}
]
}
Example items
by defining labels and values:
{
"schema": [
{
"name": "day",
"title": "Day of week",
"items": [
{
"label": "MON",
"value": "Monday"
},
{
"label": "TUE",
"value": "Tuesday"
}
]
}
]
}
Formatting
{
"schema": [
{
"name": "temperature",
"title": "Temperature",
"type": "number",
"numberOfDecimals": 2,
"engUnit": "°C"
},
{
"name": "timestamp",
"title": "Timestamp",
"type": "date",
"format": "YYYY-MM-DD HH:mm:ss.SSS"
},
{
"name": "status",
"title": "Status",
"enumMode": "valueToName", // valueToName (default), nameToValue
"enum": {
"Dry": 1,
"Wet": 2
}
}
]
}
Enum formatting can do valueToName
(default) or nameToValue
.
-
valueToName
: Table cell value will be matched with a value of an enum and the name will be shown in the table cell. -
nameToValue
: Table cell value will be matched with a name of an enum and the value will be shown in the table cell.
Style
Cell or column based styling can be defined in the style
within a schema
element.
{
"schema": [
{
"name": "value",
"title": "Value",
"style": {
"backgroundColor": "#00c302",
"fontSize": "15px",
"fontWeight": "bold",
"whiteSpace": "pre-line"
}
}
]
}
Conditional styling on schema level applies for to cell. The name
field is optional and can be defined in case the
value match needs to be done with another data field. The styles of all matching rules will be applied to the cell.
Cell based rules:
{
"schema": [
{
"name": "value",
"title": "Value",
"style": {
"backgroundColor": "#00c302",
"fontSize": "15px",
"fontWeight": "bold",
"whiteSpace": "pre-line"
},
"rules": [
{
"value": 1,
"style": {
"backgroundColor": "green",
"fontSize": "15px",
"fontWeight": "normal"
}
},
{
"value": 2,
"style": {
"color": "yellow"
}
},
{
"range": {
"from": 5,
"to": 10
},
"style": {
"color": "yellow"
}
}
]
}
]
}
Hierarchical Data
Data field has a key children
, which is used in schema with isExpander
to create hierarchical table layout.
"data": [
{
"Item": "Pencil",
"Total": 189.05,
"Unit Cost": 1.99,
"children": [
{
"Item": "Pencil",
"Total": 59.7,
"Unit Cost": 1.99,
"OrderDate": "2019-06-08"
},
{
"Item": "Pencil",
"Total": 59.7,
"Unit Cost": 1.99,
"OrderDate": "2019-06-10"
}
]
}
]
"schema": [
{
"name": "children",
"collapsedIcon": "▷",
"expandedIcon": "▽",
"isExpander": true
},
{
"name": "Item",
"title": "Item"
},
{
"name": "Total",
"title": "Total"
},
{
"name": "Unit Cost",
"title": "Unit Cost"
},
{
"name": "OrderDate",
"title": "Order date",
"type": "date",
"format": "YYYY-MM-DD"
}
],
Toolbars
Customizing toolbars has become available for every widget. See Toolbars.
State
The table widget maintains some dynamic state information in the state
property to record the currently selected
row(s) as well as the active search and filter options.
{
"state": {
"selectedRowIndex": [],
"filters": [
{
"name": "Count",
"value": [
2,
5
]
}
],
"search": {
"value": ""
}
}
}
Field | Description | |
---|---|---|
|
list of 0 based row indices of selected rows |
|
|
boolean flag indicating whether the bar used to edit column filters is visible or not. |
|
|
boolean flag indicating whether the global search input box is visible or not. |
|
|
List of filter objects |
|
|
Name of the column the filter is applied to |
|
|
Filter value. The content of this field depends on the type of the filter. |
|
|
Mode selected for the
|
|
|
The
|
|
|
Search text |
These may be set in the initial model, but it is more common to access and manipulate the values at run-time.
Note: If the filter
type is set to "none" in the schema
for a particular column, the filter state is ignored for that column.
Note: The state properties can be modified using action pipelines to explicitly set the filters, selected rows or search values. When applying such changes the current state fields need to be overwritten in their entirety. Changing fields inside the root level properties is not supported.
To put this into perspective, consider the following example: Suppose we want to change the "range" filter on the "Count" field so it ends at 10 rather than 5, as in the example above.
We might be tempted to do something like this:
{
"type": "modify",
"id": "table",
"set": [
{ // This is not supported !
"name": "model.table.state.filters.0.value.1",
"value": 10
}
]
}
What we need to do instead, is to provide the complete filter array
{
"type": "modify",
"id": "table",
"set": [
{
// The model field being modified is the "filter" array
"name": "model.table.state.filters",
// The value assigned to it must include all filter settings to be applied.
"value": [
{
"value": [
3,
10
],
"name": "Count"
}
]
}
]
}
In this case, only one field is involved in the filter, but if there were more, they would all need to be included in the
filters
array even if only one of them changes.
Actions
-
onSave
: Save button is clicked and all rows are in the message. -
onSelect
: Single row is selected. -
onSelectionChanged
: Perform an action each time the selection is changed. -
onSubmit
: Submit button is clicked and selected rows are in the message. payload.
onClick
An onClick
action defined in the schema
is used to trigger pipeline execution when any field in the relevant column is clicked.
{
"schema": [
{
"name": "value",
"title": "Value",
"actions": {
"onClick": [] // Can be a single action or action pipeline.
}
}
]
}
The input message payload
will contain the complete row data and the selected cell info. Example:
{
"row": {
"_idx": 1,
"name": "Temperature",
"value": 26
},
"cell": {
"_idx": 2,
"name": "value",
"value": 26
}
}
Example to delete a row from the table using onClick
The schema definition below shows how to add a column to your table containing an icon, which can be clicked to delete a row.
{
"schema": [
{
"type": "string",
"value": "🗑",
"actions": {
"onClick": [
{
"type": "transform",
"aggregateOne": [
{
"$project": {
"removeFromArray": [
{
"name": "model.data",
"idx": {
"$subtract": [
"$row._idx",
1
]
}
}
]
}
}
]
},
{
"type": "modify",
"refresh": false,
"id": "self"
}
]
}
}
]
}
In the pipeline, the delete is performed in two steps.
-
From the incoming message,
row._idx
is used to initialize theremoveFromArray
parameter needed by themodify
action. Since therow._idx
is provided as a 1-based number, we need to subtract 1 before assigning it. The output message from the first step after clicking on the icon in the first table row looks like this:{ "payload": { "removeFromArray": [ { "name": "model.data", "idx": 0 } ] } }
-
modify
is invoked on the table, without specifying theremoveFromArray
property which will be taken from the incoming message. Therefresh
: false setting prevents the data from being reloaded from the back end when the table uses adataSource
onSave
Gets invoked when the save button is pressed. The input message payload
contains all data rows, unlike the onSubmit
action hook, which only receives selected rows in the message. The save button appears in the toolbar when either of the following is true:
-
The table has a
dataSource
of type "read-write". -
An
onSave
action pipeline is configured.
The onSave
action hook is not required when the dataSource
type is "read-write", since clicking the save button automatically writes the data rows back to the server.
A more interesting scenario presents when the dataSource
is read-only and some additional processing is needed before the data can be saved. Another is when a specific server endpoint needs to be used to write the data to the server.
In the example below, the dataSource
is configured to read from a Table Holder object while onSave
is configured to write the data rows back. Note that the path
in the write
action needs to explicitly reference the TableData
object property!
{
"actions": {
"onSave": {
"type": "write",
"path": "/System/Core/Examples/WebStudio/Tables/SalesOrders.TableData"
}
},
"dataSource": {
"path": "/System/Core/Examples/WebStudio/Tables/SalesOrders",
"type": "read"
},
}
onSelect
Gets invoked when a row is selected. The input message payload
contains the selected row data.
{
"actions": {
"onSelect": [
{
"type": "function",
"lib": "library-name",
"func": "function-name",
"farg": {} // This will be set automatically.
}
]
}
}
Transforming the selected row data and invoke a function:
{
"actions": {
"onSelect": [
{
"type": "transform",
"aggregateOne": [] // This will return one object instead of an array.
},
{
"type": "function",
"lib": "library-name",
"func": "function-name",
"farg": {} // This will be set automatically.
}
]
}
}
Send a message to another widget with selected row data:
{
"actions": {
"onSelect": [
{
"type": "send",
"to": "widget01"
}
]
}
}
onSelectionChanged
Gets invoked when a row is (de)selected. The input message payload
contains all the selected rows (array).
{
"actions": {
"onSelectionChanged": [] // Can be a single action or action pipeline.
}
}
onSubmit
The selected rows (array) are set on the input message payload
. Whether rows can be selected with respect to the
minimum and maximum number of selected rows, can be set with multi
, multiMin
and multiMax
in the
Options.
{
"actions": {
"onSubmit": []
}
}
Receive messages (Send Topics)
This widget receives messages from others. Besides the generic, this widget also support topics:
-
selectRows
: Can be used to select one or more rows in the table. The messagepayload
should contain arowIndex
array, which contains the index(es) of the rows which need to be selected.
Here’s an example to send a message to a table widget with an array of the selected rows:
{
"type": "send",
"to": "table",
"message": {
"topic": "selectRows",
"payload": {
"rowIndex": [
5,
6
]
}
}
}
Collect
Collect without a topic
defined will result in the data rows.
This widget supports the following topics to collect data:
-
No topic defined: collects all the data rows.
-
selectedRows
: collects the selected rows data.
Here’s an example of getting the selected rows data from a table widget by using an onClick action of a text Widget:
{
"type": "text",
"text": "Click here",
"actions": {
"onClick": [
{
"type": "collect",
"from": "table",
"message": {
"topic": "selectedRows"
}
},
{
"type": "consoleLog",
"tag": "Selected Row Data"
}
]
}
}