UI JSON elements – forms

In this context, ‘form components’ are defined as visual and interactive UI elements that either display information and facilitate data modification and enable the initiation of user actions.

In this section, we will not focus on layout or layout-related components like group boxes or tabsheets. It’s recommended to first familiarize yourself with layout principles before delving into forms.

Input

Inputs are the most basic and frequently used form elements. Text input is the most common, but HTML supports a variety of input types. However, it’s important to note that the behavior of some input types may vary across different web browsers.

{
    "variables": {
        "person": {
            "firstName": "First Name"
        }
    },

    "body": {
        "elements": [
            {
                "id": "edtFirstName",
                "element": "input",
                "field": "model.firstName",
                "description": "model.descriptionFieldName",
                "title": "@translations.person.firstName",
                "attributes": {
                    "type": "text"
                }
            }
        ]
    }
}

Although ‘input’ is a basic HTML element, we have a dedicated provider for it. This provider allows us to augment the schema with extra features, facilitating the creation of composite UI parts that fulfill our specific needs. For instance, the standard input element doesn’t inherently support a field property nor does it include a label by default. Our provider can generate a composite UI consisting of a label and an input element, along with binding expressions. These expressions link the input to the binding context, enabling two-way data binding for the input.

When an input element represents a referential relationship, we can specify the desired preview and determine the lookup method for setting the field value. Additionally, it has the capability to perform lookups based on the exact value typed in. If a custom value is entered and a lookup is defined, the underlying system will conduct a search for the specified field. Typically, while the code value is defined in the input, the relationship is established through the ‘id’ property. The lookup details are predefined, so the input’s role is limited to displaying and updating values based on user interactions. The actual lookup and preview settings are defined not on the UI but on the dataset that the input binds too.

Additionally, some inputs may offer support for a description value. This feature is optional, but when defined, it leads to the creation of a descriptor UI. This UI displays the description value, which is determined based on the context binding path.

For all input-related elements, you can make them read-only by setting the ‘readonly‘ attribute to true.

{
    "body": {
        "elements": [
            {
                "id": "id",
                "element": "elementType",
                "attributes": {
                    "readonly": true
                }
            }
        ]
    }
}

Memo

Where input is a single text value, a memo is a multi line text input using a text-aria under the hood. The memo provider builds a composite UI including a label, the text-aria and binding expressions required.

{
    "variables": {
       ... variables ...
    },
    
    "body": {
        "elements": [
            {
                "id": "memoNotes",
                "element": "memo",
                "title": "@translations.labels.notes",
                "field": "model.notes"
            }
        ]
    }
}

Note that you can set the cols and rows attributes as required.

Checkbox

In standard HTML, an input of type “checkbox” supports only true and false values. However, if a tri-state checkbox is needed that accommodates null, true, and false as possible values, a custom element is required. This custom element will cycle through the three values.

Similar to the input element, we have a custom provider that generates the necessary custom element. This provider also sets up the binding expressions and properties according to the defined intent.

{
    "variables": {
       ... variables ...
    },
    
    "body": {
        "elements": [
            {
                "id": "cbIsActive",
                "element": "checkbox",
                "title": "@translations.labels.isActive",
                "field": "model.isActive",
                "attributes": {
                    "tristate": true
                }
            }
        ]
    }
}

Date

The date is also a input type but for browser consistency it is better to use a custom element.

{
    "variables": {
       ... variables ...
    },
    
    "body": {
        "elements": [
            {
                "id": "edtDate",
                "element": "date",
                "title": "@translations.labels.startDate",
                "field": "model.startDate"
            }
        ]
    }
}

DateTime

The datetime component is similar to a date component but includes an additional time aspect. You can set the ‘include-seconds‘ property to either include or exclude the seconds input from the UI.

{
    "variables": {
       ... variables ...
    },
    
    "body": {
        "elements": [
            {
                "id": "edtDateTime",
                "element": "dateTime",
                "title": "@translations.labels.start",
                "field": "model.start",
                "attributes": {
                    "include-seconds": true
                }
            }
        ]
    }
}

Duration

The duration input is designed for displaying and editing duration values. It features a distinct approach for field type and display type. Although the duration is represented by a single field for binding, it is edited in terms of days, hours, minutes, or seconds.

Whenever any of these units (day, hour, minute, or second) is updated, the total duration value is recalculated and updated back to the source field. This component essentially acts as a visual value converter, transforming individual time units into a cohesive duration representation.

