dataGrid

dataGrid отображает структурированные данные в строках и колонках и позволяет эффективно просматривать большие наборы данных.

XML-элемент

dataGrid

Java-класс

DataGrid

Обзор

Сетка данных

Основы

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

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

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

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

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

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

XML
<data readOnly="true">
    <collection id="ordersDc"
                class="io.jmix.uisamples.entity.Order">
        <fetchPlan extends="_local">
            <property name="customer" fetchPlan="_local"/>
        </fetchPlan>
        <loader id="ordersLoader">
            <query>
                <![CDATA[select e from uisamples_Order e order by e.date]]>
            </query>
        </loader>
    </collection>
</data>
<facets>
    <dataLoadCoordinator auto="true"/>
</facets>
<layout>
    <checkboxGroup id="dataGridSettingsGroup" label="Settings" themeNames="horizontal"/>
    <dataGrid id="dataGrid" width="100%" minHeight="20em"
              dataContainer="ordersDc">
        <columns>
            <column property="date"/>
            <column property="customer"/>
            <column property="amount"/>
            <column property="description"/>
        </columns>
    </dataGrid>
</layout>
Java
@ViewComponent
protected JmixCheckboxGroup<String> dataGridSettingsGroup;
@ViewComponent
protected DataGrid<Order> dataGrid;

@Subscribe
protected void onInit(InitEvent event) {
    initSettingsGroup();
}

protected void initSettingsGroup() {
    dataGridSettingsGroup.setItems("Sortable", "Resizable", "Column reordering allowed");
    dataGridSettingsGroup.setTypedValue(Collections.singletonList("Sortable"));
}

@Subscribe("dataGridSettingsGroup")
protected void onDataGridSettingsGroupValueChange(
        TypedValueChangeEvent<JmixCheckboxGroup<String>, Collection<String>> event) {
    if (event.getValue() == null) {
        return;
    }

    //clear
    dataGrid.getAllColumns().forEach(col -> {
        col.setSortable(false);
        col.setResizable(false);
    });
    dataGrid.setColumnReorderingAllowed(false);

    event.getValue()
            .forEach(this::applyGridSettings);
}

protected void applyGridSettings(String setting) {
    switch (setting) {
        case "Sortable" -> dataGrid.getAllColumns().forEach(col -> col.setSortable(true));
        case "Resizable" -> dataGrid.getAllColumns().forEach(col -> col.setResizable(true));
        case "Column reordering allowed" -> dataGrid.setColumnReorderingAllowed(true);
    }
}

dataGrid связанный с данными

Укажите dataContainer, чтобы отображать сущности из контейнера коллекции. Для скалярных и агрегатных результатов запроса используйте контейнер ключ-значение. Таблица отслеживает контейнер, поэтому при его перезагрузке или изменении строки обновляются.

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

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

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

Свяжите таблицу с контейнером ключ-значение, если запрос возвращает скалярные значения или агрегаты. Колонки в примере соответствуют свойствам, объявленным в контейнере ключ-значение:

<data>
    <keyValueCollection id="salesDc">
        <loader id="salesLoader">
            <query>
                <![CDATA[select o.customer, sum(o.amount) from uisamples_Order o group by o.customer]]>
            </query>
        </loader>
        <properties>
            <property class="io.jmix.uisamples.entity.Customer"
                      name="customer"/>
            <property datatype="decimal"
                      name="sum"/>
        </properties>
    </keyValueCollection>
</data>
<facets>
    <dataLoadCoordinator auto="true"/>
</facets>
<layout>
    <dataGrid id="salesDataGrid"
              width="100%"
              minHeight="20em"
              dataContainer="salesDc">
        <columns>
            <column property="customer" header="Customer"/>
            <column property="sum" header="Sum"/>
        </columns>
    </dataGrid>
</layout>

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

Для контейнера, созданного в Java, укажите metaClass вместо dataContainer, чтобы колонки в XML могли получить метаданные сущности:

<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 и передайте его в сетку. Оболочка распространяет изменения контейнера и состояние выбора через API поставщика данных сетки:

@ViewComponent
private DataGrid<User> dataGrid;

@ViewComponent
private CollectionContainer<User> usersDc;

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

Программные колонки

addColumn() и addComponentColumn() позволяют создавать колонки в Java вместо XML.

addColumn() удобно использовать вместе с рендерерами, например с LocalDateRenderer, показанным в разделе LocalDateRenderer. addComponentColumn() подходит, когда в каждой ячейке должен отображаться UI-компонент.

В следующем примере создаётся таблица с заданным пользователем количеством колонок:

XML
<hbox>
    <integerField id="columnCountField" placeholder="Number of columns" value="3">
        <validators>
            <max value="10"/>
            <min value="1"/>
        </validators>
    </integerField>
    <button id="createDataGridBtn" text="Create DataGrid"/>
