dataGrid 数据网格

dataGrid 用于展示、排序表格类数据,由于使用了在滚动时加载数据的延迟加载方式,所以此组件具有更好的数据行、列操作性能。

  • XML 元素:dataGrid

  • Java 类:DataGrid

基本用法

一个在 XML 视图描述声明 dataGrid 的示例如下:

<data readOnly="true">
    <collection id="usersDc"
                class="com.company.onboarding.entity.User"> (1)
        <fetchPlan extends="_base"/>
        <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"> (2)
        <columns> (3)
            <column property="username"/>
            <column property="firstName"/>
            <column property="lastName"/>
            <column property="active"/>
            <column property="onboardingStatus"/>
            <column property="joiningDate"/>
        </columns>
    </dataGrid>
</layout>
1 User 实体的 集合数据容器
2 dataGrid 使用 dataContainer 属性绑定 usersDc 数据容器。
3 columns 元素定义在数据网格中展示哪些实体属性。
data grid basics

数据绑定

声明式绑定

通常,使用 dataContainer 属性在视图 XML 描述中将数据与 dataGrid 绑定。此时需要使用一个 集合数据容器

使用键值对容器

数据网格还可以绑定至 键值对容器 展示查询的纯数值和聚合值。示例:

<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>

编程式绑定

如需以编程的方式在视图控制器定义数据容器,则可以在 XML 中设置 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 类将数据网格和数据容器绑定:

@ViewComponent
private DataGrid<User> dataGrid;

@ViewComponent
private CollectionContainer<User> usersDc;

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

columns 元素

数据网格的列集合定义在 columns 元素中。columns 元素有如下属性:

  • includeAll - 加载数据容器中 fetch plan 的所有属性。

    在下面的例子中,我们显示了 usersDc 中使用 fetch plan 的所有属性:

    <dataGrid width="100%"
              dataContainer="usersDc">
        <columns includeAll="true"/>
    </dataGrid>

    如果实体的 fetch plan 包含引用属性,该属性会按照其 实例名 进行展示。如果需要展示一个特定的级联属性,则需要在 fetch plan 和 column 元素中定义:

    <data readOnly="true">
        <collection class="com.company.onboarding.entity.UserStep" id="userStepsDc">
            <fetchPlan extends="_base">
                <property name="user" fetchPlan="_base">
                    <property name="department" fetchPlan="_base"/>
                </property>
            </fetchPlan>
            <loader id="userStepsDl">
                <query>
                    <![CDATA[select e from UserStep e]]>
                </query>
            </loader>
        </collection>
    </data>
    <layout>
        <dataGrid width="100%"
                  dataContainer="userStepsDc">
            <columns includeAll="true">
                <column property="user.department.name"/>
            </columns>
        </dataGrid>
    </layout>
  • exclude - 英文逗号分隔的属性列表,这些属性不会被加载到数据网格。

    在下面的例子中,我们会显示除了 idversionsortValue 之外的所有属性:

    <dataGrid width="100%"
              dataContainer="userStepsDc">
        <columns includeAll="true"
                 exclude="id,version,sortValue"/>
    </dataGrid>

column 元素

每一列是在内部的 column 元素中描述。

如需在 Jmix Studio 中添加 column,可以在视图 XML 或者 Jmix UI 层级面板中选择 columns 元素,然后点击组件面板的 Add→Column 按钮。

autoWidth

为该列启用/禁用自动列宽。

autoWidth = true 时,列的宽度依据内容自动调整。

该属性默认为 false

editable

为该列创建一个编辑器组件。

参考 行内编辑

flexGrow

设置列的 flex grow 比例。当设置成 0 时,列宽固定。

设置列的 footer 文本。

该属性可以是文本本身或者 消息包 中的一个键值。如果是消息包键值,则必须以 msg:// 开头。

frozen

设置是否锁定该列。

列锁定是 UI 层操作,如需锁定请从左往右锁定,以得到更流畅的展示。

设置列的 header。

该属性可以是文本本身或者 消息包 中的一个键值。如果是消息包键值,则必须以 msg:// 开头。

key

设置用户定义的列映射 id。这个键值可以用来通过 getColumnByKey(String) 方法获取该列。

键值必须在同一个数据网格中唯一,设定后不能更改。

property

property 指定列对应的实体属性。可以是数据容器实体的属性,也可以是关联实体的属性,关联实体属性前面需要加上关联类名字并通过 “.” 连接。例如:

<columns>
    <column property="user.firstName" sortable="false"/>
    <column property="user.lastName" sortable="false"/>
    <column property="step" frozen="true" sortable="true"/>
    <column property="dueDate" editable="true" sortable="true"/>
    <column property="user.department.name" sortable="false"/>
