3.5.2.1.11. DataGrid

В этом разделе:

DataGrid, подобно компоненту Table, позволяет выводить информацию в виде таблицы, сортировать её, вызывать действия для выбранных строк, а также более эффективно управлять строками и колонками таблицы за счёт отложенной загрузки данных при прокрутке.

gui dataGrid 1

XML-имя компонента: dataGrid.

Пример описания компонента в XML-дескрипторе экрана:

<data>
    <collection id="ordersDc" class="com.company.sales.entity.Order" view="order-with-customer">
        <loader id="ordersDl">
            <query>
                <![CDATA[select e from sales_Order e order by e.date]]>
            </query>
        </loader>
    </collection>
</data>
<layout>
    <dataGrid id="ordersDataGrid" dataContainer="ordersDc" height="100%" width="100%">
        <columns>
            <column id="date" property="date"/>
            <column id="customer" property="customer.name"/>
            <column id="amount" property="amount"/>
        </columns>
    </dataGrid>
</layout>

В данном примере атрибут id - это идентификатор колонки, а атрибут property содержит имя атрибута сущности, содержащейся в источнике данных, который следует использовать в качестве данных для колонки.

Если вы хотите указать источник данных для таблицы программно в контроллере экрана, используйте атрибут metaClass вместо декларативного указания значения dataContainer в XML.

Элементы dataGrid

  • columns - элемент, определяющий набор колонок DataGrid. Если не задан, то столбцы таблицы будут автоматически определены из атрибутов представления, используемого при загрузке данных из контейнера данных. Элемент columns имеет следующие атрибуты:

    • includeAll – загружает все атрибуты представления, используемого при загрузке данных из контейнера данных.

      В приведенном ниже примере мы покажем все атрибуты из представления, используемого в customersDc. Если представление содержит системные свойства, они также будут показаны.

      <dataGrid id="dataGrid"
                width="100%"
                height="100%"
                dataContainer="customersDc">
          <columns includeAll="true"/>
      </dataGrid>

      Если представление сущности содержит ссылочный атрибут, этот атрибут выводится в соответствии с его шаблоном @NamePattern. Если вы хотите показать какой-то конкретный атрибут, он должен быть определен в представлении, а также в элементе column:

      <columns includeAll="true">
          <column id="address.street"/>
      </columns>

      Если представление не указано, атрибут includeAll загрузит все атрибуты из данной сущности и ее предков.

    • exclude – разделенный запятыми список атрибутов, которые не должны быть загружены в DataGrid.

      В приведенном ниже примере мы покажем все атрибуты, за исключением атрибутов name и order:

      <dataGrid id="dataGrid"
                width="100%"
                height="100%"
                dataContainer="customersDc">
          <columns includeAll="true"
                   exclude="name, order"/>
      </dataGrid>

    Каждая колонка описывается во вложенном элементе column со следующими атрибутами:

    • id - необязательный атрибут, содержит строковый идентификатор колонки. Если не задан, в качестве идентификатора колонки будет использоваться строковое значение атрибута property. В этом случае проставление атрибута property является обязательным, в противном случае будет брошено исключение GuiDevelopmentException. Атрибут id по-прежнему является обязательным для колонки, создаваемой программно.

    • property - необязательный атрибут, содержит название атрибута сущности, выводимого в колонке. Может быть как непосредственным атрибутом сущности, находящейся в источнике данных или data container, так и атрибутом связанной сущности; переход по графу объектов обозначается точкой. Например:

      <columns>
          <column id="date" property="date"/>
          <column id="customer" property="customer"/>
          <column id="customerName" property="customer.name"/>
          <column id="customerCountry" property="customer.address.country"/>
      </columns>
    • caption - необязательный атрибут, содержит заголовок колонки. Если не задан, будет отображено локализованное название атрибута сущности.

    • expandRatio - необязательный атрибут, устанавливает соотношение, с которым столбец расширяется. По умолчанию все колонки расширяются равномерно (словно все колонки имеют expandRatio = 1). Если хотя бы одной колонке установлено иное значение, все неявные значения удаляются и учитываются только проставленные.

    • collapsible - необязательный атрибут, определяющий, может ли пользователь управлять отображением колонок с помощью меню (sidebar menu) в правой верхней части DataGrid. По умолчанию имеет значение true.

    • collapsed - необязательный атрибут, при указании true колонка будет изначально скрыта. По умолчанию имеет значение false.

    • collapsingToggleCaption - необязательный атрибут, задает имя колонки в меню в правой верхней части DataGrid. По умолчанию имеет значение null, и в этом случае берется значение из заголовка колонки, доступного из свойства caption.

      gui dataGrid 2
    • resizable - необязательный атрибут, определяет, может ли пользователь изменять размер колонки.

    • sortable - необязательный атрибут, позволяющий запретить сортировку колонки. Вступает в действие, если атрибут sortable всего DataGrid установлен в true (что имеет место по умолчанию).

    • width - необязательный атрибут, отвечает за изначальную ширину колонки. Может принимать только числовые значения в пикселах.

    • minimumWidth - необязательный атрибут, отвечает за минимальную ширину колонки. Может принимать только числовые значения в пикселах.

    • maximumWidth - необязательный атрибут, отвечает за максимальную ширину колонки. Может принимать только числовые значения в пикселах.

    Элемент column может содержать вложенный элемент formatter для представления значения атрибута в виде, отличном от стандартного для данного DataType:

    <column id="date" property="date">
        <formatter class="com.haulmont.cuba.gui.components.formatters.DateFormatter"
                   format="yyyy-MM-dd HH:mm:ss"
                   useUserTimezone="true"/>
    </column>
  • actions - необязательный элемент для описания действий, связанных с DataGrid. Кроме описания произвольных действий, поддерживаются следующие стандартные действия, определяемые перечислением ListActionType: create, edit, remove, refresh, add, exclude.

  • buttonsPanel - необязательный элемент, создающий над DataGrid контейнер ButtonsPanel для отображения кнопок действий.

  • rowsCount - необязательный элемент, создающий для DataGrid компонент RowsCount, который позволяет загружать в DataGrid данные постранично. Размер страницы задается путем ограничения количества записей в data loader’е методом CollectionLoader.setMaxResults() в контроллере экрана. Также можно управлять количеством записей, используя универсальный компонент Filter, связанный с источником данных DataGrid.

    Компонент RowsCount может также отобразить общее число записей, возвращаемых текущим запросом в источнике данных, без извлечения этих записей. По клику пользователя на знак "?" вызывается com.haulmont.cuba.core.global.DataManager#getCount, что приводит к выполнению в БД запроса с такими же, как у текущего запроса, условиями, но с агрегатной функцией COUNT(*) вместо результатов. Полученное число отображается вместо знака "?".

    Атрибут autoLoad компонента RowsCount, установленный в значение true, позволяет автоматически загружать общее число записей. Его можно установить в XML-дескрипторе:

    <rowsCount autoLoad="true"/>

    Также включить или отключить автоматическую загрузку количества записей можно с помощью API в контроллере экрана:

    boolean autoLoadEnabled = rowsCount.getAutoLoad();
    rowsCount.setAutoLoad(false);