</hbox>
<vbox id="box" padding="false"/>
Java
@ViewComponent
protected VerticalLayout box;
@ViewComponent
protected JmixIntegerField columnCountField;

@Autowired
protected DataComponents dataComponents;
@Autowired
protected UiComponents uiComponents;
@Autowired
protected Notifications notifications;

@Subscribe("createDataGridBtn")
protected void onCreateDataGridBtnClick(ClickEvent<JmixButton> event) {
    box.removeAll();
    Integer columnCount = columnCountField.getValue();

    if (columnCount == null || columnCount < 1 || columnCount > 10) {
        notifications.create("Column count must be between 1 and 10")
                .withType(Notifications.Type.WARNING)
                .withCloseable(false)
                .show();
        return;
    }

    KeyValueCollectionContainer container = createDataContainer(columnCount);
    DataGrid<KeyValueEntity> dataGrid = createDataGrid(columnCount, container);
    box.add(dataGrid);
}

protected DataGrid<KeyValueEntity> createDataGrid(Integer columnCount, KeyValueCollectionContainer container) {
    DataGrid<KeyValueEntity> dataGrid = uiComponents.create(DataGrid.class);
    dataGrid.setWidthFull();
    dataGrid.setMinHeight("12em");

    for (int col = 1; col <= columnCount; col++) {
        dataGrid.addColumn("prop" + col, container.getEntityMetaClass().getPropertyPath("prop" + col))
                .setHeader("Prop" + col);
    }
    dataGrid.setItems(new ContainerDataGridItems(container));
    return dataGrid;
}

protected KeyValueCollectionContainer createDataContainer(Integer columnCount) {
    KeyValueCollectionContainer container = dataComponents.createKeyValueCollectionContainer();

    for (int col = 1; col <= columnCount; col++) {
        container.addProperty("prop" + col, String.class);
    }

    container.setItems(loadData(columnCount));
    return container;
}

protected Collection<KeyValueEntity> loadData(Integer columnCount) {
    Collection<KeyValueEntity> list = new ArrayList<>();

    for (int row = 0; row < 5; row++) {
        KeyValueEntity entity = new KeyValueEntity();

        for (int col = 1; col <= columnCount; col++) {
            entity.setValue("prop" + col, "value" + row + col);
        }
        list.add(entity);
    }

    return list;
}

Множественный выбор

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

XML
<select id="selectionModeSelect" label="Selection mode"/>
<hbox id="buttonsPanel" width="100%" wrap="true">
    <button id="greetAllBtn" action="customersDataGrid.greetAll"/>
    <button id="greetOneBtn" action="customersDataGrid.greetOne"/>
</hbox>
<dataGrid id="customersDataGrid"
          width="100%"
          minHeight="20em"
          dataContainer="customersDc">
    <actions>
        <action id="greetAll" type="list_itemTracking" icon="COMMENTS" text="Greet all"/>
        <action id="greetOne" type="list_itemTracking" icon="COMMENT" text="Greet single selected"/>
    </actions>
    <columns>
        <column property="name"/>
        <column property="lastName"/>
        <column property="email"/>
        <column property="age"/>
        <column property="active"/>
        <column property="grade"/>
    </columns>
</dataGrid>
Java
@ViewComponent
protected JmixSelect<Grid.SelectionMode> selectionModeSelect;
@ViewComponent
protected DataGrid<Customer> customersDataGrid;
@Subscribe
protected void onInit(InitEvent event) {
    ComponentUtils.setItemsMap(selectionModeSelect, getSelectionModeItemsMap());
    selectionModeSelect.setValue(Grid.SelectionMode.NONE);
}

@Subscribe("selectionModeSelect")
protected void onSelectionModeValueChange(
        ComponentValueChangeEvent<JmixSelect<Grid.SelectionMode>, Grid.SelectionMode> event) {
    customersDataGrid.setSelectionMode(event.getValue());
}
protected Map<Grid.SelectionMode, String> getSelectionModeItemsMap() {
    return Arrays.stream(Grid.SelectionMode.values())
            .collect(Collectors.toMap(Function.identity(), mode -> mode.name().replace('_', ' ')));
}
Выбор диапазона с помощью Shift + щелчок и других сочетаний клавиш пока не поддерживается.

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

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

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

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

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

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

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

Колонка действий по умолчанию добавляется рядом с другими колонками. Если указано includeAll="true" - автоматически размещается крайней справа.
<dataGrid id="customersDataGridNonBuffered"
          width="100%"
          height="100%"
          minHeight="20em"
          dataContainer="customersDc">
    <columns>
        <column property="name" editable="true"/>
        <column property="lastName" editable="true"/>
        <column property="email"/>
        <column property="age"/>
        <column property="active"/>
        <column property="grade" editable="true"/>
        <editorActionsColumn key="nonBufferedEditorColumn">
            <editButton icon="PENCIL" text="msg:///actions.Edit"/>
            <closeButton icon="CHECK"/>
        </editorActionsColumn>
    </columns>
