dataGrid

dataGrid - это таблица данных, которую можно редактировать, сортировать, фильтровать и агрегировать по столбцам. Для работы с большими объёмами данных используется отложенная загрузка при прокрутке, что обеспечивает высокую производительность.

XML-элемент

dataGrid

Java-класс

DataGrid

Атрибуты

id - aggregatable - aggregationPosition - alignSelf - allRowsVisible - classNames - colspan - columnReorderingAllowed - css - dataContainer - detailsVisibleOnClick - dropMode - editorBuffered - emptyStateText - enabled - focusShortcut - height - maxHeight - maxWidth - metaClass - minHeight - minWidth - multiSort - multiSortOnShiftClickOnly - multiSortPriority - nestedNullBehavior - pageSize - rowDraggable - selectionMode - tabIndex - themeNames - visible - width

Обработчики

AttachEvent - BlurEvent - CellFocusEvent - ColumnReorderEvent - ColumnResizeEvent - DetachEvent - FocusEvent - GridDragEndEvent - GridDragStartEvent - GridDropEvent - ItemClickEvent - ItemDoubleClickEvent - SortEvent - dataGenerator - dragFilter - dropFilter enterPressHandler - tooltipGenerator

Элементы

actions - columns - column - contextMenu - emptyStateComponent

Основы

Для создания компонента используйте XML-элемент dataGrid и свяжите его с контейнером данных. Поддерживаются оба типа контейнеров: коллекции и KeyValue.

Затем укажите, какие атрибуты из контейнера вы хотите отобразить:

  • Чтобы показать все атрибуты, указанные в фетч-плане, добавьте элемент columns с атрибутом includeAll, установленным в true. Чтобы исключить ненужные атрибуты, перечислите их через запятую в атрибуте exclude.

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

  • Если фетч-план содержит ссылочный атрибут, этот атрибут будет отображаться по имени его экземпляра. Чтобы отобразить конкретный атрибут, добавьте его явно как в фетч-план, так и в элемент column.

Ниже приведён пример создания базового компонента dataGrid:

<data readOnly="true">
    <collection id="usersDc"
                class="com.company.onboarding.entity.User">
        <fetchPlan extends="_base">
            <property name="department">
                <property name="name"/> (1)
            </property>
        </fetchPlan>
        <loader id="usersDl">
            <query>
                <![CDATA[select e from User e order by e.username]]>
            </query>
        </loader>
    </collection>
</data>
<layout>
    <dataGrid id="usersTable"
              width="100%"
              minHeight="20em"
              dataContainer="usersDc">
        <columns>
            <column property="username"/> (2)
            <column property="firstName"/>
            <column property="lastName"/>
            <column property="active"/>
            <column property="onboardingStatus"/>
            <column property="department.name"/> (3)
        </columns>
    </dataGrid>
</layout>
1 Ссылочный атрибут.
2 Столбец для отображения атрибута из корневой сущности.
3 Столбец для отображения ссылочного атрибута, к которому обращаются с помощью точечной нотации.
data grid basics

Привязка данных

Привязка данных - это связывание визуального компонента с контейнером данных. Компонент dataGrid может отображать данные, загруженные как в контейнер коллекции, так и в контейнер KeyValue.

Контейнер коллекции

Обычно dataGrid связывается с данными декларативно в XML-дескрипторе с помощью атрибута dataContainer. Этот атрибут должен ссылаться на контейнер коллекции, который содержит данные для отображения. Соответствующий пример приведён в предыдущем разделе.

Контейнер Key-Value

Также возможно привязать dataGrid к контейнеру Key-Value. Это позволяет отображать результаты запроса, возвращающего скалярные значения и/или агрегаты. Например:

<data readOnly="true">
    <keyValueCollection id="statusesDc">
        <loader id="statusesLoader">
            <query>
                <![CDATA[select o.department, o.onboardingStatus,
                count(o.onboardingStatus) from
                User o group by o.department, o.onboardingStatus]]>
            </query>
        </loader>
        <properties>
            <property name="department" datatype="string"/>
            <property name="onboardingStatus" datatype="int"/>
            <property name="count" datatype="int"/>
        </properties>
    </keyValueCollection>
</data>
<layout>
    <dataGrid width="100%" dataContainer="statusesDc">
        <columns>
            <column property="department"/>
            <column property="onboardingStatus"/>
            <column property="count"/>
        </columns>
    </dataGrid>
</layout>

Программная привязка

Если необходимо задать контейнер данных программно, используйте атрибут metaClass вместо dataContainer:

<dataGrid width="100%" id="dataGrid" metaClass="User">
    <columns>
        <column property="firstName"/>
        <column property="lastName"/>
        <column property="username"/>
        <column property="joiningDate"/>
        <column property="onboardingStatus"/>
    </columns>
</dataGrid>

После этого в контроллере экрана используйте класс ContainerDataGridItems для привязки dataGrid к контейнеру данных:

@ViewComponent
private DataGrid<User> dataGrid;

@ViewComponent
private CollectionContainer<User> usersDc;

@Subscribe
public void onInit(InitEvent event) {
    dataGrid.setItems(new ContainerDataGridItems<>(usersDc));
}

Встроенное редактирование

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

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

  • В экране деталей сущности объединяются в DataContext. Изменения сохраняются автоматически при нажатии OK, и происходит вызов метода DataContext.save().

  • В экране списка, где DataContext не используется, сохранение изменений выполняется с помощью DataManager, как описано ниже.