</columns>

resizable

当设置为 true 时,用户可以手动调整列宽。该属性默认为 false

列宽变化事件可以通过 ColumnResizeEvent 跟踪。

sortable

设置用户是否能对该列排序。

dataGrid 的排序事件可以通过 SortEvent 跟踪。

textAlign

设置列的文字对齐。

支持三种不同的对齐方式:START(默认)、CENTEREND

visible

设置列可见性。

width

使用 CSS 字符串设置列宽。

操作

dataGrid 组件实现了 HasActions 接口,可以包含自定义操作和标准列表操作。

数据网格的操作定义在内部的 actions 元素中。

如需在 Jmix Studio 中添加 action,可以在视图 XML 或者 Jmix UI 面板中选择组件,然后点击组件面板的 Add→Action 按钮。

如果操作有 标题,则会显示在数据网格的右键菜单中。此外,数据网格的操作可以分配给视图中任意位置的 按钮

可以用 hbox 容器在数据网格上方显示操作按钮。

<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 在数据网格上方定义一个 buttonsPanel hbox 容器。
2 定义 list_create 标准操作。
3 定义 getInfo 自定义操作
data grid actions

渲染器

使用 addColumn()addComponentColumn() 方法可以添加列。

为更好地展示数据,可以使用渲染器对添加的列进行配置。

下面示例中,我们创建了一个生成列,并用 LocalDateRenderer 展示用户的入职时间。

@ViewComponent
private DataGrid<User> usersDtGr;

@Subscribe
public void onInit(InitEvent event) {
    usersDtGr.addColumn(new LocalDateRenderer<>
                    (User::getJoiningDate,"dd/MM"))
            .setHeader("Joining date");
}

下面的示例中,我们添加一列展示用户的头像:

@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 image 从给定的 StreamResource 获取内容,存储在 User 实体的 picture 属性中。
4 addComponentColumn() 方法返回的可视化组件展示在列单元格中。

行内编辑

dataGrid 组件支持行内编辑器来编辑单元格数据。

带有 editable = true 属性的列展示实体属性编辑的组件。每个可编辑列的编辑组件是根据对应实体的属性自动选取的。

editorBuffered 属性定义行内编辑器是否启用缓存模式。缓存模式:修改后的数据需要显式提交。

  • 如需将某些列设为可编辑,请设置 editable 属性为 true

  • 然后,就可以通过编程的方式编辑某一行,参考 Vaadin 文档;或者定义 editorActionsColumn 元素,示例:

    <dataGrid width="100%" dataContainer="usersDc" id="editableUsersTable">
        <columns>
            <column property="username"/>
            <column property="firstName" editable="true"/>
            <column property="lastName" editable="true"/>
            <column property="active" editable="true"/>
            <column property="onboardingStatus"/>
            <editorActionsColumn width="8em" flexGrow="0">
                <editButton text="Edit" icon="PENCIL"/>
                <closeButton text="Close" icon="CLOSE"/>
            </editorActionsColumn>
        </columns>
    </dataGrid>
    data grid editing

editorActionsColumn

editorActionsColumn 元素用来创建自定义带编辑按钮的列。

<editorActionsColumn width="16em" flexGrow="0">
    <editButton text="Edit" icon="PENCIL"/>
    <saveButton icon="CHECK" themeNames="success"/>
    <cancelButton icon="CLOSE" themeNames="error"/>
    <closeButton text="Close" icon="CLOSE"/>
</editorActionsColumn>

编辑列中可以包含下列按钮:

  • editButton - 开始编辑。适用于缓存或非缓存模式。

  • saveButton - 保存编辑列中的改动。适用于缓存模式。

  • cancelButton - 丢弃编辑列中的改动。适用于缓存模式。

  • closeButton - 关闭编辑模式。仅适用于非缓存模式。

每个按钮仅支持标准按钮的有限几个属性:texticontitleclassNamesthemeNamesiconAfterText

编辑列会相对其他列添加到表格中。如果是 includeAll="true",编辑列添加在最后。

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

默认情况下,字段组件(例如,textFieldcomboBox)会在组件上方展示错误信息。但是在区域有限的单元格编辑器中,这种方式就不合适了。io.jmix.flowui.component.SupportsStatusChangeHandler 接口支持定义以不同的方式展示错误信息。实现了这个接口的组件支持错误信息代理。

行内编辑器默认使用 StatusChangeHandler,会将错误信息作为组件的 title 展示。

双击编辑