</dataGrid>

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

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

<dataGrid id="customersDataGridBuffered"
          width="100%"
          height="100%"
          minHeight="20em"
          dataContainer="customersDc"
          editorBuffered="true">
    <columns>
        <column property="name" editable="true"/>
        <column property="lastName" editable="true"/>
        <column property="email"/>
        <column property="age"/>
        <column property="active"/>
        <column property="grade" editable="true"/>
        <editorActionsColumn key="bufferedEditorColumn">
            <editButton icon="PENCIL" text="msg:///actions.Edit"/>
            <saveButton icon="CHECK" themeNames="success"/>
            <cancelButton icon="CLOSE" themeNames="error" text="msg:///actions.Cancel"/>
        </editorActionsColumn>
    </columns>
</dataGrid>

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

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

<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 следующим образом:

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

@Autowired
private DataManager dataManager;
@Autowired
private Notifications notifications;

@ViewComponent
private CollectionContainer<Customer> customersDc;
@ViewComponent
private CollectionLoader<Customer> customersDl;
@ViewComponent
private JmixCheckbox immediateCheckbox;
@ViewComponent
private JmixButton saveButton;

private final Set<Customer> changedCustomers = new HashSet<>();

@Install(to = "customersDataGridBuffered.@editor", subject = "saveListener")
private void customersDataGridBufferedEditorSaveListener(final EditorSaveEvent<Customer> event) {
    saveOrEnqueue(event.getItem());
}

@Install(to = "customersDataGridNonBuffered.@editor", subject = "closeListener")
private void customersDataGridNonBufferedEditorCloseListener(final EditorCloseEvent<Customer> event) {
    saveOrEnqueue(event.getItem());
}

@Subscribe("immediateCheckbox")
public void onImmediateCheckboxComponentValueChange(final AbstractField.ComponentValueChangeEvent<JmixCheckbox, Boolean> event) {
    saveButton.setEnabled(!event.getValue());
}

@Subscribe(id = "saveButton", subject = "clickListener")
public void onSaveButtonClick(final ClickEvent<JmixButton> event) {
    saveEnqueuedChanges();
}

private void saveOrEnqueue(Customer customer) {

    if (immediateCheckbox.getValue()) {
        saveChanges(customer);
    } else {
        changedCustomers.add(customer);
    }
}

private void saveChanges(Customer customer) {
    // Save changed entity
    Customer savedCustomer = dataManager.save(customer);
    // Replace the original entity in the data container with the saved one
    customersDc.replaceItem(savedCustomer);
    // Note that if the original entity is merged into DataContext (this is the
    // case when <loader readOnly="false">), the saved instance should be merged too
    notifications.show("Changes saved to the database");
}

private void saveEnqueuedChanges() {
    if (!changedCustomers.isEmpty()) {
        // Save enqueued changes. The returned value is ignored because all data will be reloaded.
        dataManager.saveAll(changedCustomers);
        changedCustomers.clear();
        notifications.show("Changes saved to the database");
        customersDl.load();
    }
}

Для генерации обработчиков в виде аннотированных методов используйте вкладку 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, который содержит объединённые ячейки в заголовке и вычисляемое значение в подвале:

XML
<dataGrid id="dataGrid"
          width="100%"
          minHeight="20em"
          dataContainer="countryGrowthDc"
          themeNames="column-borders row-stripes">
    <columns>
        <column property="country"/>
    </columns>
</dataGrid>
Java
@ViewComponent
protected DataGrid<CountryGrowth> dataGrid;

@Autowired
protected MessageTools messageTools;
@Autowired
protected Metadata metadata;

protected DecimalFormat percentFormat;

@Subscribe
protected void onInit(InitEvent event) {
    initPercentFormat();
    initColumns();
    initHeader();
}

@Subscribe
protected void onBeforeShow(BeforeShowEvent event) {
    //because data is loaded
    initFooter();
}

protected void initPercentFormat() {
    percentFormat = (DecimalFormat) NumberFormat.getPercentInstance(UI.getCurrent().getLocale());
    percentFormat.setMultiplier(1);
    percentFormat.setMaximumFractionDigits(2);
}

protected void initColumns() {
    MetaClass metaClass = metadata.getClass(CountryGrowth.class);
    dataGrid.addComponentColumn(countryGrowth -> new Text(percentFormat.format(countryGrowth.getPrevYear())))
            .setHeader(messageTools.getPropertyCaption(metaClass, "prevYear"))
            .setKey("prevYear");
    dataGrid.addComponentColumn(countryGrowth -> new Text(percentFormat.format(countryGrowth.getCurrYear())))
            .setHeader(messageTools.getPropertyCaption(metaClass, "currYear"))
            .setKey("currYear");
}