Атрибуты dataGrid

  • Атрибут aggregatable включает режим агрегации строк в DataGrid. Поддерживаются следующие операции:

    • SUM − сумма

    • AVG − среднее значение

    • COUNT − количество

    • MIN − минимальное значение

    • MAX − максимальное значение

    Для агрегируемых колонок необходимо указать элемент aggregation с атрибутом type, задающим функцию агрегации. По умолчанию в агрегируемых колонках поддерживаются только числовые типы данных, такие как Integer, Double, Long и BigDecimal. Агрегированные значения столбцов выводятся в дополнительной строке вверху DataGrid. Агрегирование в компоненте DataGrid функционирует также, как и в компоненте Table. Это означает, что вы можете использовать strategyClass, valueDescription и formatter для агрегации.

    Пример описания DataGrid с агрегацией:

    <dataGrid id="ordersDataGrid"
              dataContainer="ordersDc"
              aggregationPosition="BOTTOM"
              aggregatable="true">
        <columns>
            <column id="customerGrade" property="customer.grade">
                <aggregation strategyClass="com.company.sample.CustomerGradeAggregation"
                             valueDescription="msg://customerGradeAggregationDesc"/>
            </column>
            <column id="amount" property="amount">
                <aggregation type="SUM">
                    <formatter class="com.company.sample.MyFormatter"/>
                </aggregation>
            </column>
            ...
        </columns>
        ...
    </dataGrid>
  • Атрибут aggregationPosition позволяет задать положение строки агрегации: TOP или BOTTOM. По умолчанию используется TOP.

  • columnResizeMode - устанавливает режим изменения размера колонок пользователем. Поддерживаются следующие режимы (по умолчанию ANIMATED):

    • ANIMATED - размер колонки меняется сразу вслед за курсором.

    • SIMPLE - размер колонки меняется только после того, как курсор будет отпущен.

    Изменение размера колонок можно отслеживать с помощью слушателя ColumnResizeListener. Источник события изменения размера можно отследить с помощью метода isUserOriginated().

  • columnsCollapsingAllowed - разрешает или запрещает пользователю скрывать колонки с помощью меню (sidebar menu) в правой части шапки DataGrid. Существуют дополнительные пункты меню:

    • Select all − показать все колонки;

    • Deselect all − спрятать все колонки.

      gui dataGrid 16

      Флажками в меню отмечаются отображаемые в данный момент колонки. В момент установки перезаписывает значение collapsed каждой отдельной колонки. Установка значения в false не позволяет атрибуту collapsed отдельной колонки принять значение true.

      Скрытие и отображение колонок можно отслеживать с помощью слушателя ColumnCollapsingChangeListener. Источник события можно отследить с помощью метода isUserOriginated().

  • contextMenuEnabled - включает или выключает контекстное меню в DataGrid. По умолчанию имеет значение true.

    Щелчки правой кнопкой мыши по области компонента DataGrid можно отслеживать с помощью слушателя ContextClickListener.

  • editorBuffered - включает буферизацию в режиме внутристрочного редактирования. По умолчанию буферизация разрешена (true).

  • editorCancelCaption - устанавливает заголовок кнопки отмены в режиме редактирования DataGrid.

  • editorCrossFieldValidate - включает перекрестную проверку полей во встроенном редакторе. По умолчанию имеет значение true.

  • editorEnabled - включает отображение UI для внутристрочного редактирования ячеек. Если dataGrid привязан к источнику данных с типом KeyValueCollectionContainer, предполагается, что он используется только для чтения, и использование атрибута editorEnabled в этом случае бессмысленно.

  • editorSaveCaption - устанавливает заголовок кнопки сохранения изменений в режиме редактирования DataGrid.

  • frozenColumnCount - устанавливает количество фиксированных колонок в DataGrid. Значение 0 означает, что фиксированных колонок не будет, кроме встроенной колонки с чекбоксами для множественного выбора, если она используется. Значение -1 означает, что фиксированных колонок не будет вообще.

  • headerVisible - определяет видимость заголовка DataGrid. По умолчанию имеет значение true.

  • htmlSanitizerEnabled - разрешает или запрещает санитизацию HTML. Компонент DataGrid имеет несколько провайдеров, которые могут отображать HTML:

    • HtmlRenderer

    • RowDescriptionProvider с режимом ContentMode.HTML

    • DescriptionProvider с режимом ContentMode.HTML

      Результат выполнения этих провайдеров будет санитизирован, если для компонента DataGrid атрибут htmlSanitizerEnabled установлен в значение true.

      protected static final String UNSAFE_HTML = "<i>Jackdaws </i><u>love</u> <font size=\"javascript:alert(1)\" " +
                  "color=\"moccasin\">my</font> " +
                  "<font size=\"7\">big</font> <sup>sphinx</sup> " +
                  "<font face=\"Verdana\">of</font> <span style=\"background-color: " +
                  "red;\">quartz</span><svg/onload=alert(\"XSS\")>";
      
      @Inject
      private DataGrid<Customer> customersDataGrid;
      @Inject
      private DataGrid<Customer> customersDataGrid2;
      @Inject
      private DataGrid<Customer> customersDataGrid3;
      
      @Subscribe
      public void onInit(InitEvent event) {
          customersDataGrid.setHtmlSanitizerEnabled(true);
          customersDataGrid.getColumn("name")
                  .setRenderer(customersDataGrid.createRenderer(DataGrid.HtmlRenderer.class));
      
          customersDataGrid2.setHtmlSanitizerEnabled(true);
          customersDataGrid2.setRowDescriptionProvider(customer -> UNSAFE_HTML, ContentMode.HTML);
      
          customersDataGrid3.setHtmlSanitizerEnabled(true);
          customersDataGrid3.getColumn("name").setDescriptionProvider(customer -> UNSAFE_HTML, ContentMode.HTML);
      }

      Значение атрибута htmlSanitizerEnabled имеет приоритет над значением глобального свойства cuba.web.htmlSanitizerEnabled.

      Если вы хотите использовать HtmlRenderer с пользовательским presentationProvider, результирующее значение не будет санитизировано по умолчанию. Если вам нужно санитизировать значение, вы должны это сделать самостоятельно:

      protected static final String UNSAFE_HTML = "<i>Jackdaws </i><u>love</u> <font size=\"javascript:alert(1)\" " +
                  "color=\"moccasin\">my</font> " +
                  "<font size=\"7\">big</font> <sup>sphinx</sup> " +
                  "<font face=\"Verdana\">of</font> <span style=\"background-color: " +
                  "red;\">quartz</span><svg/onload=alert(\"XSS\")>";
      
      @Inject
      private DataGrid<Customer> customersDataGrid;
      
      @Inject
      private HtmlSanitizer htmlSanitizer;
      
      @Subscribe
      public void onInit(InitEvent event) {
          customersDataGrid.getColumn("name")
                  .setRenderer(customersDataGrid.createRenderer(DataGrid.HtmlRenderer.class),
                          (Function<String, String>) nameValue -> htmlSanitizer.sanitize(UNSAFE_HTML));
      }
  • reorderingAllowed - разрешает или запрещает пользователю менять местами колонки, перетаскивая их с помощью мыши. По умолчанию имеет значение true.

    Изменение расположения колонок можно отслеживать с помощью слушателя ColumnReorderListener. Источник события изменения порядка колонок можно отследить с помощью метода isUserOriginated().

  • selectionMode - определяет режим выделения строк. Поддерживаются следующие режимы:

    • SINGLE - единичный выбор строки.

    • MULTI - множественный выбор строк как в таблице.

    • MULTI_CHECK - множественный выбор строк с использованием встроенной колонки с чекбоксами.

    • NONE - выбор строк отключен.

      Выделение строк можно отслеживать с помощью слушателя SelectionListener. Источник события выделения можно отследить с помощью метода isUserOriginated().

      gui dataGrid 3
  • sortable - разрешает или запрещает сортировку в DataGrid. По умолчанию имеет значение true. Если сортировка разрешена, то при нажатии на название колонки справа от названия появляется соответствующий значок. Сортировку некоторой отдельной колонки можно запретить с помощью атрибута sortable этой колонки.

    События сортировки DataGrid можно отслеживать с помощью слушателя SortListener. Источник события сортировки можно отследить с помощью метода isUserOriginated().

  • textSelectionEnabled - разрешает или запрещает выделение текста в ячейках DataGrid. По умолчанию имеет значение false.

