Table

The Table component presents information in a table view, sorts data, manages table columns and headers, and invokes actions for selected rows.

Component’s XML-name: table.

Basics

A typical table is shown below:

table anatomy
  1. Header row

  2. Rows

  3. Column control button

  4. Pagination

  5. Sort button

  6. Buttons panel

An example of table declaration in the XML screen descriptor is shown below:

<data>
    <collection id="customersDc" class="ui.ex1.entity.Customer">
        <fetchPlan extends="_base"/>
        <loader id="customersDl">
            <query>
                <![CDATA[select e from uiex1_Customer e]]>
            </query>
        </loader>
    </collection>
</data>
<layout>
    <table id="customersTable"
           width="100%"
           dataContainer="customersDc">
        <actions>
            <action id="create" type="create"/>
            <action id="edit" type="edit"/>
            <action id="remove" type="remove"/>
            <action id="sendByEmail" type="sendByEmail">
                <properties>
                    <property name="recipientAddress" value="peter@example.com"/>
                </properties>
            </action>
        </actions>
        <columns>
            <column id="level"/>
            <column id="age"/>
            <column id="hobby"/>
            <column id="firstName"/>
            <column id="lastName"/>
            <column id="rewardPoints"/>
        </columns>
        <simplePagination/>
        <buttonsPanel id="buttonsPanel" alwaysVisible="true">
            <button id="createBtn" action="customersTable.create"/>
            <button id="editBtn" action="customersTable.edit"/>
            <button id="removeBtn" action="customersTable.remove"/>
            <button id="sendBtn" action="customersTable.sendByEmail"/>
        </buttonsPanel>
    </table>
</layout>

In the example, the data element defines the collection container, which selects Customer entities using a JPQL query. The table element defines the data container, while the columns element defines which entity attributes are used as table columns.

Data Binding

Using Collection Container

To create a table connected to data, use the dataContainer attribute, which should contain a reference to a collection container.

Using KeyValue Containers

KeyValue containers enable the execution of queries that return scalar values and aggregates. KeyValue containers are designed only for reading data because KeyValueEntity is not persistent and cannot be saved by standard persistence mechanisms.

Table can be bound to KeyValueCollectionContainer, for example:

<data>
    <keyValueCollection id="salesDc">
        <loader id="salesLoader">
            <query>
                <![CDATA[select o.customer, o.customer.firstName,
                sum(o.amount) from uiex1_Order o group by o.customer]]>
            </query>
        </loader>
        <properties>
            <property class="ui.ex1.entity.Customer" name="customerEntity"/>
            <property datatype="string" name="customerName"/>
            <property datatype="decimal" name="sum"/>
        </properties>
    </keyValueCollection>
</data>
<layout>
    <table id="keyValueTable" width="100%" dataContainer="salesDc">
        <columns>
            <column id="customerName" caption="Customer"/>
            <column id="sum" caption="Summary amount" align="RIGHT"/>
        </columns>
    </table>
</layout>

If the table is bound to KeyValueCollectionContainer, it should be read-only, and the editable attribute becomes nonsense.

Using Meta Class

If you need to define a data container programmatically in the screen controller, use the metaClass attribute in XML instead of declarative setting a dataContainer.

Columns

Columns are used to display and edit data.

The set of columns for a table is defined in the columns element. If it is not specified, columns will be automatically determined from the fetch plan attributes defined in dataContainer.