protected void initHeader() {
    HeaderRow headerRow = dataGrid.prependHeaderRow();
    HeaderRow.HeaderCell headerCell = headerRow.join(
            dataGrid.getColumnByKey("prevYear"),
            dataGrid.getColumnByKey("currYear")
    );

    Span gdpGrowth = new Span("GDP growth");
    HorizontalLayout layout = new HorizontalLayout(gdpGrowth);

    layout.setJustifyContentMode(FlexComponent.JustifyContentMode.CENTER);
    headerCell.setComponent(layout);
}

protected void initFooter() {
    FooterRow footerRow = dataGrid.appendFooterRow();

    FooterRow.FooterCell countryCell = footerRow.getCell(dataGrid.getColumnByKey("country"));
    Html html = new Html("<strong> Average: </strong>");
    countryCell.setComponent(html);

    footerRow.getCell(dataGrid.getColumnByKey("prevYear"))
            .setText(percentFormat.format(getAverage("prevYear")));
    footerRow.getCell(dataGrid.getColumnByKey("currYear"))
            .setText(percentFormat.format(getAverage("currYear")));

}

protected double getAverage(String propertyId) {
    double average = 0.0;

    Collection<CountryGrowth> items = dataGrid.getGenericDataView().getItems().toList();
    for (CountryGrowth countryGrowth : items) {
        Double value = propertyId.equals("prevYear")
                ? countryGrowth.getPrevYear()
                : countryGrowth.getCurrYear();

        average += value != null ? value : 0.0;
    }
    return average / items.size();
}

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

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

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

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

Например:

<dataGrid id="customersDataGrid"
          width="100%"
          minHeight="20em"
          dataContainer="customersDc">
    <columns>
        <column property="name" filterable="true"/>
        <column property="lastName"/>
        <column property="email"/>
        <column property="age" filterable="true"/>
        <column property="active" filterable="true"/>
        <column property="grade" filterable="true"/>
    </columns>
</dataGrid>

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

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

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

Действия

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

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

XML
<hbox id="buttonsPanel" width="100%" wrap="true">
    <button id="createBtn" action="customersDataGrid.create"/>
    <button id="editBtn" action="customersDataGrid.edit"/>
    <button id="removeBtn" action="customersDataGrid.remove"/>
    <button id="greetingBtn" action="customersDataGrid.greeting"/>
</hbox>
<dataGrid id="customersDataGrid"
          width="100%"
          minHeight="20em"
          dataContainer="customersDc">
    <actions>
        <action id="create" type="list_create">
            <properties>
                <property name="openMode" value="DIALOG"/>
            </properties>
        </action>
        <action id="edit" type="list_edit">
            <properties>
                <property name="openMode" value="DIALOG"/>
            </properties>
        </action>
        <action id="remove" type="list_remove"/>
        <action id="greeting" type="list_itemTracking" icon="COMMENT" text="Greeting"/>
    </actions>
    <columns>
        <column property="name"/>
        <column property="lastName"/>
        <column property="email"/>
        <column property="age"/>
        <column property="active"/>
        <column property="grade"/>
    </columns>
</dataGrid>
Java
@ViewComponent
protected DataGrid<Customer> customersDataGrid;

@Autowired
protected Notifications notifications;
@Autowired
protected MetadataTools metadataTools;

@Subscribe("customersDataGrid.greeting")
protected void onCustomersDataGridGreetingActionPerformed(ActionPerformedEvent event) {
    Customer customer = customersDataGrid.getSingleSelectedItem();

    notifications.show(customer != null
                    ? "Hello, " + metadataTools.getInstanceName(customer)
                    : "No selection");
}

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

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

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

XML
<hbox id="buttonsPanel" width="100%" wrap="true">
    <button id="createBtn" action="customersDataGrid.create"/>
    <button id="editBtn" action="customersDataGrid.edit"/>
    <button id="removeBtn" action="customersDataGrid.remove"/>
    <button id="greetingBtn" action="customersDataGrid.greeting"/>
