4.5.2.1.6. DataGrid

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

gui dataGrid 1

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

Компонент реализован для блока Web Client.

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

<dsContext>
    <collectionDatasource id="ordersDs"
                          class="com.sample.sales.entity.Order"
                          view="orderWithCustomer">
        <query>
            select o from sales$Order o order by o.date
        </query>
    </collectionDatasource>
</dsContext>
<dataGrid id="ordersDataGrid"
          datasource="ordersDs"
          height="100%"
          width="100%">
    <columns>
        <column id="date" property="date"/>
        <column id="customerName" property="customer.name"/>
        <column id="amount" property="amount"/>
    </columns>
</dataGrid>

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

Элементы dataGrid:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Атрибуты dataGrid:

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

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

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

  • columnsCollapsingAllowed - разрешает или запрещает пользователю скрывать колонки с помощью меню (sidebar menu) в правой части шапки DataGrid. Флажками в меню отмечаются отображаемые в данный момент колонки. В момент установки перезаписывает значение collapsed каждой отдельной колонки. Установка значения в false не позволяет атрибуту collapsed отдельной колонки принять значение true.

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

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

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

  • reorderingAllowed - разрешает или запрещает пользователю менять местами колонки, перетаскивая их с помощью мыши. По умолчанию имеет значение true.

  • responsive - определяет, должен ли компонент реагировать на изменения размеров доступной области. Реакцию можно задать с помощью стилей.

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

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

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

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

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

      gui dataGrid 3
  • settingsEnabled - определяет, нужно ли сохранять пользовательские настройки внешнего вида DataGrid. По умолчанию имеет значение true.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    customersDataGrid.setRowDescriptionProvider(Instance::getInstanceName);
    gui dataGrid 10
  • setRowDescriptionProvider() - принимает экземпляр RowDescriptionProvider, который будет использоваться для генерации всплывающих подсказок для строк DataGrid. Если CellDescriptionProvider также установлен, подсказка, сгенерированная RowDescriptionProvider, будет использована только для тех ячеек, для которых не задана подсказка ячейки.

    customersDataGrid.setCellDescriptionProvider((entity,columnId)->{
        if ("name".equals(columnId)||"lastName".equals(columnId)){
            return null;
        }
    
        String description="<strong>"+
                messages.getTools().getPropertyCaption(entity.getMetaClass(),columnId)+
                ": </strong>";
    
        if ("grade".equals(columnId)){
            description += messages.getMessage(entity.getGrade());
        } else if ("active".equals(columnId)){
            description += getMessage(entity.getActive() ? "trueString":"falseString");
        } else {
            description += entity.getValue(columnId);
        }
            return description;
    });
    gui dataGrid 11

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

DataGrd имеет возможность добавлять генерируемые, или высчитываемые, колонки. Для этого существует два метода:

  • addGeneratedColumn(String columnId, ColumnGenerator generator)

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

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

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

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

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

@Override
public void init(Map<String, Object> params){
    DataGrid.Column column = usersGrid.addGeneratedColumn("loginUpperCase",new DataGrid.ColumnGenerator<User, String>(){
        @Override
        public String getValue(DataGrid.ColumnGeneratorEvent<User> event){
            return event.getItem().getLogin().toUpperCase();
        }

        @Override
        public Class<String> getType(){
            return String.class;
        }
    },1);
    column.setCaption("Login Upper Case");
}

Результат:

gui dataGrid 7

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

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

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

@Override
public void init(Map<String, Object> params){
    DataGrid.Column avatar = usersGrid.addGeneratedColumn("userAvatar", new DataGrid.ColumnGenerator<User, String>() {
        @Override
        public String getValue(DataGrid.ColumnGeneratorEvent<User> event) {
            return "icons/user.png";
        }

        @Override
        public Class<String> getType() {
            return String.class;
        }
    }, 0);
    avatar.setCaption("Avatar");
    avatar.setRenderer(usersGrid.createRenderer(DataGrid.ImageRenderer.class));
}

Результат:

gui dataGrid 8

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

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

        @Override
        public Class<Boolean> getType() {
            return Boolean.class;
        }
    });
    hasEmail.setCaption("Has Email");
    hasEmail.setRenderer(usersGrid.createRenderer(DataGrid.HtmlRenderer.class));
    hasEmail.setConverter(new DataGrid.Converter<String, Boolean>() {
        @Override
        public Boolean convertToModel(String value, Class<? extends Boolean> targetType, Locale locale) {
            return null;
        }

        @Override
        public String convertToPresentation(Boolean value, Class<? extends String> targetType, Locale locale) {
            return BooleanUtils.isTrue(value)
                    ? FontAwesome.CHECK_SQUARE_O.getHtml()
                    : FontAwesome.SQUARE_O.getHtml();
        }

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

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

Результат:

gui dataGrid 9

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

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

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

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

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

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

  • TextRenderer - рендерер для отображения простого текста.

  • HtmlRenderer - рендерер для отображения HTML-разметки.

  • ProgressBarRenderer - рендерер, который отображает double-значения в виде компонента ProgressBar.

  • DateRenderer - рендерер для отображения дат в заданном формате.

  • NumberRenderer - рендерер для отображения чисел в заданном формате.

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

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

  • CheckBoxRenderer - рендерер, который отображает булево значение в виде иконок чек-бокса.

Header и Footer:

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

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

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

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

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

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

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

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

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

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

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

    • TEXT

    • HTML

    • COMPONENT

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

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

<dataGrid id="dataGrid"
          datasource="countryGrowthDs"
          width="100%">
    <columns>
        <column property="country"/>
        <column property="year2014"/>
        <column property="year2015"/>
    </columns>
</dataGrid>
public class DataGridHeaderFooterFrame extends AbstractFrame {
    @Inject
    private DataGrid<CountryGrowth> dataGrid;
    @Inject
    private CollectionDatasource<CountryGrowth, UUID> countryGrowthDs;
    @Inject
    private UserSessionSource userSessionSource;

    private DecimalFormat percentFormat;

    @Override
    public void init(Map<String, Object> params) {
        countryGrowthDs.refresh();

        initPercentFormat();
        initHeader();
        initFooter();
        initRenderers();
    }

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

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

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

    private void initFooter() {
        FooterRow footerRow = dataGrid.appendFooterRow();
        footerRow.getCell("country").setHtml("<strong>" + getMessage("average") + "</strong>");
        footerRow.getCell("year2014").setText(percentFormat.format(getAverage("year2014")));
        footerRow.getCell("year2015").setText(percentFormat.format(getAverage("year2015")));
    }

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