The columns element has the following attributes:

  • includeAll loads all the attributes from fetchPlan that is defined in dataContainer.

    In the example below, we will show all the attributes from the fetch plan used in customersDc. If the fetch plan contains system properties, they will be shown too.

    <table id="customersTableIncludeAll"
           width="100%"
           dataContainer="customersDc">
        <columns includeAll="true"/>
    </table>

    If the entity fetch plan contains a reference attribute, this attribute will be displayed according to its instance name. If you want to show a specific attribute, it must be defined in the fetch plan as well as in the column element:

    <data>
        <collection id="customersDc1" class="ui.ex1.entity.Customer">
            <fetchPlan extends="_base">
                <property name="city" fetchPlan="_base">
                    <property name="country" fetchPlan="_base"/>
                </property>
            </fetchPlan>
            <loader id="customersDl1">
                <query>
                    <![CDATA[select e from uiex1_Customer e]]>
                </query>
            </loader>
        </collection>
    </data>
    <layout>
        <table id="tableIncludeAllReference"
               width="100%"
               dataContainer="customersDc1">
            <columns includeAll="true">
                <column id="city.country.name"/>
            </columns>
        </table>
    </layout>
  • exclude contains a comma-separated list of attributes that should not be loaded to the table.

    In the example below, we will show all the attributes excluding id, maritalStatus, and email:

    <table id="tableExclude"
           width="100%"
           dataContainer="customersDc1">
        <columns includeAll="true"
                 exclude="id,maritalStatus,email"/>
    </table>

Column

Each column is described in a nested column element.

id is a mandatory attribute, contains the name of an entity attribute displayed in the column. It can be either an attribute of the entity from the data container or a linked entity - object graph traversal is indicated with a dot. For example:

<columns>
    <column id="firstName"/>
    <column id="lastName"/>
    <column id="city.name"/>
    <column id="city.country.name"/>
</columns>

Column Alignment

Three different column alignments are supported: LEFT (default), CENTER, and RIGHT.

Right-align numbers when it is useful to compare them by their length. Left-align all the other content. For example:

<table id="keyValueTable" width="100%" dataContainer="salesDc">
    <columns>
        <column id="customerName" caption="Customer"/>
        <column id="sum" caption="Summary amount" align="RIGHT"/>
    </columns>
</table>
table column align

Column Caption

caption - an optional attribute containing the column caption. If not specified, a localized instance name will be displayed.

captionAsHtml - an optional attribute defining whether HTML tags can be used in the column caption. The default value is false.

captionProperty - the name of an entity attribute which should be shown in the column instead of specified by id. For example, if you have a reference to the City entity with the name and id attributes, you can define the following column (3):

<columns>
    <column id="firstName"
            captionAsHtml="true"
            caption="msg://firstName"/> (1)
    <column id="lastName"/> (2)
    <column id="city"
            caption="City ID"
            captionProperty="id"/> (3)
</columns>
1 Column’s caption has HTML tags:
ui.ex1.screen.component.table/firstName=<em>First Name</em>
2 A column with the default caption
3 The column will display the city id, but the sorting of the column will be done by the city name.
table captions

Column Visibility

collapsed - an optional attribute; hides the column by default when set to true. Users can control the column’s visibility using the menu available via the table column control button button in the top right part of the table when the table’s columnControlVisible attribute is not false. By default, collapsed is false.

The visible attribute sets the visibility of the column. Possible values - true, false. By default, all columns are visible.

Let’s consider the difference between the collapsed and visible attributes in the following example.

<columns>
    <column id="firstName"
            collapsed="true"/>
    <column id="lastName"
            visible="false"/>
    <column id="city"/>
</columns>
table columns visibility

As you can see, the firstName column is collapsed by default, but users can expand it from the drop-down menu of the table column control button button.

At the same time, the lastName column cannot be made visible using user actions.

Column Width

width - an optional attribute, controlling default column width. It may contain only numeric values in pixels.

<columns>
    <column id="firstName" width="100"/>
    <column id="lastName" width="150"/>
    <column id="city" width="100"/>
</columns>
table column width

expandRatio - an optional attribute that specifies the expand ratio for each column. The ratio must be greater than or equal to 0. If some value is set for at least one column, all implicit values are ignored, and only explicitly assigned values are considered. If you set width and expandRatio attributes simultaneously, it will cause an error in the application.

<columns>
    <column id="firstName" expandRatio="1"/>
    <column id="lastName" expandRatio="1.5"/>
    <column id="city" expandRatio="1"/>
</columns>
table column expand ratio

Users can resize table columns:

table columns resize

The column width remains fixed only when the enable attribute is set to false for the entire table.

Column Reordering

Table provides the drag-and-drop functionality allowing users to change the order in which columns are displayed within the table.

table columns reordering