</hbox>
<dataGrid id="customersDataGrid"
          width="100%"
          minHeight="20em"
          dataContainer="customersDc">
    <contextMenu id="contextMenu">
        <item text="CRUD" icon="TABLE">
            <item action="customersDataGrid.create" icon="PLUS"/>
            <item action="customersDataGrid.edit" icon="PENCIL"/>
            <item action="customersDataGrid.remove" icon="TRASH"/>
        </item>
        <item action="customersDataGrid.greeting" icon="COMMENT"/>
        <separator/>
        <item id="customerItem"/>
        <item action="customersDataGrid.getInfo" icon="INFO_CIRCLE_O"/>
    </contextMenu>
    <actions showInContextMenuEnabled="false">
        <action id="create" type="list_create">
            <properties>
                <property name="openMode" value="DIALOG"/>
            </properties>
        </action>
        <action id="edit" type="list_edit">
            <properties>
                <property name="openMode" value="DIALOG"/>
            </properties>
        </action>
        <action id="remove" type="list_remove"/>
        <action id="greeting" type="list_itemTracking" icon="COMMENT" text="Greeting"/>
        <action id="getInfo" type="list_itemTracking" icon="INFO_CIRCLE_O" text="Get info"/>
    </actions>
    <columns>
        <column property="name"/>
        <column property="lastName"/>
        <column property="email"/>
        <column property="age"/>
        <column property="active"/>
        <column property="grade"/>
    </columns>
</dataGrid>
Java
@ViewComponent
protected DataGrid<Customer> customersDataGrid;

@Autowired
protected Notifications notifications;
@Autowired
protected Dialogs dialogs;
@Autowired
protected MetadataTools metadataTools;

@ViewComponent
protected GridMenuItem<Customer> customerItem;

@Install(to = "contextMenu", subject = "dynamicContentHandler")
public boolean contextMenuDynamicContentHandler(Customer customer) {
    if (customer == null) {
        return false;
    }

    customerItem.setText(metadataTools.getInstanceName(customer));
    return true;
}

@Subscribe("customersDataGrid.greeting")
public void onCustomersDataGridGreetingActionPerformed(ActionPerformedEvent event) {
    Customer customer = customersDataGrid.getSingleSelectedItem();

    notifications.show(customer != null
            ? "Hello, " + metadataTools.getInstanceName(customer)
            : "No selection");
}

@Subscribe("customersDataGrid.getInfo")
public void onCustomersDataGridGetInfoActionPerformed(ActionPerformedEvent event) {
    Customer customer = customersDataGrid.getSingleSelectedItem();

    dialogs.createMessageDialog()
            .withContent(
                    customer != null
                            ? getMessageDialogContent(customer)
                            : new Span("No selection")
            )
            .open();
}

protected Component getMessageDialogContent(Customer customer) {
    return new Html("""
            <div>
                <strong>Name:</strong> %s<br/>
                <strong>Age:</strong> %s<br/>
                <strong>Email:</strong> %s
            </div>
            """.formatted(metadataTools.getInstanceName(customer), customer.getAge(), customer.getEmail()));
}

Рендереры

Рендереры настраивают отображение ячеек колонок. В этом разделе описаны рендереры, специфичные для колонок dataGrid. Общие API рендереров, такие как ComponentRenderer, LitRenderer и FragmentRenderer, описаны в разделе Рендереры.

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

  • Декларативно в XML - используйте предопределённые рендереры numberRenderer, localDateRenderer, localDateTimeRenderer, detailLinkRenderer и detailButtonRenderer внутри элемента column.

  • С помощью аннотации @Supply - предоставляйте пользовательский рендерер из контроллера экрана.

  • С помощью fragmentRenderer - используйте общий механизм рендеринга на основе фрагментов, описанный в Рендерер фрагментов.

  • С помощью addColumn() и addComponentColumn() - создавайте колонки в Java и настраивайте рендереры программно. См. Программные колонки.

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

LocalDateRenderer

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");
}

LocalDateTimeRenderer

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

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

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

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

Number Renderer

Отображает числовые значения в колонке.

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

Укажите ровно один из атрибутов format или numberFormat. Если не указан ни один из них или указаны оба, фреймворк выбросит GuiDevelopmentException. Используйте необязательный атрибут nullRepresentation, чтобы задать отображение null. Атрибут numberFormat использует синтаксис java.text.DecimalFormat.

Отображает ссылку, открывающую экран редактирования текущей сущности.

<dataGrid width="100%" dataContainer="usersDc">
    <columns>
        <column property="username">
            <detailLinkRenderer viewId="User.detail"
                                target="BLANK"
                                classNames="detail-link"
                                css="font-weight: 600;"/>
        </column>
        <column property="firstName"/>
        <column property="lastName"/>
    </columns>
</dataGrid>

Поддерживаемые атрибуты:

  • viewClass, viewId задают целевой экран. Рендерер определяет экран в следующем порядке: viewClass, viewId, экран редактирования по умолчанию.

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

  • target задаёт цель ссылки.

  • classNames, css применяют стили.

Целевой экран должен иметь зарегистрированный маршрут ровно с одним параметром маршрута, а экземпляр сущности должен уже иметь ненулевой id. Для маршрутов без параметров или с несколькими параметрами настраивайте рендерер из Java-кода и передавайте собственный URL с помощью DetailLinkRenderer.withHrefProvider().