Колонка действий

Используйте editorActionsColumn для добавления кнопок Edit и Close рядом с каждой строкой. Они позволяют пользователям начать и завершить редактирование соответственно.

Колонка действий по умолчанию добавляется рядом с другими колонками. Если указано includeAll="true" - автоматически размещается крайней справа.
data grid editing
Показать код
<dataGrid width="100%" dataContainer="usersDc" id="editableUsersTable">
    <columns>
        <column property="username"/>
        <column property="firstName" editable="true"/> (1)
        <column property="lastName" editable="true"/>
        <column property="active" editable="true"/>
        <column property="onboardingStatus"/>
        <editorActionsColumn>
            <editButton text="Edit" icon="PENCIL"/> (2)
            <closeButton text="Close" icon="CLOSE"/> (3)
        </editorActionsColumn>
    </columns>
</dataGrid>
1 Сделайте нужные столбцы редактируемыми.
2 Кнопка для начала редактирования.
3 Кнопка для завершения редактирования.

Буферизованный режим

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

data grid buffered editor
Показать код
<dataGrid width="100%" dataContainer="usersDc" editorBuffered="true"> (1)
    <columns>
        <column property="username"/>
        <column property="firstName" editable="true"/> (2)
        <column property="lastName" editable="true"/>
        <column property="active" editable="true"/>
        <column property="onboardingStatus"/>
        <editorActionsColumn>
            <editButton text="Edit" icon="PENCIL"/> (3)
            <saveButton icon="CHECK" themeNames="success"/> (4)
            <cancelButton icon="CLOSE" themeNames="error"/> (5)
        </editorActionsColumn>
    </columns>
</dataGrid>
1 Включить буферизованный режим.
2 Сделать соответствующие столбцы редактируемыми.
3 Кнопка для начала редактирования.
4 Кнопка для подтверждения изменений.
5 Кнопка для отмены изменений.

Редактирование по двойному клику

Иногда удобнее начинать встроенное редактирование, дважды щёлкнув по элементу.

data grid double click editing
Показать код
<dataGrid width="100%" dataContainer="usersDc" id="dblClickTable">
    <columns>
        <column property="username"/>
        <column property="firstName" editable="true"/>
        <column property="lastName" editable="true"/>
        <column property="active" editable="true"/>
        <column property="onboardingStatus"/>
    </columns>
</dataGrid>
@ViewComponent
private DataGrid<User> dblClickTable;

@ViewComponent
private GridMenuItem<Object> emailItem;


@Subscribe
public void onInit(InitEvent event) {
    DataGridEditor<User> tableEditor = dblClickTable.getEditor();
    dblClickTable.addItemDoubleClickListener(e -> {
        tableEditor.editItem(e.getItem());
        Component editorComponent = e.getColumn().getEditorComponent();
        if (editorComponent instanceof Focusable) {
            ((Focusable) editorComponent).focus();
        }
    });
}

Автосохранение изменений

В стандартном экране списка загруженные сущности не объединяются в DataContext (если в XML-элементе загрузчика установлен атрибут readOnly="true"). Кроме того, в экранах списка обычно отсутствуют кнопки подтверждения, такие как OK или Save. Поэтому изменённые сущности необходимо явно сохранять в базе данных.

В небуферизованном режиме это можно сделать с помощью DataManager в обработчике события EditorCloseEvent следующим образом:

@ViewComponent
private CollectionContainer<User> usersDc;

@Install(to = "usersDataGrid.@editor", subject = "closeListener")
private void usersDataGridEditorCloseListener(final EditorCloseEvent<User> event) {
    User user = event.getItem();
    User savedUser = dataManager.save(user); (1)
    usersDc.replaceItem(savedUser); (2)
}
1 Сохранить сущность и получить сохранённый экземпляр.
2 Заменить текущий экземпляр сохранённым в контейнере данных. Все последующие изменения будут применяться к актуальной версии сущности.

В буферизованном режиме аналогичные действия выполняются в обработчике события EditorSaveEvent:

@Install(to = "usersDataGrid.@editor", subject = "saveListener")
private void usersDataGridEditorSaveListener(final EditorSaveEvent<User> event) {
    User user = event.getItem();
    User savedUser = dataManager.save(user);
    usersDc.replaceItem(savedUser);
}

Для генерации обработчиков в виде аннотированных методов используйте вкладку Handlers в Component Inspector Jmix студии, где обработчики событий редактора помечены префиксами [Editor].

Обработчики также можно добавить программно с помощью методов dataGrid.getEditor().addSaveListener() и аналогичных.

DataGridEditor

Интерфейс io.jmix.flowui.component.grid.editor.DataGridEditor предоставляет дополнительную функциональность редактора: настройку, открытие, сохранение и отмену редактирования строки, регистрацию обработчиков событий, а также служебные методы для определения компонентов редактирования столбцов.