The column reordering functionality is enabled by default. To disable it, set the reorderingAllowed attribute to false.

The contents of columns can be displayed as links.

<column id="firstName"
        link="true"
        linkScreenOpenMode="DIALOG"/>
<column id="lastName"/>
<column id="city"/>
table columns link

Maximum Text Length

maxTextLength - an optional attribute, allowing to limit the number of characters in a cell.

If the difference between the actual and the maximum allowed number of characters does not exceed the 10 character threshold, the "extra" characters remain unhidden. To see the entire record, users need to click on its visible part.

In the example below, some cells contain longer values than maxTextLength+10 and become trimmed.

<columns>
    <column id="firstName"/>
    <column id="lastName"/>
    <column id="email" maxTextLength="15"/>
    <column id="city"/>
</columns>
table max text length

Text Formatting

The column element may contain a nested formatter element that allows you to represent the attribute value in a format different from the standard for this Datatype:

<table id="tableColumnFormatter"
       width="100%"
       dataContainer="ordersDc">
    <columns>
        <column id="customer"/>
        <column id="amount"/>
        <column id="deliveryTime">
            <formatter>
                <date format="h:mm a"/>
            </formatter>
        </column>
    </columns>
</table>

Multiple Lines

Setting the multiLineCells attribute to true enables a multi-line display for cells containing several lines of text. In this mode, the web browser will load all the rows of the current table page at once instead of lazy-loading the visible part of the table. It is required for proper scrolling in the Web Client. The default value is false.

table multi line cell

Selection

The Table component enables users to select single or multiple rows.

Single

In a single selection mode, the user can select exactly one row. Single selection mode is enabled by default.

Multiple

The multiselect attribute enables setting multiple selection mode for table rows. If multiselect is true, users can select multiple rows in the table using a keyboard or mouse holding Ctrl or Shift keys. By default, multiple selection mode is switched off.

Show Selection

If the showSelection attribute is set to false, a current row is not highlighted.

Text Selection

The textSelectionEnabled attribute defines if text selection is enabled in table cells. The default value is false.

Placeholder

A table placeholder is shown when there is no data yet.

The emptyStateMessage attribute enables to set the message when no data is loaded, null items are set, or an empty container is used. This attribute is often used together with the emptyStateLinkMessage attribute. The message should contain information about why the table is empty.

emptyStateMessage supports loading messages from the message bundle. If you don’t want to show a message, do not specify this attribute.

<table id="tablePlaceholder"
       width="100%"
       height="200"
       metaClass="uiex1_Customer"
       emptyStateMessage="No customers added."
       emptyStateLinkMessage="Add customer">
    <columns>
        <column id="firstName"/>
        <column id="lastName"/>
    </columns>
</table>
table placeholder

emptyStateLinkMessage supports loading messages from the message bundle. If you don’t want to show a message, do not specify this attribute.

In order to handle click on the link message, you can subscribe to EmptyStateClickEvent:

@Install(to = "tablePlaceholder", subject = "emptyStateLinkClickHandler")
private void tablePlaceholderEmptyStateLinkClickHandler(
        Table.EmptyStateClickEvent<Customer> emptyStateClickEvent) {
    screenBuilders.editor(emptyStateClickEvent.getSource())
            .newEntity()
            .show();
}

To register the event handler programmatically, use the setEmptyStateLinkClickHandler() component method.

Header Visibility

A table header is a row at the top of the table that helps identify the columns below each of the headers.

The columnHeaderVisible attribute sets whether the table header is displayed. By default, a table header is visible.

Column Control Visibility

The user will be able to select which columns are visible using the column control button table column control button in the right part of the table header.

Currently displayed columns are marked with checkmarks in the menu. There are additional menu items:

  • Select all - shows all table columns;

  • Deselect all - hides all columns that are possible except the first one. The first column is not hidden to display the table correctly.

table column control using

If the columnControlVisible attribute is set to false, users cannot hide columns.

Aggregating

Table supports aggregation for table rows.

The following operations are supported:

  • SUM - calculate the sum;

  • AVG - calculate the average value;

  • COUNT - calculate the total number;

  • MIN - find the minimum value;

  • MAX - find the maximum value.