DetailButtonRenderer

Отображает кнопку, открывающую экран редактирования текущей сущности.

<dataGrid width="100%" dataContainer="stepsDc">
    <columns>
        <column property="name">
            <detailButtonRenderer viewId="Step.detail"
                                  icon="vaadin:edit"
                                  openMode="DIALOG"
                                  themeNames="tertiary-inline small"
                                  classNames="detail-button"
                                  css="color: var(--lumo-primary-text-color);"/>
        </column>
        <column property="duration"/>
        <column property="sortValue"/>
    </columns>
</dataGrid>

Поддерживаемые атрибуты:

  • viewClass, viewId задают целевой экран. Рендерер определяет экран в следующем порядке: viewClass, viewId, экран редактирования по умолчанию.

  • openMode управляет способом открытия экрана: NAVIGATION (по умолчанию) или DIALOG.

  • text задаёт текст кнопки. Если атрибут не указан, рендерер использует форматированное значение привязанного свойства колонки. Для колонок только с key задавайте его явно.

  • icon задаёт значок кнопки. Его можно определить через атрибут или вложенный элемент icon.

  • themeNames, classNames, css применяют стили.

Detail Renderers в колонках, не связанных со свойством

DetailLinkRenderer и DetailButtonRenderer можно использовать в колонках, которые задаются через атрибут key, а не привязываются к атрибуту сущности с помощью атрибута property.

<dataGrid width="100%" dataContainer="usersDc">
    <columns>
        <column property="username"/>
        <column key="openUser" header="Open">
            <detailButtonRenderer viewId="User.detail"
                                  text="Edit"
                                  openMode="DIALOG"/>
        </column>
    </columns>
</dataGrid>

В этом случае задайте text ссылки или кнопки явно, потому что привязанное значение свойства отсутствует.

TextRenderer

TextRenderer отображает обычный текст, переданный из Java-кода.

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

Обычно этот рендерер задаётся из Java-кода с помощью обработчика renderer или аннотации @Supply.

ComponentRenderer

Отображает в каждой ячейке пользовательский компонент. Используйте этот рендерер для интерактивного или составного содержимого: флажка, изображения или кнопки.

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

В следующем примере в колонке active отображается флажок только для чтения, а в колонке grade — значок.

XML
<dataGrid id="customersDataGrid"
          width="100%"
          minHeight="20em"
          dataContainer="customersDc">
    <columns>
        <column property="name"/>
        <column property="lastName"/>
        <column property="email"/>
        <column property="age"/>
        <column property="active"/>
        <column property="grade"/>
    </columns>
</dataGrid>
Java
@Autowired
protected UiComponents uiComponents;
@Autowired
protected Messages messages;

@Supply(to = "customersDataGrid.active", subject = "renderer")
protected Renderer<Customer> activeComponentRenderer() {
    return new ComponentRenderer<>(
            () -> {
                JmixCheckbox checkbox = uiComponents.create(JmixCheckbox.class);
                checkbox.setReadOnly(true);
                return checkbox;
            },
            (checkbox, customer) -> checkbox.setValue(customer.isActive())
    );
}

@Supply(to = "customersDataGrid.grade", subject = "renderer")
protected Renderer<Customer> statusComponentRenderer() {
    return new ComponentRenderer<>(this::createGradeComponent, this::gradeComponentUpdater);
}

protected Span createGradeComponent() {
    Span span = uiComponents.create(Span.class);
    span.getElement().getThemeList().add("badge");

    return span;
}

protected void gradeComponentUpdater(Span span, Customer customer) {
    if (customer.getGrade() != null) {
        span.setText(messages.getMessage(CustomerGrade.class, customer.getGrade().name()));

        switch (customer.getGrade()) {
            case STANDARD -> span.getElement().getThemeList().add("contrast");
            case HIGH -> span.getElement().getThemeList().add("success");
            case PREMIUM -> span.getElement().getThemeList().add("primary");
        }
    } else {
        span.setText("No data");
    }
}

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

Компонент позволяет раскрывать строки и показывать дополнительные сведения об элементах. Для этого используйте метод setItemDetailsRenderer(). Он принимает ComponentRenderer, который определяет отображение подробных сведений.

Атрибут detailsVisibleOnClick определяет, открываются ли подробные сведения при нажатии на строку. Если их должно открывать другое событие, установите атрибут в false.

XML
<dataGrid id="customersDataGrid"
          width="100%"
          minHeight="20em"
          dataContainer="customersDc">
    <columns>
        <column property="name"/>
        <column property="lastName"/>
    </columns>