Методы интерфейса DataGrid

  • getColumns() - возвращает текущий набор колонок DataGrid в порядке их текущего отображения.

  • getSelected(), getSingleSelected() - возвращают экземпляры сущностей, соответствующие выделенным в таблице строкам. Коллекцию можно получить вызовом метода getSelected(). Если ничего не выбрано, возвращается пустой набор. Если установлен SelectionMode.SINGLE, удобно пользоваться методом getSingleSelected(), возвращающим одну выбранную сущность или null, если ничего не выбрано.

  • getVisibleColumns() - возвращет набор видимых колонок DataGrid в порядке их текущего отображения.

  • scrollTo() - позволяет программно прокрутить DataGrid до нужной записи. Метод принимает экземпляр сущности, определяющий нужную строку в DataGrid. Перегруженный метод, помимо сущности, принимает ScrollDestination, имеющий следующие возможные значения:

    • ANY - прокрутить как можно меньше, чтобы показать нужную запись.

    • START - прокрутить так, чтобы нужная запись оказалась в начале видимой области DataGrid.

    • MIDDLE - прокрутить так, чтобы нужная запись оказалась в центре видимой области DataGrid.

    • END - прокрутить так, чтобы нужная запись оказалась в конце видимой области DataGrid.

  • scrollToStart() and scrollToEnd() - позволяют прокрутить DataGrid в начало и конец соответственно.

  • addCellStyleProvider() - позволяет добавить стиль отображения ячеек DataGrid.

  • addRowStyleProvider() - позволяет добавить стиль отображения строк DataGrid.

  • setEnterPressAction() - позволяет задать действие, выполняемое при нажатии клавиши Enter. Если такое действие не задано, таблица пытается найти среди своих действий подходящее в следующем порядке:

    • действие, назначенное методом setItemClickAction().

    • действие, назначенное на клавишу Enter посредством свойства shortcut.

    • действие с именем edit.

    • действие с именем view.

    Если такое действие найдено и имеет свойство enabled = true, оно выполняется.

  • setItemClickAction() - позволяет задать действие, выполняемое при двойном клике на строке таблицы. Если такое действие не задано, при двойном клике таблица пытается найти среди своих действий подходящее в следующем порядке:

    • действие, назначенное на клавишу Enter посредством свойства shortcut.

    • действие с именем edit.

    • действие с именем view.

    Если такое действие найдено и имеет свойство enabled = true, оно выполняется.

    События клика по элементу DataGrid можно отслеживать с помощью слушателя ItemClickListener.

  • sort() - сортирует данные в переданной колонке в направлении, заданном одним из двух доступных значений перечисления SortDirection:

    • ASCENDING - сортировка по возрастанию (например, A-Z, 1..9).

    • DESCENDING - сортировка по убыванию (например, Z-A, 9..1).

  • Метод getAggregationResults() возвращает мэп с результатами агрегации, где ключи в мэп − идентификаторы столбцов DataGrid, а значения − значения агрегации.