To enable aggregation for a table, you should:

  1. Set the aggregatable attribute to true.

  2. Set the aggregation element for aggregated table columns.

  3. Set the type attribute for the aggregation element, which defines the aggregation function.

The aggregated table values are shown in an additional row at the top of the table.

The aggregationStyle attribute allows you to specify the location of the aggregation row: TOP or BOTTOM. TOP is used by default.

By default, only numeric data types are supported in aggregated columns, such as Integer, Double, Long, and BigDecimal.

An example of an aggregated table description:

<table id="tableAggregation"
       width="100%"
       aggregatable="true"
       dataContainer="customersDc1">
    <columns>
        <column id="firstName"/>
        <column id="lastName"/>
        <column id="rewardPoints"/>
        <column id="age">
            <aggregation type="AVG"/>
        </column>
    </columns>
</table>
table aggregation

The valueDescription attribute defines a hint displayed in a popup when a user hovers the mouse cursor on the aggregated value. For the operations listed above (SUM, AVG, COUNT, MIN, MAX), popup hints are already available by default.

The aggregation element can contain the editable attribute. Setting the attribute to true in conjunction with using the AggregationDistributionProvider allows developers to implement algorithms to distribute data between the rows of the table.

You can specify Formatter to display the aggregated value in the format other than the standard for this Datatype:

<column id="amount">
    <aggregation type="SUM">
        <formatter>
            <number format="#,##0.00"/>
        </formatter>
    </aggregation>
</column>

The aggregation element can also contain the strategyClass attribute specifying a class implementing the AggregationStrategy interface.

public class TableCustomerHobbyAggregation implements AggregationStrategy<Hobby, String> {
    @Override
    public String aggregate(Collection<Hobby> propertyValues) {
        Hobby mostFrequent = null;
        long max = 0;
        if (CollectionUtils.isNotEmpty(propertyValues)) {
            for (Hobby hobby : Hobby.values()) {
                long current = propertyValues.stream()
                        .filter(customerHobby -> customerHobby.equals(hobby))
                        .count();

                if (current > max) {
                    mostFrequent = hobby;
                    max = current;
                }
            }
        }

        if (mostFrequent != null) {
            return String.format("%s: %d/%d", mostFrequent.name(), max, propertyValues.size());
        }

        return null;
    }

    @Override
    public Class<String> getResultClass() {
        return String.class;
    }
}
<table id="tableAggregationStrategy"
       width="100%"
       aggregatable="true"
       dataContainer="customersDc">
    <columns>
        <column id="firstName"/>
        <column id="lastName"/>
        <column id="hobby">
            <aggregation
                    strategyClass="ui.ex1.screen.component.table.TableCustomerHobbyAggregation"/>
        </column>
    </columns>
</table>

Sorting

Table allows users to sort column data. Sorting functionality is enabled by default.

The sortable attribute enables sorting data in the table. By default, it is set to true. If sorting is allowed, clicking a column header will show a table sortable down/table sortable up icon to the right of the column name.

You can disable sorting for a particular column by using its sortable attribute.

In the example below, we will disable sorting by the lastName column:

<columns>
    <column id="firstName"/>
    <column id="lastName" sortable="false"/>
    <column id="city"/>
</columns>

According to the sort direction, there is a sort attribute allowing to set the initial sorting of the table by the specified column. Possible values:

  • ASCENDING - ascending (for example, A-Z, 1..9) sort order.

  • DESCENDING - descending (for example, Z-A, 9..1) sort order.

<columns>
    <column id="firstName" sort="DESCENDING"/>
    <column id="lastName"/>
    <column id="city"/>
</columns>

Only one column can be sorted at the same time. So, the example below:

<columns>
    <column id="firstName" sort="DESCENDING"/>
    <column id="lastName" sort="ASCENDING"/>
    <column id="city"/>
</columns>

raises an exception.

Also, if you set sort and sortable="false" attributes simultaneously for the column, it will cause an error in the application.

Paging

Table enables you to split its content into pages. For these purposes, the nested SimplePagination element is used:

<table id="tablePaging"
       width="100%"
       dataContainer="customersDc">
    <columns>
        <column id="firstName"/>
        <column id="lastName"/>
        <column id="hobby"/>
        <column id="age"/>
    </columns>
    <simplePagination
            itemsPerPageVisible="true"
            itemsPerPageOptions="2, 4, 6"/>
</table>
table pagination

Table Actions

The Table component implements the ActionsHolder interface and can contain custom actions, as well as standard actions.

actions - an optional element describing the actions related to the table.

If an action has a caption, it is displayed as an item of the table context menu. Additionally, a table action can be assigned to a button located anywhere on the screen.

With the help of the buttonsPanel optional XML element, you can add a ButtonsPanel container to show action buttons above the table.

<table id="tableWithActions"
       width="100%"
       dataContainer="customersDc">
    <actions>
        <action id="create" type="create"/> (1)
        <action id="edit" type="edit"/>
        <action id="remove" type="remove"/>
        <action id="about" caption="Get info"/> (2)
    </actions>
    <columns>
        <column id="age"/>
        <column id="firstName"/>
        <column id="lastName"/>
    </columns>
    <buttonsPanel id="buttonsActionsPanel" alwaysVisible="true"> (3)
        <button id="create" action="tableWithActions.create"/>
        <button id="edit" action="tableWithActions.edit"/>
        <button id="remove" action="tableWithActions.remove"/>
        <button id="about" action="tableWithActions.about"/>
    </buttonsPanel>
</table>
1 Define the create standard action.
2 Define the about custom action.
3 Define a ButtonsPanel container inside a table.
table actions

Column Generation

Column generator handler allows you to define a custom representation of data in a column. It takes two parameters: identifier of the column and implementation of the Table.ColumnGenerator interface. An identifier can match one of the identifiers set for table columns in XML-descriptor – in this case, the new column is inserted instead of the one defined in XML. If the identifier does not match any columns, a new column is added to the right.

The generateCell() method of the Table.ColumnGenerator interface is invoked for each row of the table. The method receives an instance of the entity displayed in the corresponding row. generateCell() should return a visual component that will be displayed in the cell.

Jmix Studio can generate a handler method stub, and then you should implement it:

@Install(to = "tableGeneratedColumn.isEmail", subject = "columnGenerator")
private Component tableGeneratedColumnIsEmailColumnGenerator(Customer customer) {
    CheckBox isEmail = uiComponents.create(CheckBox.class);
    isEmail.setValue(customer.getEmail() != null);
    return isEmail;
}

If you want to display just dynamic text, use a special class Table.PlainTextCell instead of the Label component. It will simplify rendering and make the table faster.

@Install(to = "tableGeneratedColumn.fullName", subject = "columnGenerator")
private Component tableGeneratedColumnFullNameColumnGenerator(Customer customer) {
    return new Table.PlainTextCell(customer.getFirstName() + " " + customer.getLastName());
}
<table id="tableGeneratedColumn"
       width="100%"
       dataContainer="customersDc">
    <columns>
        <column id="age"/>
        <column id="firstName"/>
        <column id="lastName"/>
        <column id="fullName" caption="Full name"/>
        <column id="email"/>
        <column id="isEmail" caption="Is email"/>
    </columns>
</table>

fullName and isEmail are generated columns.

table generated columns

To register the column generator handler programmatically, use the addGeneratedColumn() component method.

Editing Values

Table can be configured to allow inline editing.

Use the editable attribute to switch the table to in-place editing mode.

In this mode, the columns with editable = true attribute show components to edit the attributes of the corresponding entity. Changing this attribute at runtime is not supported.

The component type for each editable column is selected automatically based on the type of the corresponding entity attribute. For example, for string and numeric attributes, the application will use TextField, for Date - DateField, for enumerations - ComboBox, for links to other entities - EntityPicker.

For a Date type editable column, you can additionally define the dateFormat or resolution attributes similar to the ones described for the DateField.

You can define the optionsContainer and captionProperty attributes for an editable column showing a linked entity. If optionsContainer is set, the application will use ComboBox instead of EntityPicker.