</dataGrid>
Java
@ViewComponent
protected DataGrid<Customer> customersDataGrid;

@Autowired
protected CustomerDetailsGenerator detailsGenerator;

@Subscribe
protected void onInit(InitEvent event) {
    detailsGenerator.setReadOnlyMode(true);
    customersDataGrid.setItemDetailsRenderer(createCustomerDetailsRenderer());
}

protected ComponentRenderer<FormLayout, Customer> createCustomerDetailsRenderer() {
    return new ComponentRenderer<>(detailsGenerator::createCustomerDetailsRenderer, detailsGenerator::setCustomer);
}

Сортировка

В этом разделе показана кастомизация сортировки для отдельного компонента dataGrid. О кастомизации сортировки для всего приложения см. Сортировка.

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

Атрибуты-перечисления сортируются по значениям id, а не по локализованным надписям, отображаемым в UI.

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

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

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

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

Пример:

<dataGrid id="customersDataGrid"
          width="100%"
          minHeight="20em"
          dataContainer="customersDc"
          multiSort="true"
          multiSortPriority="APPEND">
    <columns>
        <column property="name"/>
        <column property="lastName"/>
        <column property="email"/>
        <column property="age"/>
        <column property="active"/>
        <column property="grade"/>
    </columns>
</dataGrid>

Компаратор колонки

Используйте DataGridColumn#setComparator() для определения пользовательской логики сортировки конкретного колонки, когда сортировка выполняется в памяти. Сортировка в памяти используется, если у контейнера нет загрузчика, либо если загружена только первая страница и количество загруженных элементов меньше размера страницы.

В следующем примере атрибут Department.num сортируется как число, хотя тип этого атрибута - String:

<dataGrid id="inMemoryDepartmentsDataGrid"
          width="100%"
          dataContainer="inMemoryDepartmentsDc">
    <columns>
        <column property="name"/>
        <column property="num"/>
    </columns>
</dataGrid>
private void initInMemoryComparator() {
    inMemoryDepartmentsDc.setItems(dataManager.load(Department.class)
            .query("select e from Department e")
            .list());

    inMemoryDepartmentsDataGrid.getColumnByKey("num")
            .setComparator(Comparator.comparing(
                    department -> department.getNum() == null ? null : Integer.valueOf(department.getNum()),
                    Comparator.nullsFirst(Integer::compareTo)));
}

Делегат сортировки

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

В следующем примере колонка customer сортируется по имени и фамилии клиента:

XML
<simplePagination id="pagination"
                  dataLoader="ordersDl"
                  itemsPerPageDefaultValue="4"/>
<dataGrid id="ordersDataGrid"
          width="100%"
          minHeight="20em"
          dataContainer="ordersDc">
    <columns>
        <column property="date"/>
        <column property="customer"/>
        <column property="amount"/>
    </columns>
</dataGrid>
Java
@Install(to = "ordersDataGrid", subject = "sortBuilderDelegate")
public DataGridSort ordersDataGridSortBuilderDelegate(DataGridSortContext<Order> context) {
    return DataGridSortBuilder.create(context)
            .replaceSort("customer", List.of("{E}.customer.name", "{E}.customer.lastName"))
            .build();
}

Сортировка вычисляемой колонки

Делегат сортировки также полезен для колонок, которые не привязаны к атрибуту сущности. В следующем примере колонка fullName формируется из firstName и lastName, а делегат сортировки сопоставляет эту синтетическую колонку и с выражениями JPQL, и с компаратором для сортировки в памяти:

<dataGrid id="fullNameUsersDataGrid"
          width="100%"
          dataContainer="usersDc">
    <columns>
        <column key="fullName" header="Full name" sortable="true"/>
        <column property="username"/>
        <column property="department.name"/>
        <column property="onboardingStatus"/>
    </columns>
</dataGrid>
private void initComputedColumnSortBuilder() {
    fullNameUsersDataGrid.setSortBuilderDelegate(sortContext ->
            DataGridSortBuilder.create(sortContext)
                    .replaceSort("fullName",
                            List.of("{E}.firstName", "{E}.lastName"),
                            Comparator.comparing(User::getFirstName,
                                            Comparator.nullsFirst(String::compareTo))
                                    .thenComparing(User::getLastName,
                                            Comparator.nullsFirst(String::compareTo)))
                    .build());
}
@Supply(to = "fullNameUsersDataGrid.fullName", subject = "renderer")
protected Renderer<User> fullNameUsersDataGridFullNameRenderer() {
    return new TextRenderer<>(user ->
            ((user.getFirstName() == null ? "" : user.getFirstName()) + " "
                    + (user.getLastName() == null ? "" : user.getLastName())).trim());
}

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

Компонент поддерживает агрегирование значений в строках. Если агрегирование включено, отображается дополнительная строка с агрегированными значениями. Например:

