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](../../_images/visual-components/components/data-grid-basics.png)
数据绑定
声明式绑定
通常,使用 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> <property name="step" fetchPlan="_base"/> </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
- 英文逗号分隔的属性列表,这些属性不会被加载到数据网格。在下面的例子中,我们会显示除了
id
、version
和sortValue
之外的所有属性:<dataGrid width="100%" dataContainer="userStepsDc"> <columns includeAll="true" exclude="id,version,sortValue"/> </dataGrid>
column 元素
每一列是在内部的 column
元素中描述。
如需在 Jmix Studio 中添加 |
filterable
设置列的 filtering 属性。如果某列启用了过滤,则会在列的表头中添加一个过滤按钮。默认禁用列过滤。
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>
表头过滤
该功能目前是预览状态。其显示以及实现细节可能会在未来的版本中发生重大变化。 |
dataGrid
中的数据可以使用列标题中嵌入的 属性过滤器 进行过滤。
可以通过 XML 中的 filterable
属性定义哪些列可以进行过滤。可过滤的列表头中带有“漏斗”图标()。用户点击图标时,会显示一个包含属性过滤条件的对话框。如果设置了过滤条件,则该列中的图标将高亮显示。
若要确保过滤器图标始终正常显示,请使用 width
或 autoWidth
属性为列设置适当的宽度。最好不要让用户能调整列宽,否则用户有可能调小列宽导致过滤器图标不可见。
示例:
<dataGrid dataContainer="usersDc"
width="100%"
id="usersDataGrid">
<columns>
<column property="firstName" filterable="true"
width="15em"/>
<column property="lastName" filterable="true"
autoWidth="true"/>
<column property="username"/>
<column property="active"/>
<column property="joiningDate"/>
<column property="onboardingStatus"/>
</columns>
</dataGrid>
表头中的属性过滤器与独立属性过滤器或 genericFilter 通用过滤器 的工作方式相同 - 都是在 数据加载器 中添加查询条件。在标准流程中,过滤条件将转换为 JPQL 查询语句,并在数据库级别筛选数据。
可过滤的列可以与 propertyFilter
和 genericFilter
组件一起使用。所有过滤器的条件都由以 AND
逻辑运算进行组合。
目前,列过滤条件还没有绑定到页面的 URL 中。也就是说,如果用户使用了表头过滤器,然后导航到详情视图并返回,则表头过滤器将被清除。
操作
dataGrid
组件实现了 HasActions
接口,可以包含自定义操作和标准列表操作。
数据网格的操作定义在内部的 actions
元素中。
如需在 Jmix Studio 中添加 |
可以用 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](../../_images/visual-components/components/data-grid-actions.png)
渲染器
渲染器(Renderers)用来为数据网格中的数据实现自定义的展示方式。例如,可以用渲染器:
-
格式化日期或数字。
-
显示自定义的图标或图片。
-
创建可交互的元素,比如按钮、链接等。
-
根据单元格值的不同显示不同的内容。
渲染器可以通过三种方式定义:
-
在 XML 中声明式定义
XML 中可以使用一些预定义的列渲染器:
-
numberRenderer
-
localDateRenderer
-
localDateTimeRenderer
这些渲染器需要传入一个
format
字符串。示例:<column property="joiningDate"> <localDateRenderer format="MMM dd, yyyy"/> </column>
如需通过 Jmix Studio 添加一个渲染器,在视图 XML 或 Jmix UI 的结构面板中选中
column
元素并点击属性面板中的 Add→<Some>Renderer。
-
-
使用 @Supply 注解定义
渲染器可以在视图控制器中通过
@Supply
注解提供。使用@Supply
注解的方法需要返回一个值,该值传递给“主体”中定义的一个方法。自定义的列渲染器可以用列的
renderer
处理方法定义,该方法可以在 Jmix Studio 中通过column
元素属性面板的 Handlers 标签页创建。 -
通过 addColumn() 和 addComponentColumn() 方法定义
addColumn()
和addComponentColumn()
方法可以在数据网格中添加列。可以配置新添加的列使用渲染器展示数据。
下面的示例中,我们添加一列用于展示用户的图片:
@ViewComponent private DataGrid<User> usersDtGr; @Autowired private FileStorage fileStorage; @Subscribe public void onInit(InitEvent event) { Grid.Column<User> pictureColumn = usersDtGr.addComponentColumn(user -> { (1) FileRef fileRef = user.getPicture(); if (fileRef != null) { Image image = uiComponents.create(Image.class); (2) image.setWidth("30px"); image.setHeight("30px"); image.setClassName("user-picture"); StreamResource streamResource = new StreamResource( fileRef.getFileName(), () -> fileStorage.openStream(fileRef)); image.setSrc(streamResource); (3) return image; (4) } else { return new Span(); } }) .setHeader("Picture") .setAutoWidth(true) .setFlexGrow(0); usersDtGr.setColumnPosition(pictureColumn,0); }
1 添加一列,显示 image
组件。2 Image
组件的实例使用UiComponents
工厂创建。3 image
从给定的StreamResource
获取内容,存储在User
实体的picture
属性中。4 addComponentColumn()
方法返回的可视化组件展示在列单元格中。
本地日期渲染器
LocalDateRenderer
用于以 LocalDate
值的形式展示日期。
在数据网格的 column
元素中,支持 localDateRenderer
子元素,该元素可以包含一个可选的 nullRepresentation
属性和一个必需的 format
字符串属性。
<column property="joiningDate">
<localDateRenderer format="MMM dd, yyyy"/>
</column>
nullRepresentation
属性用于展示 null
值的文本。
下面的示例中,在 addColumn()
方法使用了 LocalDateRenderer
:
@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
用于以 LocalDateTime
值的形式展示日期。
在数据网格的 column
元素中,支持 localDateTimeRenderer
子元素,该元素可以包含一个可选的 nullRepresentation
属性和一个必需的 format
字符串属性。
<column property="passwordExpiration">
<localDateTimeRenderer format="dd/MM/YYYY HH:mm:ss"/>
</column>
nullRepresentation
属性用于展示 null
值的文本。
数字渲染器
NumberRenderer
用于在单元格中以指定的格式显示数字。
在数据网格的 column
元素中,支持 numberRenderer
子元素,该元素可以包含一个可选的 nullRepresentation
属性和一个必需的 format
或 numberFormat
字符串属性。
<column property="factor">
<numberRenderer numberFormat="#,#00.0000"/>
</column>
nullRepresentation
属性用于展示 null
值的文本。
numberFormat
属性需按照 java.text.DecimalFormat
类规定的格式和语法进行配置。
|
文本渲染器
TextRenderer
渲染器用于展示文本。
下面的示例中,列展示了一个自定义的文本:
<column key="status" header="Status"/>
@Supply(to = "userStepsDataGrid.status", subject = "renderer")
private Renderer<UserStep> userStepsDataGridStatusRenderer() {
return new TextRenderer<>(userStep ->
isOverdue(userStep) ? "Overdue!" : "");
}
组件渲染器
下面的示例显示如何在列中使用一个自定义的渲染器显示一个复选框:
<column key="completed" width="4em" flexGrow="0"/>
@Supply(to = "userStepsDataGrid.completed", subject = "renderer")
private Renderer<UserStep> userStepsDataGridCompletedRenderer() {
return new ComponentRenderer<>(userStep -> {
JmixCheckbox checkbox = uiComponents.create(JmixCheckbox.class);
checkbox.setValue(userStep.getCompletedDate() != null);
checkbox.addValueChangeListener(e -> {
// ...
});
return checkbox;
});
}
行内编辑
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>
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
- 关闭编辑模式。仅适用于非缓存模式。
每个按钮仅支持标准按钮的有限几个属性:text、icon
、title、classNames、themeNames、iconAfterText
。
编辑列会相对其他列添加到表格中。如果是 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
默认情况下,字段组件(例如,textField、comboBox)会在组件上方展示错误信息。但是在区域有限的单元格编辑器中,这种方式就不合适了。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();
}
});
}
聚合
dataGrid
可以对行内值进行聚合。
支持下列运算:
-
SUM
- 求和; -
AVG
- 求平均; -
COUNT
- 计算总数; -
MIN
- 最小值; -
MAX
- 最大值。
如需启用数据网格的聚合功能,需要:
-
设置
aggregatable
属性为true
。 -
为需要进行聚合运算的列设置
aggregation
元素。 -
设置
aggregation
元素的type
属性,定义聚合运算的类型。
Jmix Studio 中可以定义聚合,在 XML 或 Jmix UI 层级面板中选择需要聚合的列,然后点击属性面板中的 Add→Aggregation 按钮。 |
聚合值在附加的新一行中显示。
aggregationPosition
属性可以定义附加聚合行的位置:TOP
或 BOTTOM
。默认为 BOTTOM
。
示例:
<dataGrid width="100%"
dataContainer="stepsDc"
aggregatable="true"
aggregationPosition="TOP">
<columns>
<column property="name"/>
<column property="duration">
<aggregation type="AVG"
cellTitle="msg://aggregation.cellTitle"/> (1)
</column>
<column property="sortValue"/>
<column property="factor">
<aggregation type="AVG">
<formatter>
<number format="#,##0.00"/> (2)
</formatter>
</aggregation>
</column>
</columns>
</dataGrid>
1 | cellTitle 属性定义当用户光标悬停至聚合值时需要显示的提示信息。 |
2 | 可以通过 formatter 设置该数据类型自定义的显示格式。 |
aggregation
元素还有 strategyClass
属性,可以指定实现 AggregationStrategy
接口的类。
public class DataGridUserStatusAggregation implements AggregationStrategy<OnboardingStatus, String> {
@Autowired
public Messages messages;
@Override
public String aggregate(Collection<OnboardingStatus> propertyValues) {
OnboardingStatus mostFrequent = null;
long max = 0;
if (CollectionUtils.isNotEmpty(propertyValues)) {
for (OnboardingStatus status : OnboardingStatus.values()) {
long current = propertyValues.stream()
.filter(status :: equals)
.count();
if (current > max) {
mostFrequent = status;
max = current;
}
}
}
if (mostFrequent != null) {
String key = OnboardingStatus.class.getSimpleName() + "." + mostFrequent.name();
return String.format("%s: %d/%d", messages.getMessage(OnboardingStatus.class, key), max, propertyValues.size());
}
return null;
}
@Override
public Class<String> getResultClass() {
return String.class;
}
}
<dataGrid width="100%"
dataContainer="usersDc"
aggregatable="true"
aggregationPosition="TOP">
<columns>
<column property="firstName"/>
<column property="lastName"/>
<column property="onboardingStatus">
<aggregation
strategyClass="com.company.onboarding.view.component.datagrid.DataGridUserStatusAggregation"/>
</column>
</columns>
</dataGrid>
XML 属性
id - alignSelf - allRowsVisible - classNames - colspan - columnReorderingAllowed - css - dataContainer - detailsVisibleOnClick - dropMode - editorBuffered - enabled - height - maxHeight - maxWidth - metaClass - minHeight - minWidth - multiSort - multiSortOnShiftClickOnly - multiSortPriority - nestedNullBehavior - pageSize - rowDraggable - selectionMode - tabIndex - themeNames - visible - width
allRowsVisible
如果 allRowsVisible
属性设置为 true
,数据网格的高度由数据行数决定。会从 DataProvider
中读取所有数据并展示,dataGrid
不带垂直滚动条。
allRowsVisible = true 会禁用数据网格的垂直滚动条,所有数据会一次性在 DOM 渲染。如果数据网格有大量数据,会有性能问题,不推荐使用该功能。
|
columnReorderingAllowed
dataGrid
提供拖放功能,支持用户调整数据网格中的列顺序。
列重排的功能默认是关闭的。需要设置 columnReorderingAllowed = true
开启。
列重排可以通过事件 ColumnReorderEvent 跟踪。
dropMode
设置拖放时的放置模式。当设置为非 null
时,dataGrid
会在数据放置到数据网格或某一行是发送事件。
支持四种不同的放置模式:BETWEEN
、ON_TOP
、ON_TOP_OR_BETWEEN
、ON_GRID
。
事件和处理器
AttachEvent - BlurEvent - CellFocusEvent - ColumnReorderEvent - ColumnResizeEvent - DetachEvent - FocusEvent - GridDragEndEvent - GridDragStartEvent - GridDropEvent - ItemClickEvent - ItemDoubleClickEvent - SortEvent - classNameGenerator - dataGenerator - dragFilter - dropFilter
在 Jmix Studio 生成处理器桩代码时,可以使用 Jmix UI 组件面板的 Handlers 标签页或者视图类顶部面板的 Generate Handler 添加,也可以通过 Code → Generate 菜单(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 事件。
ItemDoubleClickEvent
当双击数据网格时,发送 com.vaadin.flow.component.grid.ItemDoubleClickEvent
。对应 item-double-click
DOM 事件。
dragFilter
为拖放源设置拖动过滤器。
当 rowDraggable = true 时,默认所有可见行都可以拖动。
可以用拖动过滤器指定哪些行支持拖动。该方法接收某一行数据作为输入,如果该行支持拖动,则返回 true
。参考 com.vaadin.flow.component.grid.Grid
。