Context Menu

The context menu is activated by right-clicking within the table.

The contextMenuEnabled attribute enables the context menu. By default, this attribute is set to true. The context menu shows table actions (if any).

table context menu

Styling

You can set predefined styles to the Table component using the stylename attribute either in the XML descriptor or in the screen controller:

<table id="tableBorderless"
       caption="Table with borderless style"
       width="100%"
       stylename="borderless"
       dataContainer="customersDc1">
tableBorderless.setStyleName(ThemeClassNames.TABLE_BORDERLESS);

Predefined styles:

  • borderless - removes the outer border of the table;

  • no-header - hides the table column headers;

  • no-horizontal-lines - removes the horizontal divider lines between the table rows;

  • no-vertical-lines - remove the vertical divider lines between the table columns.

  • no-stripes - removes the alternating row colors.

The appearance of the Table component can be customized using SCSS variables with the $jmix-table-* prefix. You can change these variables in the visual editor after creating a custom theme.

Methods of Table Interface

  • getSelected(), getSingleSelected() return instances of the entities corresponding to the selected rows of the table. A collection can be obtained by invoking getSelected(). If nothing is selected, the application returns an empty set. If multiselect is disabled, it is more convenient to use getSingleSelected() method returning one selected entity or null if nothing is selected.

  • The requestFocus() method allows you to set focus on the concrete editable field of a certain row. It takes two parameters: an entity instance that identifies the row and the column identifier. Example of requesting a focus:

    table.requestFocus(item, "count");
  • The scrollTo() method allows you to scroll the table to the concrete row. It takes one parameter: an entity instance identifying the row.

    Example of scrolling:

    table.scrollTo(item);
  • The setItemClickAction() method allows you to define an action performed when a table row is double-clicked. If such action is not defined, the table will attempt to find an appropriate one in the list of its actions in the following order:

    • The action assigned to the Enter key by the shortcut property;

    • The edit action;

    • The view action;

      If such action is found and has enabled = true property, the action is executed.

      Example of setting the ItemClickAction for the Table component defined in the screen XML with the tableClick id:

      @Autowired
      private Table<Customer> tableClick;
      
      @Subscribe
      public void onInit(InitEvent event) {
          tableClick.setItemClickAction(new BaseAction("itemClickAction")
                  .withHandler(actionPerformedEvent -> {
                      Customer customer = tableClick.getSingleSelected();
                      if (customer != null) {
                          notifications.create()
                                  .withCaption("Item clicked for: " + customer.getFirstName()+
                                          "" + customer.getLastName())
                                  .show();
                      }
                  }));
      }
  • The setEnterPressAction() allows you to define an action executed when Enter is pressed. If such action is not defined, the table will attempt to find an appropriate one in the list of its actions in the following order:

    • The action defined by the setItemClickAction() method;

    • The action assigned to the Enter key by the shortcut property;

    • The edit action;

    • The view action;

      If such action is found and has enabled = true property, the action is executed.

      Example of setting the ItemClickAction for the Table component defined in the screen XML with the tableClick id:

      @Autowired
      private Table<Customer> tableClick;
      
      @Subscribe
      public void onInit(InitEvent event) {
          tableClick.setEnterPressAction(new BaseAction("enterPressAction")
                  .withHandler(actionPerformedEvent -> {
                      Customer customer = tableClick.getSingleSelected();
                      if (customer != null) {
                          notifications.create()
                                  .withCaption("Enter pressed for: " + customer.getFirstName()+
                                          "" + customer.getLastName())
                                  .show();
                      }
                  }));
      }
  • The getAggregationResults() method returns a map with aggregation results, where map keys are table column identifiers and values are aggregation values.

  • The addPrintable() method enables setting a custom presentation of the data within a column when exporting to an XLS file via the excel action or directly using the ExcelExporter class. The method accepts the column identifier and implementation of the Table.Printable interface for the column. For example:

    printableTable.addPrintable("firstName", new Table.Printable<Customer, String>() {
        @Override
        public String getValue(Customer item) {
            return "Name: " + item.getFirstName();
        }
    });

    The getValue() method of the Table.Printable interface should return data to be displayed in the table cell. This is not necessarily a string – the method may return values of other types, for example, numeric data or dates, which will be represented in the XLS file accordingly.

    If formatted output to XLS is required for a generated column, an implementation of the Table.PrintableColumnGenerator interface passed to the addGeneratedColumn() method should be used. The value for a cell in an XLS document is defined in the getValue() method of this interface:

    printableTable.addGeneratedColumn("fullName", new Table.PrintableColumnGenerator<Customer, String>() {
        @Override
        public String getValue(Customer item) {
            return item.getFirstName() + " " + item.getLastName();
        }
    
        @Override
        public Component generateCell(Customer entity) {
            Label label = uiComponents.create(Label.NAME);
            label.setValue(entity.getFirstName() + " " + entity.getLastName());
            return label;
        }
    });

    If Printable presentation is not defined for a generated column in one way or another, then the column will either show the value of the corresponding entity attribute or nothing if there is no associated entity attribute.