Использование всплывающих подсказок

  • setDescriptionProvider() - используется для генерации всплывающих подсказок для отдельных колонок DataGrid. Строка описания может содержать HTML-разметку.

    @Inject
    private DataGrid<Customer> customersDataGrid;
    
    @Subscribe
    protected void onInit(InitEvent event) {
        customersDataGrid.getColumnNN("age").setDescriptionProvider(customer ->
                        getPropertyCaption(customer, "age") +
                                customer.getAge(),
                ContentMode.HTML);
    
        customersDataGrid.getColumnNN("active").setDescriptionProvider(customer ->
                        getPropertyCaption(customer, "active") +
                                getMessage(customer.getActive() ? "trueString" : "falseString"),
                ContentMode.HTML);
    
        customersDataGrid.getColumnNN("grade").setDescriptionProvider(customer ->
                        getPropertyCaption(customer, "grade") +
                                messages.getMessage(customer.getGrade()),
                ContentMode.HTML);
    }
    gui dataGrid 11
  • setRowDescriptionProvider() - используется для генерации всплывающих подсказок для строк DataGrid. Если подсказки для колонок также установлены, подсказка, сгенерированная для строки, будет использована только для тех ячеек, для которых не задана подсказка колонки.

    customersDataGrid.setRowDescriptionProvider(Instance::getInstanceName);
    gui dataGrid 10

Использование интерфейса DetailsGenerator

Интерфейс DetailsGenerator позволяет задать свой компонент для отображения информации о выбранной строке DataGrid с помощью метода setDetailsGenerator():

@Inject
private DataGrid<Order> ordersDataGrid;
@Inject
private UiComponents uiComponents;

@Install(to = "ordersDataGrid", subject = "detailsGenerator")
protected Component ordersDataGridDetailsGenerator(Order order) {
    VBoxLayout mainLayout = uiComponents.create(VBoxLayout.NAME);
    mainLayout.setWidth("100%");
    mainLayout.setMargin(true);

    HBoxLayout headerBox = uiComponents.create(HBoxLayout.NAME);
    headerBox.setWidth("100%");

    Label infoLabel = uiComponents.create(Label.NAME);
    infoLabel.setHtmlEnabled(true);
    infoLabel.setStyleName("h1");
    infoLabel.setValue("Order info:");

    Component closeButton = createCloseButton(order);
    headerBox.add(infoLabel);
    headerBox.add(closeButton);
    headerBox.expand(infoLabel);

    Component content = getContent(order);

    mainLayout.add(headerBox);
    mainLayout.add(content);
    mainLayout.expand(content);

    return mainLayout;
}

private Component createCloseButton(Order entity) {
    Button closeButton = uiComponents.create(Button.class);
    // ... (1)
    return closeButton;
}

private Component getContent(Order entity) {
    Label<String> content = uiComponents.create(Label.TYPE_STRING);
    content.setHtmlEnabled(true);
    StringBuilder sb = new StringBuilder();
    // ... (2)
    content.setValue(sb.toString());
    return content;
}
1 – Смотрите код метода createCloseButton целиком в классе DataGridDetailsGeneratorSample.
2 – Смотрите код метода getContent целиком в классе DataGridDetailsGeneratorSample.

Результат:

gui dataGrid 15

Использование режима внутристрочного редактирования

У компонента DataGrid есть API, позволяющий напрямую редактировать записи в ячейках. Во время редактирования ячейки будет отображён UI с кнопками для сохранения и отмены изменений.

Методы API встроенного редактора:

  • getEditedItem() - возвращает редактируемую запись.

  • isEditorActive() - возвращает true, если в момент вызова редактируется какая-либо запись.

  • editItem(Object itemId)(устаревший) - открывает интерфейс внутристрочного редактора для идентификатора указанной записи. Пролистывает таблицу до нужной записи, если в момент вызова она не была видна на экране.

  • edit(Entity item) - открывает интерфейс внутристрочного редактора для указанной записи. Пролистывает таблицу до нужной записи, если в момент вызова она не была видна на экране.