<dataGrid id="ordersDataGrid"
          width="100%"
          minHeight="20em"
          dataContainer="ordersDc"
          aggregatable="true"
          aggregationPosition="TOP">
    <columns>
        <column property="date"/>
        <column property="customer"/>
        <column property="customer.grade">
            <aggregation
                    strategyClass="io.jmix.uisamples.view.flowui.components.datagrid.aggregation.DataGridCustomerGradeAggregation"/>
        </column>
        <column property="amount">
            <aggregation type="SUM"/>
        </column>
        <column property="description"/>
    </columns>
</dataGrid>

В атрибуте strategyClass элемента aggregation можно указать класс с пользовательской логикой агрегирования:

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

@Autowired
public Messages messages;

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

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

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

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

    return "NaN";
}

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

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

Если в таблице нет данных, область между заголовком и нижним колонтитулом по умолчанию остаётся пустой. Настройте пустое состояние, чтобы вывести сообщение или UI-компонент об отсутствии элементов.

  • Используйте атрибут 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.

    <hbox id="buttonsPanel" width="100%" wrap="true">
        <button id="unloadBtn" action="customersDataGrid.unload"/>
    </hbox>
    <dataGrid id="customersDataGrid"
              width="100%"
              minHeight="20em"
              dataContainer="customersDc">
        <actions>
            <action id="unload" icon="ERASER" text="msg://customersDataGrid.unload.text"
                    enabled="false"/>
        </actions>
        <columns>
            <column property="name"/>
            <column property="lastName"/>
            <column property="email"/>
            <column property="age"/>
            <column property="active"/>
            <column property="grade"/>
        </columns>
        <emptyStateComponent>
            <vbox padding="false" height="100%"
                  justifyContent="CENTER" alignItems="CENTER">
                <h3 text="msg://emptyState.text"/>
                <button id="loadBtn" text="msg://loadBtn.text" icon="REFRESH" themeNames="primary"/>
            </vbox>
        </emptyStateComponent>
    </dataGrid>
    @ViewComponent
    private CollectionContainer<Customer> customersDc;
    @ViewComponent
    private CollectionLoader<Customer> customersDl;
    
    @ViewComponent("customersDataGrid.unload")
    private Action unloadAction;
    
    @Subscribe("customersDataGrid.unload")
    public void onCustomersDataGridUnloadActionPerformed(ActionPerformedEvent event) {
        customersDc.getMutableItems().clear();
        unloadAction.setEnabled(false);
    }
    
    @Subscribe("loadBtn")
    public void onLoadBtnClick(ClickEvent<JmixButton> event) {
        customersDl.load();
        unloadAction.setEnabled(true);
    }

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

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

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

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

Варианты темы

Используйте атрибут themeNames, чтобы настроить тему компонента.

Вариант Описание Поддерживается в

column-borders

Добавляет вертикальные границы между колонками.

Aura, Lumo

compact

Уменьшает высоту строк и интервалы.

Lumo

no-border

Удаляет внешнюю границу вокруг сетки.

Aura, Lumo

no-row-borders

Удаляет горизонтальные границы между строками.

Aura, Lumo

row-stripes

Чередует цвета фона строк для улучшения читаемости.

Aura, Lumo

wrap-cell-content

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

Aura, Lumo

Атрибуты

dataGrid имеет следующие уникальные атрибуты:

Имя Описание По умолчанию

allRowsVisible

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

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

false

aggregatable

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

false

aggregationPosition

Если данные в компоненте — aggregatable, определяет, отображается ли строка агрегации выше или ниже других строк. Возможные значения: TOP или BOTTOM. См. Aggregating.

BOTTOM

columnReorderingAllowed

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

false

detailsVisibleOnClick

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

true

dropMode

Определяет, где можно оставить перетаскиваемую строку. Возможные значения: BETWEEN, ON_TOP, ON_TOP_OR_BETWEEN, ON_GRID. Этот атрибут можно использовать, например, для изменения порядка строк и их перетаскивания между таблицами.

editorBuffered

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

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

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

В небуферизированном режиме изменения применяются сразу, без подтверждения.

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, NONE.

SINGLE

Элементы

Элементы 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

Элементы

Агрегация - FragmentRenderer - LocalDateRenderer - LocalDateTimeRenderer - NumberRenderer - DetailLinkRenderer - DetailButtonRenderer

Table 2. Атрибуты

Название

Описание

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

autoWidth

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

false

editable

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

false

filterable

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

false

flexGrow

Задаёт коэффициент растяжения колонки. При значении 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

Элементы

элемент - разделитель

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

Название

Описание

GridContextMenuOpenedEvent

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

openedChangeEvent

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

dynamicContentHandler

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

emptyStateComponent

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

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