Events and Handlers

To generate handler method stubs in Jmix Studio, use the Generate Handler button in the top panel of the screen controller code. Or you can select the component in the screen descriptor layout and use the Handlers tab of the Component Inspector panel.

AggregationDistributionProvider

Defines the rules for distributing the aggregated value between rows in a table. According to a custom algorithm, if the user enters a value in an aggregated cell, it is distributed to the constituent cells. It is supported only for the TOP aggregation style. To make aggregated cells editable, use the editable attribute of the aggregation element.

When creating a provider, you should use the AggregationDistributionContext<E> object, which contains the data needed to distribute the aggregated value:

  • Column column where total or group aggregation was changed;

  • Object value − the new aggregation value;

  • Collection<E> scope − a collection of entities that will be affected by changed aggregation;

  • boolean isTotalAggregation shows total aggregation changed, or it was group aggregation.

As an example, consider a table that represents a budget. The user creates budget categories and sets the percentages according to which the amount of income should be distributed. Next, the user sets the total amount of income in the aggregated cell; after that, it distributes by category.

An example of table configuration in an XML-descriptor of a screen:

<table id="budgetTable"
       width="100%"
       aggregatable="true"
       dataContainer="budgetItemsDc">
    <columns>
        <column id="category"/>
        <column id="percent"/>
        <column id="amount">
            <aggregation type="SUM"
                         editable="true"/>
        </column>
    </columns>
</table>

An example in a screen controller:

@Install(to = "budgetTable", subject = "aggregationDistributionProvider")
private void budgetTableAggregationDistributionProvider(
        Table.AggregationDistributionContext<BudgetItem> context) {
    Collection<BudgetItem> scope = context.getScope();
    if (scope.isEmpty()) {
        return;
    }

    double value = context.getValue() != null ?
            ((double) context.getValue()) : 0;

    for (BudgetItem budgetItem : scope) {
        budgetItem.setAmount(value / 100 * budgetItem.getPercent());
    }
}
table aggregation budget

To register the aggregation distribution provider programmatically, use the setAggregationDistributionProvider() component method.

ColumnCollapseEvent

ColumnCollapseEvent is sent whenever the column collapses state changes.

Example of subscribing to the event for the Table component defined in the screen XML with the tableCollapsed id:

@Subscribe("tableCollapsed")
public void onTableCollapsedColumnCollapse(Table.ColumnCollapseEvent<Customer> event) {
    notifications.create()
            .withCaption((event.isCollapsed() ? "Collapsed: " : "Expanded: ") +
                    event.getColumn().getCaption())
            .show();
}

To register the event handler programmatically, use the addColumnCollapseListener() component method.

ColumnReorderEvent

ColumnReorderEvent is fired when the user reorders columns.

Example of subscribing to the event for the Table component defined in the screen XML with the tableReorder id:

@Subscribe("tableReorder")
public void onTableReorderColumnReorder(Table.ColumnReorderEvent<Customer> event) {
    notifications.create()
            .withCaption("Columns were reordered!")
            .show();
}