Встроенный редактор DataGrid может учитывать ограничения сущностей (перекрестная проверка полей). Если есть ошибки валидации, DataGrid покажет сообщение об ошибке. Чтобы включить/отключить проверку или получить текущее состояние, используйте следующие методы:

  • setEditorCrossFieldValidate(boolean validate) - включает или отключает перекрестную проверку полей во встроенном редакторе. По умолчанию имеет значение true.

  • isEditorCrossFieldValidate() - возвращает значение true, если для встроенного редактора включена перекрестная проверка полей.

Вы также можете добавить к встроенному редактору или удалить слушатели, использовав следующие методы:

  • addEditorOpenListener(), removeEditorOpenListener() - слушатель открытия встроенного редактора DataGrid.

    Данный слушатель обрабатывает событие открытия встроенного редактора DataGrid по двойному щелчку и позволяет получить доступ к полям редактируемой строки. Это даёт возможность обновлять значения в отдельных полях в зависимости от изменения значений в других полях, не закрывая встроенный редактор.

    Например:

    customersTable.addEditorOpenListener(editorOpenEvent -> {
        Map<String, Field> fieldMap = editorOpenEvent.getFields();
        Field active = fieldMap.get("active");
        Field grade = fieldMap.get("grade");
    
        ValueChangeListener listener = e ->
                active.setValue(true);
        grade.addValueChangeListener(listener);
    });
  • addEditorCloseListener(), removeEditorCloseListener() - слушатель закрытия встроенного редактора DataGrid.

  • addEditorPreCommitListener(), removeEditorPreCommitListener() - слушатель редактора DataGrid, срабатывающий в процессе коммита изменений.

  • addEditorPostCommitListener(), removeEditorPostCommitListener() - слушатель, срабатывающий на финальной стадии коммита изменений.

Коммит изменений сохраняет их в источнике данных. Логику сохранения изменений в базу данных необходимо задать отдельно.

Само поле редактирования также может быть изменено с помощью класса EditorFieldGenerationContext. Используйте метод setEditFieldGenerator() для определённой колонки таблицы, чтобы указать компонент для отображения в режиме редактирования этой колонки:

@Inject
private DataGrid<Order> ordersDataGrid;
@Inject
private UiComponents uiComponents;

@Subscribe
protected void onInit(InitEvent event) {
    ordersDataGrid.getColumnNN("amount").setEditFieldGenerator(orderEditorFieldGenerationContext -> {
        LookupField<BigDecimal> lookupField = uiComponents.create(LookupField.NAME);
        lookupField.setValueSource((ValueSource<BigDecimal>) orderEditorFieldGenerationContext
                .getValueSourceProvider().getValueSource("amount"));
        lookupField.setOptionsList(Arrays.asList(BigDecimal.ZERO, BigDecimal.ONE, BigDecimal.TEN));

        return lookupField;
    });
}

Результат:

gui dataGrid 14

Использование интерфейса ColumnGenerator

DataGrid имеет возможность добавлять генерируемые, или высчитываемые, колонки. Существует два пути создания таких колонок:

  • Декларативно, используя аннотацию @Install в контроллере экрана:

    @Inject
    private UiComponents uiComponents;
    
    @Install(to = "dataGrid.fullName", subject = "columnGenerator")
    protected Component fullNameColumnGenerator(DataGrid.ColumnGeneratorEvent<Customer> e) {
        Label<String> label = uiComponents.create(Label.TYPE_STRING);
        label.setValue(e.getItem().getFirstName() + " " + e.getItem().getLastName());
        return label;
    }

    ColumnGeneratorEvent хранит информацию о сущности, которая отображается в текущей строке DataGrid, и идентификатор колонки.

  • Программно с помощью следующих методов:

    • addGeneratedColumn(String columnId, ColumnGenerator generator)

    • addGeneratedColumn(String columnId, ColumnGenerator generator, int index)

    ColumnGenerator - это специальный интерфейс, который описывает генерируемую колонку:

    • значение для каждой строки колонки,

    • тип значения - общий для всей колонки.

    Например, для добавления генерируемой колонки, которая будет отображать логин пользователя в верхнем регистре, можно использовать следующий код:

    @Subscribe
    protected void onInit(InitEvent event) {
        DataGrid.Column column = usersGrid.addGeneratedColumn("loginUpperCase", new DataGrid.ColumnGenerator<User, String>(){
            @Override
            public String getValue(DataGrid.ColumnGeneratorEvent<User> event){
                return event.getItem().getLogin().toUpperCase();
            }
    
            @Override
            public Class<String> getType(){
                return String.class;
            }
        }, 1);
        column.setCaption("Login Upper Case");
    }

    Результат:

    gui dataGrid 7

    По умолчанию, генерируемая колонка добавляется в конец таблицы. Управлять расположением генерируемых колонок можно либо вставляя колонку по индексу, либо предварительно добавив колонку в XML с id, который потом передавать в метод addGeneratedColumn.

Использование рендереров

Отображение данных в колонках может быть изменено с помощью параметризованных декларативных рендереров. Некоторые рендереры DataGrid задаются специальными XML-элементами с параметрами, определенными в соответствующих атрибутах. Рендереры могут быть объявлены также и для не генерируемых колонок.

