LSPS documentation logo
LSPS Documentation
Tabular Components

Tabular components include the Table and Tree Table components which share common features: they serve to display data in a tabular manner and have the Column components as their immediate children. Both work with the same types of data sets and support ordering and filtering of their content.

Table (ui::Table)

The Table component displays the data of its data set in the child components of the table's columns: The system iterates through individual objects of the data set and each object becomes the data iterator value for a row. The iterator is then used by the child components of table columns to access the data object.

It supports ordering, filtering, and grouping of the data.

The way the data set is acquired and the way the table is rendered is defined by the table type:

  • simple: The table is on one page and the data is obtained in a single request.
  • lazy: The table is rendered with a particular size and with a scroll. The data is loaded as the user scrolls through the table.
  • paged: The table is rendered with a page navigation at the bottom. The data is acquired per page.

The data set itself can be defined as the following:

  • Type: shared record or a shared record field

    The system fetches all shared record instances from the database.

  • Query: query that returns a collection of objects

    The table iterates through the collection objects.

  • Collection: collection of the data object

    The table iterates through the collection objects.

  • Data: parametric closure that returns the collection of data objects

    The first parameter holds the index of the first entry; The second parameter is the count of entries per load or page. You will use this option typically when integrating with other systems, for example, {currentIndex, count -> getEntryBatch(currentIndex, count)}.

    This data kind requires the Data count property, expression that returns the total amount of entries to be loaded.

  • Generic: an Object that results in any of the above on runtime

    The setting is used for generic reusable tables when the user wants to fill the same table with different data queried in different ways, typically when reusing the form in other forms.

Creating a Simple Table

A simple Table is rendered as a table on a single page with all its items.

To create a simple Table, do the following:

  1. Insert the Table component in a Form.
  2. On the Details tab of the Properties view, define the following:
    • Data Kind: data source type and the related data
    • Data Iterator: reference to a local form variable of the same type as your Data objects.

      Important: The data iterator can be used solely as the table iterator: Use the iterator outside of the Table component only when creating the table dynamically. The practice is generally discouraged and results in validation issues.

    • Type: TableType.simple
  3. Insert Table Columns into the Table.
  4. Into each column, insert the required components and bind them to the iterator data.

Creating a Paged Table

A paged table data is rendered on pages with the number of rows defined by the initial-page-size presentation hint and page navigation at the bottom. It can be set to load a particular page using the Show Index property.

A paged table produces the TablePageSizeChangeEvent when the user changes the size of the table page.

To create a paged Table, do the following:

  1. Select the Table component in a Form.
  2. On the Details tab of the Properties view, define the following:
    • Data Kind: data source type
    • Type: TableType.paged
    • Data Iterator: reference to a local form variable of a type that can hold your Data objects

      Important: The data iterator can be used solely as the table iterator: using the iterator out of the Table results in a validation Error.

    • Show index: the index number of the page that should be opened on load.
    • Index Iterator: reference to an object that will hold the index of the row where the current Data object is used
  3. Set the size of the page in the initial-page-size presentation hint.
  4. Insert Table Columns into the Table.
  5. Into each column, insert the required components and bind them to the iterator data.

Creating a Lazy-Loaded Table

A lazy table is rendered as scrollable table with the number of rows defined by the initial-page-size presentation hint.

To create a Table, do the following:

  1. Select the Table component in a Form.
  2. On the Details tab of the Properties view, define the following:
    • Data Kind: data source type
    • Type: TableType.lazy
    • Data Iterator: reference to a local form variable of a type that can hold your Data objects

      Important: The data iterator can be used solely as the table iterator: using the iterator out of the Table results in a validation Error.

    • Show index: the index number of the page that should be opened on load.
    • Index Iterator: reference to an object that will hold the index of the row where the current Data object is used
  3. Insert Table Columns into the Table.
  4. Into each column, insert the required components and bind them to the iterator data.

Defining Grouping on a Table

Groups serve to group the items in a table on multiple levels according to their properties in a tree-like way, for example, a list of songs according to their interpret and then according to the album.

Note that grouping takes place on the data set and renders the table type setting irrelevant.