{
    "body": {
        "elements": [
            {
                "id": "edtEstimatedDuration",
                "element": "duration",
                "title": "@translations.labels.estimatedDuration",
                "field": "model.estimatedDuration"
            }
        ]
    }
}

Buttons

Buttons are supported by a provider that overrides the default HTML button. Rather than directly defining the binding expression on button attributes, we utilize actions specified in the schema for this purpose.

Button

{
    "variables": {
       ... variables ...
    },
    
    "actions": [
        {
            "id": "greet",
            ... action details ...
        }
    ],
    
    "body": {
        "elements": [
            {
                "id": "btnGreet",
                "element": "button",
                "title": "@translations.buttons.greet",
                "action": "greet"
            }
        ]
    }
}

In scenarios when you need icons on your button you can use icon buttons.
They work the same as the normal buttons but has an additional property called “icon”.

Icon button

{
    "variables": {
       ... variables ...
    },
    
    "actions": [
        {
            "id": "greet",
            ... action details ...
        }
    ],
    
    "body": {
        "elements": [
            {
                "id": "btnGreet",
                "element": "icon-button",
                "icon": "greet-icon",
                "label": "@translations.buttons.greet",
                "action": "greet"
            }
        ]
    }
}

Dropdown button

A dropdown button is essentially a collection of buttons, with one designated as the default button. This default button is displayed on the left, accompanied by a dropdown button to its right. When you click the dropdown button, it reveals the buttons in the dropdown. You can then click any button from this collection to perform the respective action.

{
    "body": {
        "elements": [
            {
                "element": "dropdown-button",
                "elements": [
                    {
                        "element": "button",
                        "title": "@translations.buttons.save"
                        "attributes": {
                            "default": true
                        }
                        ...
                    }
                    {
                        "element": "button",
                        "title": "@translations.buttons.saveAndNew"
                        ...
                    },
                    {
                        "element": "button",
                        "title": "@translations.buttons.saveAndClose"
                        ...
                    }
                ]
            }
        ]
    }
}

Select

Select refers to a combobox that combines an input field with a dropdown list of selectable options. This is a composite UI element that includes a label, the select element, and binding options. Beyond the usual title and field properties, the select element also features a datasource property. This property specifies the items to be displayed as selectable options within the select dropdown.

{
    "variables": {
       ... variables ...
    },
    
    "datasets": [
        {
            "id": "model",
            "fields": [
                ...,
                {
                    "name": "selected"
                },
                {
                    "name": "items",
                    "collection": true,
                    "datasource": "itemsDataSource"
                }
            ]
        }
    ],
    
    "body": {
        "elements": [
            {
                "id": "selectItems",
                "element": "select",
                "title": "@variables.labels.selected",
                "field": "model.selected",
                "datasource": "model.items"
            }
        ]
    }
}

Data grid

The data grid is a visual tool that employs a datasource to display a list of records. It allows customization of the columns to be displayed and their sequence. Each column has the following properties:

  1. title – The title represents the column’s heading.
  2. field – This specifies the data field used to render a cell in a record.
  3. width – This defines the pixel width of the column.
{
    "datasources": [
        {
            "id": "items_datasource",
            ...
        }
    ],
    "datasets": [
        {
            "id": "model",
            "fields": [
                {
                    "name": "items",
                    "collection": true,
                    "datasource": "items_datasource"                   
                }            
            ]
        }
    ],
    "perspectives": [
        {
            "id": "gridPerspective",
            ... define grouping and sorting ...
        }
    ],
    "body": {
        "elements": [
            {
                "element": "grid",
                "id": "WorkOrderTasksDashboardGrid",
                "datasource": "model.items",
                "perspective": "gridPerspective",
                "selection": "multiple",
                "selectedId": "model.taskSelectedId",
                
                "columns": [
                    {
                        "field": "sequenceNumber",
                        "title": "@translations.sequenceNumber",
                        "width": 200
                    },
                    {
                        "field": "taskType",
                        "title":"@translations.taskType",
                        "width": 200
                    },
                    
                    ... other columns ...
                 ]
            }                    
        ]
    }
}

As can be seen in the above example there are a number of grid properties to take note of.

  1. datasource – Identifies the datasource to be used.
  2. perspective – Specifies the perspective ID, which determines the grid’s grouping and sorting.
  3. selection – Defaults to single selection, but can be set to multiple.
  4. selectedId – Represents the single ID value, or a collection of IDs if multi-select is enabled.