To register the event handler programmatically, use the addColumnReorderListener() component method.

ContextHelpIconClickHandler

IconProvider

Each row can have an icon on the left by creating the row icon provider for Table:

@Install(to = "tableWithIcons", subject = "iconProvider")
private String tableWithIconsIconProvider(Customer customer) {
    return customer.getEmail() != null ?
            JmixIcon.MAIL_FORWARD.source() : JmixIcon.CANCEL.source();
}
table icon provider

To register the icon provider programmatically, use the setIconProvider() component method.

ItemDescriptionProvider

The item description provider generates tooltip descriptions for items when the user hovers over a cell.

In the example below, we will show an ItemDescriptionProvider usage for the tableWithDescription:

@Install(to = "tableWithDescription", subject = "itemDescriptionProvider")
private String tableWithDescriptionItemDescriptionProvider(
        Customer customer, String property) {
    if (property == null)
        return null;
    else if (property.equals("rewardPoints")) {
        return "Reward points are a part of the loyalty program affecting the rating of the " +
                customer.getFirstName() + " " + customer.getLastName();
    } else if (property.equals("level")) {
        switch (customer.getLevel()) {
            case SILVER:
                return "The customer gets special offers from sellers and birthday congratulations";
            case GOLD:
                return "The customer gets 2 coupons with a rating of $ 1";
            case PLATINUM:
                return "The customer gets a coupons with a par value of $ 3";
            case DIAMOND:
                return "The customer gets a coupon with a par value of $ 5";
        }
    }
    return null;
}
table item description

LookupSelectHandler

LookupSelectHandler is a handler invoked when a user presses Enter or double-clicks a table row in the lookup screen. It accepts the collection of selected entities. You can provide the handler as follows:

@Install(to = "tableLookupSelect", subject = "lookupSelectHandler")
private void tableLookupSelectLookupSelectHandler(Collection<Customer> collection) {
    checkCustomers(collection);
}

To register the select lookup handler programmatically, use the setLookupSelectHandler() component method.

SelectionEvent

SelectionEvent is sent when the selection changes.

Example of subscribing to the event for the table defined in the screen XML with the tableSelectEvent id:

@Autowired
private Table<Customer> tableSelectEvent;

@Subscribe("tableSelectEvent")
public void onTableSelectEventSelection(Table.SelectionEvent<Customer> event) {
    Customer customer = tableSelectEvent.getSingleSelected();
    notifications.create()
            .withCaption("You selected " + customer.getFirstName() +
                    " " + customer.getLastName() + " customer")
            .show();
}

To register the event handler programmatically, use the addSelectionListener() component method.

StyleProvider

StyleProvider allows setting table cell display style. The method accepts an implementation of the Table.StyleProvider interface as a parameter. The getStyleName() method of this interface is invoked by the table for each row and each cell separately. If the method is invoked for a row, the first parameter contains the entity instance displayed by the row, the second parameter is null. If the method is called for a cell, the second parameter contains the name of the attribute displayed by the cell.

Example of setting a style:

@Install(to = "styledTable", subject = "styleProvider")
private String styledTableStyleProvider(Customer entity, String property) {
    if (property == null) {
        if (Boolean.TRUE.equals(entity.getEmail() != null)) {
            return "customer-has-email";
        }
    } else if (property.equals("level")) {
        switch (entity.getLevel()) {
            case SILVER:
                return "level-silver";
            case GOLD:
                return "level-gold";
            case PLATINUM:
                return "level-platinum";
            case DIAMOND:
                return "level-diamond";
        }
    }
    return null;
}

Then you should define the cell and row styles in the application theme. Detailed information on creating a theme is available in Themes. Style names representing in the controller form CSS selectors. For example:

.customer-has-email {
    font-weight: bold;
}

.level-silver {
    background-color: #f2f2f2;
    color: black;
}

.level-gold {
    background-color: #ffda79;
    color: black;
}

.level-platinum {
    background-color: #637497;
    color: white;
}

.level-diamond {
    background-color: #8befff;
    color: black;
}
table style provider

To register the style provider programmatically, use the setStyleProvider() component method.