The groups are defined as a subtype of the abstract GroupSpec record and that as one of the following:

  • PropertyGroupSpec: the groups are defined as Record properties;
    [
    //first-level grouping:
    new PropertyGroupSpec (
        label -> "Surname",
        groupBy -> Author.surname),
        //second-level grouping:
        new PropertyGroupSpec
              (label -> "First name",
              groupBy -> Author.firstName)
    ]
    
  • ClosureGroupSpec: the groups are calculated on runtime in a closure;
    [new ClosureGroupSpec(
       label -> "Surname",
       groupBy -> {a:Author -> a.surname}
       )
    ]
    
  • OptClosureGroupSpec: the groups are calculated on runtime in a closure in optimized manner;
    [new OptClosureGroupSpec(label -> "Surname",
          groupBy ->
            { c:Collection<Author > ->
            def Set<String> surnames := toSet(collect(c, {a:Author -> a.surname}));
            //creates map with surname as the key and collection of authors as value
            map(surnames, {surname:String -> select(c, {a:Author -> a.surname == surname})})}
        )
    ]
    

Alternatively, if you are using the Type data kind, you can infer grouping: The system will "guess" the applicable groups. The Group Spec then becomes a collection of all bindings of child components of all columns. Therefore, grouping is inferred only on columns with one Input component with binding set to a simple data type entity.

To define grouping on a table, do the following:

  1. In your form definition, select the table component.
  2. In the Properties view, open the Grouping tab.
  3. Uncheck Disable Grouping.
  4. Define the grouping:
    • For your own grouping:
      1. Define the GroupSpec and optionally Grouping that holds the currently applied grouping.
      2. Optionally, on individual table columns, define the Group value on their Grouping tab. The value is used as a group header in the line that serves to expand and collapse the group.
        {values:Collection<Author>->values[0].gender}
        tableGroupingDef.png
        Form with Table and its Grouping Definition
    • To infer grouping, select Infer Grouping.
activatingTableGrouping.png
Setting Grouping

Disabling Grouping on Tables

To disable grouping on a table, do the following:

  1. In the Properties view of the Table component, go to the Filtering, Ordering, or Grouping tab.
  2. Unselect the applicable grouping option:
    1. Unselect Disable Grouping to disable grouping altogether.

      For the Columns with inferring of grouping, the inferring will be disabled. Other Grouping settings will be applied.

    2. Unselect Infer Grouping to disable inferred grouping.
disablingGrouping.png

Tree Table (ui::TreeTable)

The Tree Table component renders as a table with rows organized in a tree structure. The rows of the collapsible tree nodes are loaded on expand.

Individual nodes are represented by TreeItem objects, which hold the business data and information about the tree node and its child elements: The tree table component iterates through a collection of the root <TreeItem> objects. If a root object defines children, it iterates also through the child elements. Every element becomes the object for a row.

Tree Tables support ordering and filtering of their content.

The TreeItem objects define the following:

  • data: business object the tree table item operates over (business data of a row)
  • label: label you can use as component content
  • expanded: whether the node is expanded by default
  • parent: parent TreeItem object (element "above")
  • children: child TreeItem object (elements "inside")

You can access the data through the iterator or tree item iterator: the iterator holds only the business data object, while the tree item iterator holds its TreeItem object.

TreeTable.png
Tree Table with expanded nodes
The Tree Table component has the following properties:

  • Root: collection of root TreeItem elements
  • Children: closure that returns a list of tree table items that are loaded when a node is expanded

    The closure is called when the children in the Root expression are null; note that when children is an empty collection, the node is considered a leaf and the closure is not called.

  • Iterator: reference to an object of the business data type

    The iterator will hold the data object of the given row so it has to be able to hold any business data type used in the Tree Table: if the table uses different records, consider using the Record type or another parent data type.

    Important: The data iterator can be used solely as the table iterator: using the iterator out of the Table results in a validation Error.

  • Tree Item Iterator: reference to an object of the TreeItem type

    It will hold the TreeItem object of the current row.

    Important: The iterator can be used solely as the table iterator: using the iterator out of the Table results in a validation Error.