Список рендереров, поддерживаемых платформой:

  • ButtonRenderer – рендерер, который использует строковое значение в качестве заголовка кнопки.

    ButtonRenderer не может быть объявлен в XML-дескрипторе, так как в нем невозможно определить слушатель отслеживания кликов по кнопке. Studio сгенерирует код объявления ButtonRenderer в методе init() контроллера экрана:

    @Inject
    private DataGrid<Customer> customersDataGrid;
    
    @Inject
    private Notifications notifications;
    
    @Subscribe
    public void onInit(InitEvent event) {
        DataGrid.ButtonRenderer<Customer> customersDataGridNameRenderer =
                    customersDataGrid.createRenderer(DataGrid.ButtonRenderer.class);
        customersDataGridNameRenderer.setRendererClickListener(clickableButtonRendererClickEvent ->
            {
                notifications.create()
                    .withType(Notifications.NotificationType.TRAY)
                    .withCaption("ButtonRenderer")
                    .withDescription("Column id: " + clickableButtonRendererClickEvent.getColumnId())
                    .show();
            });
        customersDataGrid.getColumn("name").setRenderer(customersDataGridNameRenderer);
    }
  • CheckBoxRenderer – рендерер, который отображает булево значение в виде значков чек-бокса.

    Элемент column имеет вложенный элемент checkBoxRenderer:

    <column property="checkBoxRenderer" id="checkBoxRendererColumn">
        <checkBoxRenderer/>
    </column>
  • ClickableTextRenderer – отображает простой текст в виде ссылки. Позволяет задать обработчик, который будет вызван после нажатия на ссылку.

    ClickableTextRenderer не может быть объявлен в XML-дескрипторе, так как в нем невозможно определить слушатель отслеживания нажатий на ссылку. Studio сгенерирует код объявления ClickableTextRenderer в методе init() контроллера экрана:

    @Inject
    private DataGrid<Customer> customersDataGrid;
    
    @Inject
    private Notifications notifications;
    
    @Subscribe
    public void onInit(InitEvent event) {
        DataGrid.ClickableTextRenderer<Customer> customersDataGridNameRenderer =
                    customersDataGrid.createRenderer(DataGrid.ClickableTextRenderer.class);
        customersDataGridNameRenderer.setRendererClickListener(clickableTextRendererClickEvent -> {
            notifications.create()
                    .withType(Notifications.NotificationType.TRAY)
                    .withCaption("ClickableTextRenderer")
                    .withDescription("Column id: " + clickableTextRendererClickEvent.getColumnId())
                    .show();
        });
        customersDataGrid.getColumn("name").setRenderer(customersDataGridNameRenderer);
    }
  • ComponentRenderer – рендерер для компонентов UI.

    Элемент column имеет вложенный элемент componentRenderer:

    <column property="componentRenderer" id="componentRendererColumn">
        <componentRenderer/>
    </column>
  • DateRenderer – рендерер для отображения дат в заданном формате.

    Элемент column имеет вложенный элемент dateRenderer с необязательным атрибутом nullRepresentation и обязательным строковым атрибутом format:

    <column property="dateRenderer" id="dateRendererColumn">
        <dateRenderer nullRepresentation="null" format="yyyy-MM-dd HH:mm:ss"/>
    </column>
  • IconRenderer – рендерер, который представляет CubaIcon.

    Элемент column имеет вложенный элемент iconRenderer.

    Ниже приведен пример рендеринга строкового атрибута сущности в виде CubaIcon:

    <column id="iconOS" property="iconOS">
        <iconRenderer/>
    </column>
    @Install(to = "devicesTable.iconOS", subject = "columnGenerator")
    private Icons.Icon devicesTableIconOSColumnGenerator(DataGrid.ColumnGeneratorEvent<Device> event) {
        return CubaIcon.valueOf(event.getItem().getIconOS());
    }

    Результат:

    gui dataGrid iconColumn
  • ImageRenderer – рендерер, который использует строковое значение в качестве пути до изображения.

    ImageRenderer не может быть объявлен в XML-дескрипторе, так как в нем невозможно определить слушатель отслеживания нажатий на изображение. Studio сгенерирует код объявления ImageRenderer в методе init() контроллера экрана:

    @Inject
    private DataGrid<TestEntity> testEntitiesDataGrid;
    @Inject
    private Notifications notifications;
    
    @Subscribe
    public void onInit(InitEvent event) {
        DataGrid.ImageRenderer<TestEntity> imageRenderer =
                testEntitiesDataGrid.createRenderer(DataGrid.ImageRenderer.class);
        imageRenderer.setRendererClickListener(imageRendererClickEvent -> notifications.create()
                .withType(Notifications.NotificationType.TRAY)
                .withCaption("ImageRenderer")
                .withDescription("Column id: " + imageRendererClickEvent.getColumnId())
                .show());
        testEntitiesDataGrid.getColumn("imageRendererColumn").setRenderer(imageRenderer);
    }
  • HtmlRenderer – рендерер для отображения HTML-разметки.

    Элемент column имеет вложенный элемент htmlRenderer с необязательным атрибутом nullRepresentation:

    <column property="htmlRenderer" id="htmlRendererColumn">
        <htmlRenderer nullRepresentation="null"/>
    </column>
  • LocalDateRenderer – рендерер для отображения дат как значений типа LocalDate.

    Элемент column имеет вложенный элемент localDateRenderer с необязательным атрибутом nullRepresentation и обязательным строковым атрибутом format:

    <column property="localDateRenderer" id="localDateRendererColumn">
        <localDateRenderer nullRepresentation="null" format="dd/MM/YYYY"/>
    </column>
  • LocalDateTimeRenderer – рендерер для отображения дат как значений типа LocalDateTime.

    Элемент column имеет вложенный элемент localDateTimeRenderer с необязательным атрибутом nullRepresentation и обязательным строковым атрибутом format:

    <column property="localDateTimeRenderer" id="localDateTimeRendererColumn">
        <localDateTimeRenderer nullRepresentation="null" format="dd/MM/YYYY HH:mm:ss"/>
    </column>
  • NumberRenderer – рендерер для отображения чисел в заданном формате.

    Элемент column имеет вложенный элемент numberRenderer с необязательным атрибутом nullRepresentation и обязательным строковым атрибутом format:

    <column property="numberRenderer" id="numberRendererColumn">
        <numberRenderer nullRepresentation="null" format="%f"/>
    </column>
  • ProgressBarRenderer – рендерер, который отображает double-значения от 0 до 1 в виде компонента ProgressBar.

    Элемент column имеет вложенный элемент progressBarRenderer:

    <column property="progressBar" id="progressBarColumn">
        <progressBarRenderer/>
    </column>
  • TextRenderer – рендерер для отображения простого текста.

    Элемент column имеет вложенный элемент textRenderer с необязательным атрибутом nullRepresentation:

    <column property="textRenderer" id="textRendererColumn">
        <textRenderer nullRepresentation="null"/>
    </column>