Для поддержки механизмов фреймворка, таких как контейнеры данных, источники значений и т.д., компонент редактирования столбца должен добавляться с использованием методов DataGridEditor (например, DataGridEditor#setColumnEditorComponent()), а не через прямое API столбца Column#setEditorComponent().

Пример использования:

@Autowired
private UiComponents uiComponents;

@ViewComponent
private DataGrid<User> editableUserTable;

@Subscribe
public void onInit(InitEvent event) {
    DataGridEditor<User> editor = editableUserTable.getEditor(); (1)

    editor.setColumnEditorComponent("timeZoneId", generationContext -> {
        //noinspection unchecked
        JmixComboBox<String> timeZoneField = uiComponents.create(JmixComboBox.class); (2)
        timeZoneField.setItems(List.of(TimeZone.getAvailableIDs()));
        timeZoneField.setValueSource(generationContext.getValueSourceProvider().getValueSource("timeZoneId"));
        timeZoneField.setWidthFull();
        timeZoneField.setClearButtonVisible(true);
        timeZoneField.setRequired(true);
        //noinspection unchecked,rawtypes
        timeZoneField.setStatusChangeHandler(((Consumer) generationContext.getStatusHandler())); (3)

        return timeZoneField; (4)
    });
}
1 Получить экземпляр DataGridEditor.
2 Экземпляр компонента JmixComboBox создается с помощью фабрики UiComponents.
3 Установить StatusChangeHandler.
4 Метод setColumnEditorComponent() возвращает визуальный компонент, который будет отображаться как редактор столбца.

SupportsStatusChangeHandler

По умолчанию компоненты ввода (например, textField, comboBox) отображают сообщения об ошибках в тексте над собой. Такое поведение имеет недостатки при ограниченной области ячейки редактирования. Интерфейс io.jmix.flowui.component.SupportsStatusChangeHandler позволяет определить альтернативный способ отображения сообщений об ошибках. Компоненты, реализующие этот интерфейс, поддерживают делегирование обработки ошибок.

По умолчанию встроенный редактор использует StatusChangeHandler для установки сообщения об ошибке компонента в качестве его title.

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

Метод

Описание

appendHeaderRow()

Добавляет новую строку в нижнюю часть секции заголовка.

prependHeaderRow()

Добавляет новую строку в верхнюю часть секции заголовка.

appendFooterRow()

Добавляет новую строку в нижнюю часть секции подвала.

prependFooterRow()

Добавляет новую строку в верхнюю часть секции подвала.

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

data grid header footer
Показать код
<dataGrid id="dataGrid"
          width="100%"
          dataContainer="usersDc"
          themeNames="column-borders">
    <columns>
        <column property="firstName"/>
        <column property="lastName"/>
        <column property="active"/>
    </columns>
</dataGrid>
@ViewComponent
private DataGrid<User> dataGrid;
@ViewComponent
private CollectionContainer<User> usersDc;
@ViewComponent
private CollectionLoader<User> usersDl;
@Autowired
private DataManager dataManager;
@Autowired
private UiComponents uiComponents;
@Autowired
private Notifications notifications;
@ViewComponent
private DataContext dataContext;

@Subscribe
protected void onInit(final InitEvent event) {
    usersDl.load();
    initHeader();
    initFooter();
}

protected void initHeader() {
    HeaderRow headerRow = dataGrid.prependHeaderRow();
    HeaderRow.HeaderCell headerCell = headerRow.join(
            dataGrid.getColumnByKey("firstName"),
            dataGrid.getColumnByKey("lastName")); (1)
    headerCell.setText("Full Name");
}

protected void initFooter() {
    FooterRow footerRow = dataGrid.appendFooterRow();
    FooterRow.FooterCell activeCell = footerRow.getCell(dataGrid.getColumnByKey("active"));
    activeCell.setText(getActiveCount() + "/" + usersDc.getItems().size());
}

protected int getActiveCount() {
    int activeCount = 0;
    Collection<User> items = dataGrid.getGenericDataView().getItems().toList();
    for (User user : items) {
        if (user.getActive()) {
            activeCount++;
        }
    }
    return activeCount;
}
1 Ячейки в заголовке могут быть объединены. Это позволяет представить общую информацию о связанных столбцах.
Как в заголовке, так и в подвале можно размещать сложное содержимое и компоненты. Смотрите интерактивный пример.

Фильтрация по заголовкам столбцов

Это экспериментальная функция. Внешний вид и детали реализации могут существенно измениться в следующем релизе Jmix.

Данные в dataGrid можно фильтровать с помощью встроенных в заголовки столбцов фильтров свойств.

Вы можете указать, для каких столбцов должен быть доступен фильтр, используя XML-атрибут filterable. В фильтруемых столбцах в заголовках отображается иконка фильтра (funnel). При клике на эту иконку появляется диалог с условием фильтрации по свойству. Если условие задано, иконка в соответствующем столбце подсвечивается.

Чтобы иконка фильтра всегда была видна, задайте столбцу подходящую ширину с помощью атрибутов width или autoWidth. Не делайте столбец изменяемым по ширине, иначе пользователь сможет уменьшить его ширину и иконка фильтра станет невидимой.

Например:

<dataGrid dataContainer="usersDc"
          width="100%">
    <columns>
        <column property="firstName" filterable="true"
                width="15em"/>
        <column property="lastName" filterable="true"
                autoWidth="true"/>
        <column property="username"/>
        <column property="active"/>
        <column property="joiningDate"/>
        <column property="onboardingStatus"/>
    </columns>
</dataGrid>

Фильтры свойств в заголовках столбцов работают так же, как отдельные фильтры по свойствам и как genericFilter - они добавляют условия к загрузчику данных. В стандартном режиме эти условия преобразуются в JPQL-запрос и фильтруют данные на уровне базы данных.

Фильтруемые столбцы можно использовать совместно с компонентами propertyFilter и genericFilter. Условия всех фильтров объединяются логическим оператором AND.

В настоящее время условия фильтрации в столбцах не сохраняются в URL страницы. Это означает, что если пользователь применит фильтр, перейдёт в экран деталей и затем вернётся обратно, фильтр будет сброшен.

Действия

Компонент dataGrid реализует интерфейс HasActions и может содержать как стандартные действия списка, так и пользовательские. Выполнение действий происходит при нажатии соответствующих кнопок или через контекстное меню, которое вызывается по правому клику.

data grid actions

Чтобы добавить action в Jmix Studio, выберите компонент в XML-дескрипторе экрана или в панели структуры Jmix UI, затем нажмите кнопку Add→Action в панели инспектора Jmix UI.

Показать код
<hbox id="buttonsPanel" classNames="buttons-panel"> (1)
    <button id="createBtn" action="usersDataGrid.create"/>
    <button id="editBtn" action="usersDataGrid.edit"/>
    <button id="removeBtn" action="usersDataGrid.remove"/>
    <button id="infoBtn" action="usersDataGrid.getInfo"/>
</hbox>
<dataGrid width="100%" dataContainer="usersDc" id="usersDataGrid">
    <columns>
        <column property="username"/>
        <column property="firstName"/>
        <column property="lastName"/>
        <column property="active"/>
        <column property="onboardingStatus"/>
        <column property="joiningDate"/>
    </columns>
    <actions>
        <action id="create" type="list_create"/> (2)
        <action id="edit" type="list_edit"/>
        <action id="remove" type="list_remove"/>
        <action id="getInfo" text="Get Info"/> (3)
    </actions>
</dataGrid>
1 Определите hbox для размещения кнопок действий.
2 Определите стандартное действие list_create.
3 Определите пользовательское действие getInfo. Значение его атрибута text используется как название действия в контекстном меню и как подпись кнопки.

Контекстное меню

Контекстное меню предоставляет альтернативный способ доступа к действиям через правую кнопку мыши. Каждое действие представлено отдельным пунктом меню.

Элемент contextMenu позволяет настроить список пунктов, организовав их с помощью разделителей и иерархической структуры.

data grid context menu
Показать код
<dataGrid width="100%" dataContainer="usersDc" id="menuDataGrid">
    <columns>
        <column property="username"/>
        <column property="firstName"/>
        <column property="lastName"/>
        <column property="active"/>
        <column property="onboardingStatus"/>
        <column property="joiningDate"/>
    </columns>
    <actions showInContextMenuEnabled="false"> (1)
        <action id="create" type="list_create"/>
        <action id="edit" type="list_edit"/>
        <action id="remove" type="list_remove"/>
        <action id="getInfo" text="View"/>
        <action id="sendMessage" text="Message"/>
    </actions>
    <contextMenu id="contextMenu">
        <item action="menuDataGrid.getInfo" icon="INFO_CIRCLE"/> (2)
        <separator/>
        <item text="Contact" icon="USER"> (3)
            <item action="menuDataGrid.sendMessage" icon="COMMENT"/>
            <item id="emailItem"/> (4)
        </item>
        <separator/>
        <item action="menuDataGrid.remove" icon="TRASH"/>
    </contextMenu>
</dataGrid>
1 Скрыть все действия из меню или скрыть конкретное действие, установив для него атрибут visible.
2 Явно добавить пункты меню для необходимых действий.
3 Вложить пункты друг в друга для создания иерархии.
4 Пункт с содержимым, добавленным динамически через dynamicContentHandler.
@ViewComponent
private GridMenuItem<Object> emailItem;

@Install(to = "contextMenu", subject = "dynamicContentHandler")
private boolean contextMenuDynamicContentHandler(final User user) {
    if (user == null) {
        return false;
    }
        emailItem.setText("Email: " + user.getEmail());
    return true;
}

Рендереры

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

  • Форматировать даты или числа.

  • Отображать пользовательские иконки или изображения.

  • Создавать интерактивные элементы, такие как кнопки или ссылки.

  • Показывать разное содержимое в зависимости от значения ячейки.

Рендереры можно определить следующими способами:

  • Декларативно в XML

    Существует несколько встроенных рендереров, которые можно назначить столбцу в XML:

    • numberRenderer

    • localDateRenderer

    • localDateTimeRenderer

      Они принимают строку format. Например:

      <column property="joiningDate">
          <localDateRenderer format="MMM dd, yyyy"/>
      </column>

      Для добавления рендерера в Jmix Studio выберите элемент column XML-дескрипторе экрана или в панели структуры Jmix UI, затем нажмите кнопку Add→<Some>Renderer в панели инспектора Jmix UI.

  • Используя аннотацию @Supply

    Рендереры могут быть предоставлены в контроллере экрана с помощью аннотации @Supply. Методы, помеченные этой аннотацией, возвращают значение, которое передаётся в качестве входного параметра методу, указанному в свойстве subject.

    Пользовательский рендерер может быть назначен столбцу с помощью обработчика renderer, доступного на вкладке Handlers в инспекторе Jmix UI для любого элемента column.

    Элемент column может быть определён без атрибута property, то есть без прямой привязки к атрибуту сущности. В этом случае элемент column должен иметь атрибут key с уникальным значением.

    Примеры использования можно посмотреть здесь и здесь.

  • Используя FragmentRenderer

    Колонки можно отображать с помощью вложенного элемента fragmentRenderer. Подробнее смотрите в разделе Рендерер фрагментов.

  • Используя addColumn() и addComponentColumn()

    Методы addColumn() и addComponentColumn() позволяют добавлять столбцы в таблицу.

    Добавленным столбцам можно настроить использование рендерера для отображения данных.

    В следующем примере мы добавим столбец, показывающий фотографию пользователя:

    @ViewComponent
    private DataGrid<User> usersDtGr;
    
    @Autowired
    private FileStorage fileStorage;
    
    @Subscribe
    public void onInit(InitEvent event) {
        Grid.Column<User> pictureColumn = usersDtGr.addComponentColumn(user -> { (1)
            FileRef fileRef = user.getPicture();
            if (fileRef != null) {
                Image image = uiComponents.create(Image.class); (2)
                image.setWidth("30px");
                image.setHeight("30px");
                image.setClassName("user-picture");
    
                StreamResource streamResource = new StreamResource(
                        fileRef.getFileName(),
                        () -> fileStorage.openStream(fileRef));
                image.setSrc(streamResource); (3)
    
                return image; (4)
            } else {
                return new Span();
            }
        })
                .setHeader("Picture")
                .setAutoWidth(true)
                .setFlexGrow(0);
        usersDtGr.setColumnPosition(pictureColumn,0);
    }
    1 Добавляет новый столбец, в котором отображается компонент image.
    2 Экземпляр компонента image создаётся с помощью фабрики UiComponents.
    3 Изображение получает содержимое из переданного StreamResource, ссылка на который хранится в атрибуте picture сущности User.
    4 Метод addComponentColumn() возвращает визуальный компонент, который будет отображаться в ячейках столбца.

Рендерер LocalDate

LocalDateRenderer отвечает за отображение дат в формате значений типа LocalDate.

В элементе column таблицы присутствует вложенный элемент localDateRenderer, который содержит необязательный атрибут nullRepresentation и обязательный атрибут format для строки формата.

<column property="joiningDate">
    <localDateRenderer format="MMM dd, yyyy"/>
</column>

Атрибут nullRepresentation используется для задания текстового отображения значений null.

Ниже приведён пример использования LocalDateRenderer с методом addColumn():

@ViewComponent
private DataGrid<User> usersDtGr;

@Subscribe
public void onInit(InitEvent event) {
    usersDtGr.addColumn(new LocalDateRenderer<>(
                    User::getJoiningDate,
                    () -> DateTimeFormatter.ofLocalizedDate(
                            FormatStyle.MEDIUM)))
            .setHeader("Joining date");
}

Рендерер LocalDateTime

LocalDateTimeRenderer отвечает за отображение дат в формате значений типа LocalDateTime.

В элементе column таблицы присутствует вложенный элемент localDateRenderer, который содержит необязательный атрибут nullRepresentation и обязательный атрибут format, задающий строку формата.

<column property="passwordExpiration">
    <localDateTimeRenderer format="dd/MM/YYYY HH:mm:ss"/>
</column>

Атрибут nullRepresentation используется для задания текстового отображения значений null.

NumberRenderer

NumberRenderer позволяет отображать числа в заданном формате в ячейках таблицы. Он особенно полезен для отображения различных типов чисел, включая числа с плавающей запятой.

В элементе column таблицы присутствует вложенный элемент numberRenderer, который содержит необязательный атрибут nullRepresentation и обязательный атрибут format или numberFormat.

<column property="factor">
    <numberRenderer numberFormat="#,#00.0000"/>
</column>

Атрибут nullRepresentation используется для задания текстового отображения значений null.

Атрибут numberFormat использует правила и синтаксис форматирования класса java.text.DecimalFormat.

Для numberRenderer требуется указать только один атрибут формата. Если ни один из атрибутов не задан, будет выброшено исключение GuiDevelopmentException. Кроме того, нельзя одновременно задавать формат с помощью двух атрибутов - это также приведёт к выбросу исключения GuiDevelopmentException.

TextRenderer

TextRenderer - это рендерер для отображения простых строковых значений в виде обычного текста.

В приведённом ниже примере в колонке отображается пользовательский текст:

<column key="status" header="Status"/>
@Supply(to = "userStepsDataGrid.status", subject = "renderer")
private Renderer<UserStep> userStepsDataGridStatusRenderer() {
    return new TextRenderer<>(userStep ->
            isOverdue(userStep) ? "Overdue!" : "");
}

ComponentRenderer

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

<column key="completed" width="4em" flexGrow="0"/>
@Supply(to = "userStepsDataGrid.completed", subject = "renderer")
private Renderer<UserStep> userStepsDataGridCompletedRenderer() {
    return new ComponentRenderer<>(userStep -> {
        JmixCheckbox checkbox = uiComponents.create(JmixCheckbox.class);
        checkbox.setValue(userStep.getCompletedDate() != null);
        checkbox.addValueChangeListener(e -> {
            // ...
        });
        return checkbox;
    });
}

Детализация элементов

Компонент ItemDetails позволяет разворачивать строки для отображения дополнительной информации об элементе. Для реализации данной функциональности используйте метод setItemDetailsRenderer().

Метод принимает в качестве аргумента объект ComponentRenderer, который определяет способ отображения детализированной информации.

data grid items details

Атрибут detailsVisibleOnClick позволяет управлять отображением дополнительных сведений при клике на строку. Если необходимо, чтобы детализация появлялась по другому событию (не по клику), установите значение этого атрибута в false.

Сортировка

Любой столбец таблицы может использоваться для сортировки отображаемых данных. При нажатии на заголовок столбца в таблице активируется специальный индикатор, который показывает текущий столбец сортировки и направление сортировки (по возрастанию или убыванию).

Последующие нажатия на тот же заголовок будут переключать направление сортировки на противоположное.

Сортировка по нескольким столбцам

Данные можно сортировать одновременно по нескольким столбцам. В этом случае первый выбранный столбец определяет основной критерий сортировки, второй столбец задаёт дополнительный критерий, а последующие столбцы устанавливают второстепенные критерии сортировки.

Настройка осуществляется с помощью атрибутов multiSort, multiSortOnShiftClickOnly и multiSortPriority.

Пример:

<dataGrid width="100%"
          dataContainer="usersDc"
          multiSort="true"
          multiSortOnShiftClickOnly="true"
          multiSortPriority="APPEND">

Агрегация данных

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

Например:

<dataGrid width="100%"
          dataContainer="stepsDc"
          aggregatable="true"
          aggregationPosition="TOP"> (1)
    <columns>
        <column property="name"/>
        <column property="duration">
            <aggregation type="AVG"
                         cellTitle="msg://aggregation.cellTitle"/> (2)
        </column>
        <column property="sortValue"/>
        <column property="factor">
            <aggregation type="AVG">
                <formatter>
                    <number format="#,##0.00"/> (3)
                </formatter>
            </aggregation>
        </column>
    </columns>
</dataGrid>
1 Установите атрибут aggregatable в значение true.
2 Для каждого столбца, который нужно агрегировать, добавьте элемент aggregation. При необходимости укажите атрибут cellTitle для отображения подсказки при наведении на агрегированное значение.
3 Определите formatter для отображения значения в нестандартном формате.

Элемент aggregation позволяет задать пользовательскую логику вычисления агрегированных значений с помощью атрибута strategyClass:

<dataGrid width="100%"
          dataContainer="usersDc"
          aggregatable="true"
          aggregationPosition="TOP">
    <columns>
        <column property="firstName"/>
        <column property="lastName"/>
        <column property="onboardingStatus">
            <aggregation
                    strategyClass="com.company.onboarding.view.component.datagrid.DataGridUserStatusAggregation"/>
        </column>
    </columns>
</dataGrid>

Такой класс должен реализовывать интерфейс AggregationStrategy. Например:

public class DataGridUserStatusAggregation implements AggregationStrategy<OnboardingStatus, String> {

    @Autowired
    public Messages messages;

    @Override
    public String aggregate(Collection<OnboardingStatus> propertyValues) {
        OnboardingStatus mostFrequent = null;
        long max = 0;

        if (CollectionUtils.isNotEmpty(propertyValues)) {
            for (OnboardingStatus status : OnboardingStatus.values()) {
                long current = propertyValues.stream()
                        .filter(status :: equals)
                        .count();

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

        if (mostFrequent != null) {
            String key = OnboardingStatus.class.getSimpleName() + "." + mostFrequent.name();
            return String.format("%s: %d/%d", messages.getMessage(OnboardingStatus.class, key), max, propertyValues.size());
        }

        return null;
    }

    @Override
    public Class<String> getResultClass() {
        return String.class;
    }
}

Отображение при отсутствии данных

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

  • Используйте атрибут emptyStateText для определения текста, который будет отображаться при пустой таблице.

    <dataGrid id="dataGridEmptyState"
              dataContainer="customersDc"
              width="100%"
              emptyStateText="No customers found">
        <columns>
            <column property="firstName"/>
            <column property="lastName"/>
            <column property="age"/>
            <column property="martialStatus"/>
        </columns>
    </dataGrid>
    Данный атрибут имеет приоритет над компонентом пустого состояния, настроенным через метод setEmptyStateComponent(Component) в контроллере экрана и над элементом emptyStateComponent в XML-дескрипторе. Установка значения emptyStateText приведёт к удалению любого ранее заданного компонента.
  • Для отображения компонента при отсутствии данных используйте элемент emptyStateComponent.

    <dataGrid id="dataGridEmptyStateComp"
              dataContainer="customersDc"
              width="100%">
        <columns>
            <column property="firstName"/>
            <column property="lastName"/>
            <column property="age"/>
            <column property="martialStatus"/>
        </columns>
        <emptyStateComponent>
            <button id="newCustomerBtn"
                    text="Create customer" width="100%"/>
        </emptyStateComponent>
    </dataGrid>

    После настройки emptyStateComponent вы можете работать с вложенным компонентом как обычно, включая настройку его стилей и создание обработчиков событий.

    Установка этого компонента отменяет:

    • любой текст пустого состояния, заданный методом setEmptyStateText(String) в контроллере экрана;

    • атрибут emptyStateText в XML-дескрипторе экрана.

Атрибуты

В Jmix существует множество общих атрибутов, которые выполняют одинаковые функции для всех компонентов. Ниже перечислены атрибуты, специфичные для dataGrid:

Название

Описание

Значение по-умолчанию

allRowsVisible

Если значение равно true, таблица будет отображать все строки одновременно без полос прокрутки, фактически отключая виртуальную прокрутку. Это означает, что вместо рендеринга только видимых строк и подгрузки дополнительных при прокрутке пользователем, таблица будет отображать все строки в DOM одновременно.

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

false

aggregatable

Если значение true, активирует агрегацию столбцов. Смотрите раздел Агрегация данных.

false

aggregationPosition

Определяет положение строки с агрегированными данными. Допустимые значения: TOP (вверху) или BOTTOM (внизу). Подробнее смотрите раздел Агрегация данных.

BOTTOM

columnReorderingAllowed

Если значение true, позволяет пользователям изменять порядок столбцов.

false

detailsVisibleOnClick

Если значение true, позволяет раскрывать детализацию элементов по клику мыши.

true

dropMode

Определяет, в каких строках возможно выполнение операции перетаскивания (drop). Допустимые значения: BETWEEN, ON_TOP, ON_TOP_OR_BETWEEN, ON_GRID. Данная функция может использоваться, например, для изменения порядка строк и для перемещения строк в рамках таблицы.

editorBuffered

При значении true активируется буферизированный режим редактирования, который означает что:

- пользователь должен подтверждать изменения нажатием кнопки подтверждения;

- предусмотрена возможность отмены внесённых изменений.

В небуферизированном режиме (unbuffered mode) изменения применяются сразу без необходимости подтверждения.

false

emptyStateText

Определяет текст, отображаемый когда таблица данных пуста. Используйте null для удаления текущего содержимого пустого состояния. Подробнее смотрите раздел Отображение при отсутствии данных.

multiSort

При значении true активируется возможность сортировки данных по нескольким столбцам одновременно.

false

multiSortOnShiftClickOnly

При значении true множественная сортировка активируется только при одновременном нажатии и удержании клавиши Shift и клике на заголовке.

false

multiSortPriority

Определяет, добавляется ли выбранный столбец в начало или конец списка сортировки. Допустимые значения: PREPEND, APPEND.

PREPEND

nestedNullBehavior

Определяет поведение при обработке вложенных свойств, которые могут содержать значения null в цепочке свойств. Допустимые значения: THROW, ALLOW_NULLS.

THROW

pageSize

Определяет размер страницы (количество элементов, загружаемых из источника данных за один раз).

50

rowDraggable

Если значение true, пользователи могут перетаскивать строки в таблице.

false

selectionMode

Устанавливает режим выделения элементов. Допустимые значения:

- SINGLE - одиночное выделение

- MULTI - множественное выделение

SINGLE

Обработчики

В Jmix существует множество общих обработчиков, которые настраиваются одинаково для всех компонентов. Ниже представлены обработчики, специфичные для dataGrid:

Чтобы сгенерировать заглушку обработчика в Jmix Studio, используйте вкладку Handlers панели инспектора Jmix UI, или команду Generate Handler, доступную на верхней панели контроллера экрана и через меню CodeGenerate (Alt+Insert / Cmd+N).

Название

Описание

CellFocusEvent

com.vaadin.flow.component.grid.CellFocusEvent - событие, которое возникает при фокусировке ячейки в таблице. Соответствует событию DOM grid-cell-focus.

ColumnReorderEvent

com.vaadin.flow.component.grid.ColumnReorderEvent - событие, которое возникает при изменении порядка столбцов в таблице. Соответствует событию DOM column-reorder-all-columns.

ColumnResizeEvent

com.vaadin.flow.component.grid.ColumnResizeEvent - событие, которое возникает при изменении размера столбца в таблице данных пользователем. Соответствует событию DOM column-drag-resize.

GridDragEndEvent

com.vaadin.flow.component.grid.dnd.GridDragEndEvent - событие окончания перетаскивания строк в таблице данных. Соответствует событию DOM grid-dragend.

GridDragStartEvent

com.vaadin.flow.component.grid.dnd.GridDragStartEvent - событие начала перетаскивания строк в таблице данных. Соответствует событию DOM grid-dragstart.

GridDropEvent

com.vaadin.flow.component.grid.dnd.GridDropEvent - представляет событие перетаскивания (drop), которое возникает на самой таблице данных или на её строках. Соответствует событию DOM grid-drop.

ItemClickEvent

com.vaadin.flow.component.grid.ItemClickEvent - событие, которое возникает при клике на элемент таблицы данных. Соответствует событию DOM item-click.

ItemDoubleClickEvent

com.vaadin.flow.component.grid.ItemDoubleClickEvent - событие, которое возникает при двойном клике на элемент таблицы данных. Соответствует событию DOM item-double-click.

SortEvent

com.vaadin.flow.data.event.SortEvent - событие, описывающее изменение сортировки в DataProvider. Генерируется объектами, реализующими интерфейс SortNotifier.

dataGenerator

Добавляет генератор данных для таблицы. Если генератор уже был добавлен, действие не выполняется. Подробнее см. в интерфейсе com.vaadin.flow.data.provider.HasDataGenerators.

dragFilter

Позволяет задавать, какие именно строки можно перетаскивать, вместо активации этой функции для всех строк через атрибут rowDraggable.

dropFilter

Позволяет задавать, какие именно строки могут быть целями для операции перетаскивания (drop targets).

enterPressHandler

Обрабатывает событие нажатия пользователем клавиши Enter.

tooltipGenerator

Функция создает всплывающие подсказки (tooltip) для ячеек столбца на основе заданных условий. Смотрите рабочий пример в живой демонстрации.

Элементы

Элементы dataGrid предоставляют широкий набор возможностей для управления внешним видом, поведением и функциональностью столбцов как в целом, так и по отдельности.

Чтобы добавить элемент в выбранный компонент, нажмите кнопку Add в панели инспектора Jmix UI.

columns

Элемент columns позволяет задать набор атрибутов для отображения и поведения для всех столбцов.

XML-элемент

columns

Атрибуты

exclude - includeAll - resizable - sortable

Элементы

column - EditorActionsColumn

Table 1. Атрибуты

Название

Описание

Значение по-умолчанию

exclude

Исключает отображение определённых атрибутов. Несколько атрибутов следует перечислять через запятую. Например: exclude = "id, version, sortValue".

includeAll

Если значение равно true, включаются все атрибуты, указанные в фетч-плане соответствующего контейнера данных.

resizable

При значении true пользователь может изменять ширину всех столбцов таблицы.

false

sortable

Если значение равно true, все столбцы поддерживают сортировку пользователем.

true

column

Элемент column определяет отдельный столбец. Атрибуты, заданные для отдельного столбца, переопределяют атрибуты, установленные для всех столбцов.

XML-элемент

column

Атрибуты

autowidth - editable - filterable - flexGrow - footer - frozen - header - key - property - resizable - sortable - textAlign - visible - width

Обработчики

AttachEvent - DataGridColumnVisibilityChangedEvent - DetachEvent - partNameGenerator - renderer - tooltipGenerator

Элементы

Aggregation - FragmentRenderer - LocalDateRenderer - LocalDateTimeRenderer - NumberRenderer

Table 2. Атрибуты

Название

Описание

Значение по-умолчанию

autoWidth

Если значение равно true, ширина столбца будет автоматически подстраиваться под его содержимое.

false

editable

Если значение равно true, столбец можно редактировать. Подробнее смотрите раздел Встроенное редактирование.

false

filterable

Если значение равно true, для этого столбца включается фильтрация. Подробнее смотрите раздел Фильтрация по заголовкам столбцов.

false

flexGrow

Задаёт коэффициент расширения (flex grow) для данного столбца. При значении 0 ширина столбца фиксирована.

0

footer

Задаёт текст в нижней части столбца (футере). Значением атрибута может быть либо сам текст, либо ключ из пакета сообщений. В случае использования ключа значение должно начинаться с префикса msg://.

frozen

Если значение равно true, столбец фиксируется (закрепляется на месте) и остаётся видимым при горизонтальной прокрутке таблицы. Рекомендуется фиксировать столбцы слева направо.

false

header

Задаёт текст заголовка столбца. Значением атрибута может быть либо сам текст, либо ключ из пакета сообщений. В случае использования ключа значение должно начинаться с префикса msg://.

key

Задаёт пользовательский идентификатор для привязки к этому столбцу. Этот ключ можно использовать для последующего получения столбца с помощью метода getColumnByKey(String).

Ключ должен быть уникальным в пределах dataGrid и не может быть изменён после установки.

property

Указывает имя атрибута сущности, который будет отображаться в столбце. Это может быть атрибут корневой сущности, например, property = "user", или атрибут дочерней сущности, например, property = "user.department.name" (используйте точечную нотацию для перехода по объектному графу).

resizable

Если значение равно true, пользователь может изменять ширину столбца.

false

sortable

Если значение равно true, столбец поддерживает сортировку.

false

textAlign

Задает выравнивание текста с возможными значениями: START, CENTER, END.

START

visible

Если значение true, столбец видимый.

true

width

Задает ширину столбца в виде CSS-строки.

Table 3. Обработчики

Название

Описание

DataGridColumnVisibilityChangedEvent

Срабатывает при изменении видимости столбца через компонент gridColumnVisibility.

partNameGenerator

Генерирует части CSS-классов для этого столбца на основе заданных условий. Это позволяет настраивать внешний вид ячеек в зависимости от отображаемых данных. Смотрите живую демонстрацию.

renderer

Отображает содержимое столбца с использованием текста или компонентов. Смотрите TextRenderer и ComponentRenderer.

tooltipGenerator

Генерирует всплывающую подсказку для ячейки столбца на основе заданных условий. Смотрите живую демонстрацию.

contextMenu

Элемент contextMenu организует пункты контекстного меню (вызываемого правой кнопкой мыши) в порядке, отличном от стандартного. Смотрите пример.

XML-элемент

contextMenu

Атрибуты

id - classNames - css - enabled - visible

Обработчики

AttachEvent - DetachEvent - GridContextMenuOpenedEvent - openedChangeEvent - dynamicContentHandler

Элементы

item - separator

Table 4. Обработчики

Название

Описание

GridContextMenuOpenedEvent

Срабатывает при изменении состояния открытия контекстного меню. Может возвращать целевой элемент или идентификатор целевого столбца, позволяя меню отображать пункты в зависимости от выбранного элемента.

openedChangeEvent

Срабатывает при изменении состояния открытия контекстного меню.

dynamicContentHandler

Обрабатывает динамические обновления меню при его открытии, такие как добавление пунктов меню или их содержимого. Смотрите пример.

emptyStateComponent

Элемент emptyStateComponent определяет компонент для отображения при пустом состоянии таблицы. Используйте null, чтобы удалить текущее содержимое. Смотрите раздел Отображение при отсутствии данных.

Смотрите также

Смотрите документацию Vaadin для получения дополнительной информации.