Creating a Tree Table

To create a Tree Table, do the following:

  1. Insert the Tree Table component in a Form.
  2. Define the Root property with the collection of the root nodes and possibly their children.
    [new TreeItem(
    //business object
        data -> boss,
        label -> "Boss",
        expanded -> true,
        parent -> null,
        children -> [
            new TreeItem(
                data -> employee,
                label -> "Employee",
                expanded -> true,
                parent -> null,
                //if children is null, like below, then the Children property is used to retrieve child nodes:
                children -> null)
        ]
      )
    ]
    
  3. Define the Children property (the closure that returns the child objects for root objects with null value in children).
    { treeitem -> collect(getAllChildren(treeitem), convertToTreeitems())}
  4. Define the Iterator and the TreeItem iterator: typically these are references to local form variables.
  5. Insert Table columns into the Tree Table.
  6. Insert the required components into the Columns, typically, OutputTexts, Text Boxes, etc.
  7. Define the content of the Column components using the iterators where applicable.
    treeTableForm.png
    Tree Table form with its Detail properties

Table Columns (ui::TableColumn)

The Table Column component can represent column of a Table or a Tree Table. It defines table column properties, including ordering and filtering options. Its children are components that can operate over the Table business data.

To define how children of a column are aligned use the align presentation hint: note that the hint is applied on the header as well.

Warning: Note that refreshing a table column does not refresh its underlying data set: only its header, hints, and any data not related to the data set, such as global variable references, are recalculated. This is justified by the fact that if you managed to refresh only the data set for a column, the table could be populated with inconsistent data. To refresh the underlying data set of a column, either refresh the parent table or the content of the column child components.

The Table Column component has the following properties:

  • Header: column header
  • Visible: visibility of the table column

    If false, the column is not visible or available in the column picker.

    The expression is evaluated whenever the column's parent component is refreshed.

Collapsing a Table Column

To hide a Table Column, that is to display it collapsed, set the hidden presentation hint to true: this setting will be applied when the form is initialized.

Saving Column Width and Collapsed State

To save the column width in a table and the collapsed state of a table when a document or a to-do is saved, call the getColumnStates(<MYTABLE>) function and store the returned column states. When applicable, call the restoreColumnStates(<MYTABLE>, <COLUMNSTATE>) function to restore the column widths and collapsed states.

Ordering and Filtering of Tables and Tree Tables

Tables and Tree Tables support

  • ordering: sorting of data in an ascending or descending order when the user clicks a column label
  • filtering: filtering of data so that only entries that meet the filter requirements are displayed

The features are enabled on the Table and Tree Table components; however, since their logic applies to Columns, that is where they are actually defined.

All the features allow their inferring, that is, setting up the feature automatically as best effort for the given Column data.

Defining Ordering

Ordering is defined on the Column components in Tables or Tree Tables. By default, Columns are set to infer ordering so the feature is automatically set up. However, inferring is applicable only on Columns with exactly one input component and with their binding set to a simple data type. For columns with multiple or other than input components, the inferring of ordering is not supported. Also, if the data set is obtained as Data, you will need to define the ordering manually.

Note: Ordering cannot be applied on columns with enumeration values. If you want to order a table according to an enumeration, use a query to acquire the data set already sorted. You can then define the enumeration as the sorting property on table columns.