有时候我们需要通过双击的方式打开行内编辑器。示例:

<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;

@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();
        }
    });
}

XML 属性

allRowsVisible

如果 allRowsVisible 属性设置为 true,数据网格的高度由数据行数决定。会从 DataProvider 中读取所有数据并展示,dataGrid 不带垂直滚动条。

allRowsVisible = true 会禁用数据网格的垂直滚动条,所有数据会一次性在 DOM 渲染。如果数据网格有大量数据,会有性能问题,不推荐使用该功能。

columnReorderingAllowed

dataGrid 提供拖放功能,支持用户调整数据网格中的列顺序。

列重排的功能默认是关闭的。需要设置 columnReorderingAllowed = true 开启。

列重排可以通过事件 ColumnReorderEvent 跟踪。

detailsVisibleOnClick

设置是否可以通过点击行打开或关闭条目详情。

dropMode

设置拖放时的放置模式。当设置为非 null 时,dataGrid 会在数据放置到数据网格或某一行是发送事件。

支持四种不同的放置模式:BETWEENON_TOPON_TOP_OR_BETWEENON_GRID

editorBuffered

设置 Editor 的缓存模式。当编辑器使用缓存模式时,只有当用户点击保存按钮时才会提交改动。在非缓存模式时,改动会自动提交。

参考 行内编辑

nestedNullBehavior

设置当解析可能包含 null 值的内部属性时的行为。

pageSize

设置每页展示的行数,这个数值也是每次从数据提供器中获取的数量。参考 com.vaadin.flow.component.grid.Grid.setPageSize() 方法。

默认值:50

rowDraggable

设置用户是否可以拖放网格的行。

selectionMode

设置数据网格的选择模式。

支持两种模式:SINGLEMULTI。默认使用单选模式。

事件和处理器

在 Jmix Studio 生成处理器桩代码时,可以使用 Jmix UI 组件面板的 Handlers 标签页或者视图类顶部面板的 Generate Handler 添加,也可以通过 CodeGenerate 菜单(Alt+Insert / Cmd+N)生成。

CellFocusEvent

当数据网格的某个单元格获得焦点时,发送 com.vaadin.flow.component.grid.CellFocusEvent。对应 grid-cell-focus DOM 事件。

ColumnReorderEvent

当数据网格的列重排时,发送 com.vaadin.flow.component.grid.ColumnReorderEvent。对应 column-reorder-all-columns DOM 事件。

ColumnResizeEvent

当用户改变数据网格列大小时,发送 com.vaadin.flow.component.grid.ColumnResizeEvent。对应 column-drag-resize DOM 事件。

GridDragEndEvent

当数据网格的行拖放结束时,发送 com.vaadin.flow.component.grid.dnd.GridDragEndEvent。对应 grid-dragend DOM 事件。

GridDragStartEvent

当数据网格的行拖放开始时,发送 com.vaadin.flow.component.grid.dnd.GridDragStartEvent。对应 grid-dragstart DOM 事件。

GridDropEvent

当拖放事件发生在数据网格或数据行时,发送 com.vaadin.flow.component.grid.dnd.GridDropEvent。对应 grid-drop DOM 事件。

ItemClickEvent

当点击数据网格时,发送 com.vaadin.flow.component.grid.ItemClickEvent。对应 item-click DOM 事件。

ItemDoubleClickEvent

当双击数据网格时,发送 com.vaadin.flow.component.grid.ItemDoubleClickEvent。对应 item-double-click DOM 事件。

SortEvent

com.vaadin.flow.data.event.SortEvent - 描述 DataProvider 中排序变化的事件。由 SortNotifiers 触发。

classNameGenerator

classNameGenerator 用来为单元格生成 CSS 类名。

dataGenerator

为数据网格添加数据生成器。如果已经添加,则不做操作。参考 com.vaadin.flow.data.provider.HasDataGenerators 接口。

dragFilter

为拖放源设置拖动过滤器。 当 rowDraggable = true 时,默认所有可见行都可以拖动。 可以用拖动过滤器指定哪些行支持拖动。该方法接收某一行数据作为输入,如果该行支持拖动,则返回 true。参考 com.vaadin.flow.component.grid.Grid

dropFilter

为拖放目标设置放置过滤器。

当数据网格的 放置模式 设置为 BETWEENON_TOPON_TOP_OR_BETWEEN 之一时,默认所有可见行的位置都可以放置。 可以用放置过滤器指定哪些行支持放置。该方法接收某一行数据作为输入,如果该行支持放置,则返回 true。参考 com.vaadin.flow.component.grid.Grid

XML 内部元素

参考