Интерфейс WebComponentRenderer позволяет настроить отображение веб-компонентов различных типов в ячейках DataGrid. Интерфейс реализован только для блока Web Module. Ниже приведён пример создания колонки для отображения компонента LookupField:

@Inject
private DataGrid<User> usersGrid;
@Inject
private UiComponents uiComponents;
@Inject
private Configuration configuration;
@Inject
private Messages messages;

@Subscribe
protected void onInit(InitEvent event) {
    Map<String, Locale> locales = configuration.getConfig(GlobalConfig.class).getAvailableLocales();
    Map<String, String> options = new TreeMap<>();
    for (Map.Entry<String, Locale> entry : locales.entrySet()) {
        options.put(entry.getKey(), messages.getTools().localeToString(entry.getValue()));
    }

    DataGrid.Column column = usersGrid.addGeneratedColumn("language",
            new DataGrid.ColumnGenerator<User, Component>() {
                @Override
                public Component getValue(DataGrid.ColumnGeneratorEvent<User> event) {
                    LookupField<String> component = uiComponents.create(LookupField.NAME);
                    component.setOptionsMap(options);
                    component.setWidth("100%");

                    User user = event.getItem();
                    component.setValue(user.getLanguage());

                    component.addValueChangeListener(e -> user.setLanguage(e.getValue()));

                    return component;
                }

                @Override
                public Class<Component> getType() {
                    return Component.class;
                }
            });

    column.setRenderer(new WebComponentRenderer());
}

Результат:

gui dataGrid 13

Когда тип поля не совпадает с типом данных, принимаемых рендерером, можно при создании рендерера создать функцию для конвертации между типами данных модели и представления. К примеру, чтобы отобразить булево значение в виде значка, можно использовать HtmlRenderer, который умеет отображать HTML-разметку, и добавить логику, которая будет превращать булево значение в подходящую разметку для отображения значков.

@Inject
private DataGrid<User> usersGrid;

@Subscribe
protected void onInit(InitEvent event) {

    DataGrid.Column<User> hasEmail = usersGrid.addGeneratedColumn("hasEmail", new DataGrid.ColumnGenerator<User, Boolean>() {
        @Override
        public Boolean getValue(DataGrid.ColumnGeneratorEvent<User> event) {
            return StringUtils.isNotEmpty(event.getItem().getEmail());
        }

        @Override
        public Class<Boolean> getType() {
            return Boolean.class;
        }
    });

    hasEmail.setCaption("Has Email");
    hasEmail.setRenderer(
        usersGrid.createRenderer(DataGrid.HtmlRenderer.class),
        (Function<Boolean, String>) hasEmailValue -> {
            return BooleanUtils.isTrue(hasEmailValue)
                    ? FontAwesome.PLUS_SQUARE.getHtml()
                    : FontAwesome.MINUS_SQUARE.getHtml();
        });
}

Результат:

gui dataGrid 9

Создавать рендереры можно тремя способами:

  • декларативно, используя специальные вложенные элементы у элемента column.

  • через метод-фабрику интерфейса DataGrid, передавая в него интерфейс рендерера, для которого нужно создать имплементацию. Подходит для GUI и Web модулей.

  • непосредственно создавая имплементацию рендерера для соответствующего модуля:

    dataGrid.createRenderer(DataGrid.ImageRenderer.class) → new WebImageRenderer()

    На данный момент этот способ реализован только для модуля Web.

Header и Footer

Интерфейсы HeaderRow и FooterRow предназначены для отображения ячеек заголовков и строк с итогами таблицы соответственно. Эти ячейки могут быть объединёнными для нескольких колонок.

Для создания и настройки заголовков и итогов используются следующие методы:

  • appendHeaderRow(), appendFooterRow() - добавляет новую строку внизу секции заголовков/итогов.

  • prependHeaderRow(), prependFooterRow() - добавляет новую строку наверху секции заголовков/итогов.

  • addHeaderRowAt(), addFooterRowAt() - вставляет новую строку на заданную позицию в секции. Текущая строка на этой позиции, а также все следующие ниже, сдвигаются вниз с увеличением их индекса на 1.

  • removeHeaderRow(), removeFooterRow() - удаляет указанную строку в секции.

  • getHeaderRowCount(), getFooterRowCount() - возвращает количество строк в секции.

  • setDefaultHeaderRow() - устанавливает заголовок таблицы по умолчанию. Интерфейс стандартного заголовка по умолчанию включает в себя элементы для сортировки колонок таблицы.