To define ordering on a table, do the following:

  1. Define the ordering properties on the Ordering tab of the Table or Tree Table:

    • Ordering: reference to a variable that holds the ordering applied to the table at the given moment
    • Infer ordering: if checked, inferring of ordering on the table is enabled (Columns can infer filtering).

      Ordering can be inferred only on tables that define a shared Record type as its data kind and that only on columns that contain exactly one input component (the infer guess is performed based on the binding reference of the component).

    Once you have defined ordering properties on the table, you will need to define ordering on table columns.

  2. Define the ordering properties on individual columns: On the Ordering tab in the column Properties view, select the Ordering kind and define the respective Order By expression:
    • Property: the expression must return a record field of a simple data type. The returned type must be present in the row scope type. The table data will be sorted according to the values of this field in row scopes when the user clicks the column header.
      //Author is a shared record that is part of the row scope and surname is its field.
      Author.surname
    • Expression: the closure expression must return a field of a simple data type. The input closure parameter is the row scope. The returned type must be present in the row scope type. The table data will be sorted according to the values of this field in row scopes when the user clicks the column header.
      //a incoming parameter is the row scope, in this case, the Author instance; the closure returns the instances surname
      {a:Author -> a.surname}
    • Enumeration: the name of the ordering enumeration. This setting applies only to data acquired using non-native query and defines entered ordering enumeration.
    • Infer: no Group By expression applies. Inferring is applied only on the particular column.

      If setting the value to Infer, make sure inferring of ordering is enabled on the parent table or tree table.

    • Disabled: ordering is disabled on the particular column.

Tracking Current Ordering on Tables and Tree Tables

To track and programatically change the ordering of a Table or Tree Table, define the Ordering expression on the Ordering tab of the component properties. The value is a reference to a map and will be set to values in the form [ORDER_BY -> ORDER_DIRECTION], for example [MyForm::MyRecord.id -> ui::OrderDirection.Ascending].

Other model resources can use the variable value to detect the current ordering. Also, if you set the reference to an expression, the expression will be evaluated when the component is visualized in the front-end application and used as default ordering on the Table or Tree Table.

Defining Filtering

Filtering on Tables and Tree Tables is by default enabled and Columns are set to infer the filtering settings: by default your Tables and Tree Tables have filtering set up: However, inferring is applicable only on Columns with exactly one input component with binding to a simple data type. For columns with multiple or other than input components, you need to define filtering manually.

To define filtering on a table, do the following:

  1. Define the filtering properties on the Filtering tab of the Table or Tree Table:
    • Filtering: currently applied filtering expression
    • Infer Filtering: if checked, inferring of filtering on the table is enabled (Columns can infer filtering).

      Note: Filtering can be inferred only on table columns that contain exactly one input component. The guess is performed based on the binding reference.

  2. Define the filtering properties on individual columns: On the Filtering tab in the column Properties view, do the following:
    1. In the Filter UI field, define the filter type (for example, new ui::SubstringFilterUI(substring -> "default substring"), new ui::RegExpFilterUI(regexp -> "default regex")).
    2. Select the Filtering type and define the respective Filter By expression:
      • Property: the expression must return a record field of a simple data type. The returned type must be present in the row scope type. The table data will be filtered according to the values of this field in row scopes.
        //Author is a shared record that is part of the row scope and surname is its field.
        Author.surname
      • Expression: the closure expression must return a field of a simple data type. The input closure parameter is the row scope. The returned type must be present in the row scope type. The table data will be sorted according to the values of this fields in row scopes when the user clicks the column header.
        //a incoming parameter is the row scope
        // in this case, the Author instance; the closure returns the instances surname
        {a:Author -> a.surname}
      • Custom: applicable only on Data (paged collection) custom filter definition (Filter must be of PropertyFilter, ClosureFilter, or CustomFilter)
        new CustomFilter(ui -> new TextBox( binding -> &filterBinding),
        filterText -> {->"Surname Filter"},
        popup -> false)
      • Infer: no Filter By expression applies. Filtering is applied only on the particular column.

        If setting the value to Infer, make sure Inferring of Filtering is enabled on the parent component.

      • Disabled: filtering is disabled on the particular column.
  3. On the Table, consider defining the no-data-message presentation hint: the hint value will be displayed if the table is empty.

Disabling Filtering and Ordering on Tables and Tree Tables

To disable filtering, or ordering on a table, do the following:

  1. In the Properties view of the component, go to the Filtering, Ordering, or Grouping tab.
  2. Select the Disable option:
    1. To disable any filtering and ordering, select Disable Filtering or Disable Ordering.
    2. To disable only the inferred filtering or ordering, unselect Infer Filtering or Infer Ordering. For the Column with inferring of filtering, ordering, or grouping, the inferring will be disabled. Other Filtering, Ordering, or Grouping settings will be applied.