List

The list comprises a fixed assortment of items arranged vertically. Certain essential properties must be configured for it to operate effectively.

  1. data source: This term refers to the source from which the data is derived.
  2. perspective: This aspect involves how data is categorized and organized. Grouping the data enables a hierarchical layout, making it easier to navigate through sub-items in a clear, list-based format. When dealing with grouped data, parent items are marked with a chevron, which, upon clicking, automatically reveals the sub-items.
  3. selection properties: This relates to the chosen method of selection, be it single or multiple choices, and specifies where these selections are recorded. It’s important to define the identifier field to ensure the correct value is captured as the selection ID during the selection process.
  4. list item template: This defines the structure in which list items are presented. Different templates can be used for various types of list items.
{
    "datasources": [
        {
            "id": "listItems", 
            ... list items datasource details ...
        }
    ],
    
    "dataset": [
        {
            "id": "model",
            "fields": [
                {
                    "name": "listItems",
                    "collection": true,
                    "datasource": "listItems"
                }
            ]
        }
    ],

    "perspectives": [
        {
            "id", "list_perspective",
            ... perspective details ...
        }
    ],
    
    "templates": [
        {
            "id": "item_struct",
            "elements": [ ... list item elements ... ]
        },
        {
            "id": "special_item",
            "attributes": {
                "condition": "age > 50"              
            },
            "elements": [
                ... elements for special ui ...
            ]
        }
    ],  
  
    "body": {
        "elements": [
            {
                "element": "list",
                "templates": ["special_item", "item_struct"],
                "datasource": "model.listItems",
                "perspective": "list_perspective",
                "selection": "single",
                "selectionId": "model.selectedId",
                "id-field": "id"
            }
        ]
    }
}

In the provided example, multiple templates for use in the list view are being defined. These templates are standard HTMLTemplate elements, and the attributes specified in these intent definitions are applied to the HTMLTemplate elements as attributes. When rendering the list view, the evaluation condition set on each template is checked. The system then selects the template that meets the evaluation criteria. It’s important to note that the templates are evaluated in the order they are defined, starting with the first template. The last template in the collection is set as the default, used when no other templates meet the specified conditions.

The first passing conditional template wins.
No further evaluation is done if a template is found.

Tree views

A tree view is a hierarchical data structure much in the way that grouping data works. It is collapsible and expandable and from a data management part is lazy loaded by default. In other words it only loads the data that it needs to display when the parent node is expanded. For a tree view to be efficient it must be lazy loaded.

During the definition of the tree view you can define a perspective.
Perspectives can have grouping and sorting applied. When applying a grouping, it will create an disconnected branch for the group item.
This means that the group item will not remove it’s children on collapse and will not fetch new data when it is expanded because it was artificially created, disconnected from the main data source. The leaf nodes are the connected items and expanding them will again fetch data from the remote data source.

Tree views can also have conditional formatters, though they are not always required, they do allow custom styling of tree nodes. When new branch nodes are introduced to the view, each formatter condition is evaluated on the data of the item being rendered.

{
    "perspectives": [
        {
            "id": "my_grouping",
            ... perspective details ...
        }
    ],

    "body": {
        "elements": [
            {
                "id": "myTree",
                "element": "tree",
                "formatters": [
                    {
                        "condition": "item.age > 50",
                        "properties": {
                            "color": "red"
                        }
                    }
                ],
                
                "selectedId": "model.tree.selectedId",
                "datasource": "model.tree",
                "perspective": "my_grouping",
                "selection": "single"                                    
            }
        ]
    }
}

In the given example, the “tree” element is used as a generic example. While this approach works for basic systems, more complex, enterprise-level applications benefit from employing a self-contained, subject-matter-specific tree view. Each tree view would maintain a consistent structure and capability, offering uniform configurability. Internally, these specialized tree views handle aspects such as determining which entities to retrieve data from and applying relevant filters to fetch specific subject matter, among other things. This specialized approach ensures that the tree view is not only functional but also contextually relevant to the specific requirements of an enterprise environment.

Examples of such tree views are:

  1. permission-tree
  2. site-tree
  3. asset-tree
  4. asset-type-tree

Simply substitute the current value ( “tree” ) of the element property with the appropriate tree to view the corresponding data.