Интерфейсы HeaderCell и FooterCell позволяют управлять статическими ячейками:

  • setStyleName() - устанавливает пользовательский стиль для данной ячейки.

  • getCellType() - возвращает тип содержимого данной ячейки. Перечисление DataGridStaticCellType содержит 3 стандартных типа статических ячеек:

    • TEXT

    • HTML

    • COMPONENT

  • getComponent(), getHtml(), getText() - возвращает содержимое данной ячейки в зависимости от её типа.

Ниже приведён пример таблицы DataGrid с заголовком, содержащим объединённые ячейки, и строкой итогов, в которой отображаются вычисляемые значения:

<dataGrid id="dataGrid" dataContainer="countryGrowthDs" width="100%">
    <columns>
        <column property="country"/>
        <column property="year2017"/>
        <column property="year2018"/>
    </columns>
</dataGrid>
@Inject
private DataGrid<CountryGrowth> dataGrid;
@Inject
private UserSessionSource userSessionSource;
@Inject
private Messages messages;
@Inject
private CollectionContainer<CountryGrowth> countryGrowthsDc;

private DecimalFormat percentFormat;

@Subscribe
protected void onBeforeShow(BeforeShowEvent event) {
    initPercentFormat();
    initHeader();
    initFooter();
    initRenderers();
}

private DecimalFormat initPercentFormat() {
    percentFormat = (DecimalFormat) NumberFormat.getPercentInstance(userSessionSource.getLocale());
    percentFormat.setMultiplier(1);
    percentFormat.setMaximumFractionDigits(2);
    return percentFormat;
}

private void initRenderers() {
    dataGrid.getColumnNN("year2017").setRenderer(new WebNumberRenderer(percentFormat));
    dataGrid.getColumnNN("year2018").setRenderer(new WebNumberRenderer(percentFormat));
}

private void initHeader() {
    DataGrid.HeaderRow headerRow = dataGrid.prependHeaderRow();
    DataGrid.HeaderCell headerCell = headerRow.join("year2017", "year2018");
    headerCell.setText("GDP growth");
    headerCell.setStyleName("center-bold");
}

private void initFooter() {
    DataGrid.FooterRow footerRow = dataGrid.appendFooterRow();
    footerRow.getCell("country").setHtml("<strong>" + messages.getMainMessage("average") + "</strong>");
    footerRow.getCell("year2017").setText(percentFormat.format(getAverage("year2017")));
    footerRow.getCell("year2018").setText(percentFormat.format(getAverage("year2018")));
}

private double getAverage(String propertyId) {
    double average = 0.0;
    List<CountryGrowth> items = countryGrowthsDc.getItems();
    for (CountryGrowth countryGrowth : items) {
        Double value = countryGrowth.getValue(propertyId);
        average += value != null ? value : 0.0;
    }
    return average / items.size();
}
gui dataGrid 12

Стили DataGrid

К компоненту DataGrid можно применить заданные стили. Стиль задается в XML-дескрипторе с помощью атрибута stylename.

<dataGrid id="dataGrid"
          width="100%"
          height="100%"
          stylename="borderless"
          dataContainer="customersDc">
</dataGrid>

Также стиль можно задать программно в контроллере экрана.

dataGrid.setStyleName("borderless");

Предопределенные стили:

  • borderless - удаляет внешнюю рамку.

  • no-horizontal-lines - удаляет горизонтальные строковые разделители.

  • no-vertical-lines - удаляет вертикальные разделители столбцов.

  • no-stripes - отключает чередование цветов строк таблицы.

Внешний вид компонента DataGrid можно настроить с помощью переменных SCSS с префиксом $cuba-datagrid-*. Эти переменные можно изменить в визуальном редакторе после расширения темы или создания новой темы.


Атрибуты dataGrid

aggregatable - aggregationPosition - align - caption - captionAsHtml - colspan - columnResizeMode - columnsCollapsingAllowed - contextHelpText - contextHelpTextHtmlEnabled - contextMenuEnabled - css - dataContainer - description - descriptionAsHtml - editorBuffered - editorCancelCaption - editorCrossFieldValidate - editorEnabled - editorSaveCaption - emptyStateLinkMessage - emptyStateMessage - enable - box.expandRatio - frozenColumnCount - headerVisible - height - htmlSanitizerEnabled - icon - id - metaClass - reorderingAllowed - responsive - rowspan - selectionMode - settingsEnabled - sortable - stylename - tabIndex - textSelectionEnabled - visible - width

Элементы dataGrid

actions - buttonsPanel - columns - rowsCount

Атрибуты columns

includeAll - exclude

Атрибуты column

caption - collapsed - collapsible - collapsingToggleCaption - editable - expandRatio - id - maximumWidth - minimumWidth - property - resizable - sort - sortable - width

Элементы column

aggregation - checkBoxRenderer - componentRenderer - dateRenderer - formatter - iconRenderer - htmlRenderer - localDateRenderer - localDateTimeRenderer - numberRenderer - progressBarRenderer - textRenderer

Атрибуты aggregation

strategyClass - type - valueDescription

API

addGeneratedColumn - applySettings - createRenderer - edit - getAggregationResults - saveSettings - getColumns - setDescriptionProvider - addCellStyleProvider - setConverter - setDetailsGenerator - setEditorCrossFieldValidate - setEmptyStateLinkClickHandler - setEnterPressAction - setItemClickAction - setRenderer - setRowDescriptionProvider - addRowStyleProvider - sort

Слушатели dataGrid

ColumnCollapsingChangeListener - ColumnReorderListener - ColumnResizeListener - ContextClickListener - EditorCloseListener - EditorOpenListener - EditorPostCommitListener - EditorPreCommitListener - ItemClickListener - SelectionListener - SortListener

Предопределенные стили

borderless - no-horizontal-lines - no-vertical-lines - no-stripes