dataGrid 数据网格
展示表格数据,可以支持编辑、排序、过滤以及基于列的聚合。在滚动展示大数据集时,使用了延迟加载保证效率。
XML 元素 |
|
|---|---|
Java 类 |
|
XML 属性 |
通用属性: id - aggregatable - columnReorderingAllowed - emptyStateText - enabled - selectionMode - visible |
数据绑定: dataContainer - metaClass |
|
尺寸: height - maxHeight - maxWidth - minHeight - minWidth - width |
|
外观: classNames - css - themeNames |
|
其他: aggregationPosition - allRowsVisible - ariaLabel - detailsVisibleOnClick - dropMode - editorBuffered - focusShortcut - multiSort - multiSortOnShiftClickOnly - multiSortPriority - nestedNullBehavior - pageSize - rowsDraggable - tabIndex |
|
事件和处理器 |
AttachEvent - BlurEvent - CellFocusEvent - ColumnReorderEvent - ColumnResizeEvent - DetachEvent - FocusEvent - GridDragEndEvent - GridDragStartEvent - GridDropEvent - ItemClickEvent - ItemDoubleClickEvent - SortEvent - dataGenerator - dragFilter - dropFilter enterPressHandler - itemSelectableProvider - tooltipGenerator |
XML 内部元素 |
actions - columns - column - contextMenu - emptyStateComponent |
基本用法
创建该组件时,在 XML 中声明一个 dataGrid 元素,并与一个 数据容器 绑定。组件支持集合数据容器和键值对数据容器。然后,设置需要在表格中展示的属性:
下面的示例演示了 dataGrid 的基本用法:
<data readOnly="true">
<collection id="usersDc"
class="com.company.onboarding.entity.User">
<fetchPlan extends="_base">
<property name="department">
<property name="name"/> (1)
</property>
</fetchPlan>
<loader id="usersDl">
<query>
<![CDATA[select e from User e order by e.username]]>
</query>
</loader>
</collection>
</data>
<layout>
<dataGrid id="usersTable"
width="100%"
minHeight="20em"
dataContainer="usersDc">
<columns>
<column property="username"/> (2)
<column property="firstName"/>
<column property="lastName"/>
<column property="active"/>
<column property="onboardingStatus"/>
<column property="department.name"/> (3)
</columns>
</dataGrid>
</layout>
| 1 | 引用属性。 |
| 2 | 显示根实体属性的列。 |
| 3 | 显示引用实体属性的列,使用点(".")访问引用属性。 |
数据绑定
键值对容器
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>
编程式绑定
如需以编程的方式在视图控制器定义数据容器,则需要设置 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));
}
编程方式添加列
addColumn() 和 addComponentColumn() 支持在 Java 中创建列。
addColumn() 与渲染器一起使用,例如 本地日期渲染器 中展示的 LocalDateRenderer。当单元格需要包含 UI 组件时,请使用 addComponentColumn()。
以下示例使用 addComponentColumn() 添加一列,用于显示用户的头像:
import com.company.onboarding.entity.User;
import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.component.html.Image;
import com.vaadin.flow.component.html.Span;
import com.vaadin.flow.server.streams.DownloadHandler;
import com.vaadin.flow.server.streams.DownloadResponse;
import com.vaadin.flow.server.streams.InputStreamDownloadHandler;
import io.jmix.core.FileRef;
import io.jmix.core.FileStorageLocator;
import io.jmix.flowui.UiComponents;
import io.jmix.flowui.component.grid.DataGrid;
import java.io.InputStream;
// class declaration and annotations omitted
@ViewComponent
private DataGrid<User> usersDtGr;
@Autowired
private UiComponents uiComponents;
@Autowired
private FileStorageLocator fileStorageLocator;
@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");
InputStreamDownloadHandler handler = (3)
DownloadHandler.fromInputStream(e -> {
InputStream inputStream = fileStorageLocator.getByName(
fileRef.getStorageName()).openStream(fileRef);
return new DownloadResponse(
inputStream, fileRef.getFileName(), fileRef.getContentType(), -1);
});
image.setSrc(handler); (4)
return image; (5)
} else {
return new Span();
}
})
.setHeader("Picture")
.setAutoWidth(true)
.setFlexGrow(0);
usersDtGr.setColumnPosition(pictureColumn,0);
}
| 1 | 添加一列,用于显示 image 组件。 |
| 2 | 使用 UiComponents 工厂创建 Image 组件实例。 |
| 3 | 创建一个 DownloadHandler,用于打开 User 实体的 picture 属性所指向的文件流。 |
| 4 | 将 DownloadHandler 设置为 Image 组件的源。 |
| 5 | addComponentColumn() 方法返回需要显示的组件。 |
多选模式
默认情况下,组件在单选模式运行。当设置 selectionMode 为 MULTI 时,会在第一列显示一个复选框列,支持选择多行。如果需要选中全部行,可以点击表头的复选框。
| 使用 Shift + click 进行范围选择或其他选择的快捷键暂不支持。 |
行内编辑
|
行内编辑器仅在内存中更新实体的属性值。如果需要将修改内容保存至数据库,请使用下面的方法: |
操作列
使用 editorActionsColumn 在每行中提供 Edit 和 Close 按钮。用户可以使用这些按钮开始或结束编辑。
操作列会添加在所有列的后面,如果使用了 includeAll="true",操作列可能会在很远的最右边。
|
显示代码
<dataGrid width="100%" dataContainer="usersDc" id="editableUsersTable">
<columns>
<column property="username"/>
<column property="firstName" editable="true"/> (1)
<column property="lastName" editable="true"/>
<column property="active" editable="true"/>
<column property="onboardingStatus"/>
<editorActionsColumn>
<editButton text="Edit" icon="PENCIL"/> (2)
<closeButton text="Close" icon="CLOSE"/> (3)
</editorActionsColumn>
</columns>
</dataGrid>
| 1 | 允许某些列 可编辑。 |
| 2 | 开始编辑的按钮。 |
| 3 | 完成编辑的按钮。 |
缓存模式
当 缓存模式 开启时,用户需要显式的确认或取消正在编辑的内容,以防止误操作。点击不同的表格行,会自动取消编辑。
显示代码
<dataGrid width="100%" dataContainer="usersDc" editorBuffered="true"> (1)
<columns>
<column property="username"/>
<column property="firstName" editable="true"/> (2)
<column property="lastName" editable="true"/>
<column property="active" editable="true"/>
<column property="onboardingStatus"/>
<editorActionsColumn>
<editButton text="Edit" icon="PENCIL"/> (3)
<saveButton icon="CHECK" themeNames="success"/> (4)
<cancelButton icon="CLOSE" themeNames="error"/> (5)
</editorActionsColumn>
</columns>
</dataGrid>
| 1 | 启用缓存模式。 |
| 2 | 允许某些列 可编辑。 |
| 3 | 开始编辑的按钮。 |
| 4 | 确认编辑的按钮。 |
| 5 | 取消编辑的按钮。 |
双击编辑
有时候通过双击的方式打开行内编辑器更加方便。
显示代码
<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 中的 loader 元素具有 readOnly="true" 属性)。而且,列表视图通常不包含确认操作,如 OK 或 Save。因此,修改后的实体需要显式地保存到数据库。
在非缓存模式时,可以在 EditorCloseEvent 中通过 DataManager 保存:
@ViewComponent
private CollectionContainer<User> usersDc;
@Install(to = "usersDataGrid.@editor", subject = "closeListener")
private void usersDataGridEditorCloseListener(final EditorCloseEvent<User> event) {
User user = event.getItem();
User savedUser = dataManager.save(user); (1)
usersDc.replaceItem(savedUser); (2)
}
| 1 | 保存实体并获取保存后的实例。 |
| 2 | 将数据容器中的旧实例替换为保存后的实例,之后会基于最新的数据做修改。 |
在缓存模式时,可以在 EditorSaveEvent 监听器中完成:
@Install(to = "usersDataGrid.@editor", subject = "saveListener")
private void usersDataGridEditorSaveListener(final EditorSaveEvent<User> event) {
User user = event.getItem();
User savedUser = dataManager.save(user);
usersDc.replaceItem(savedUser);
}
|
如需通过 Jmix Studio 生成监听器注解方法,可以在 Component Inspector 的 Handlers 标签页中生成,这里的编辑器事件监听器带 监听器也可以编程式通过类似 |
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() 方法返回的可视化组件将作为列编辑组件。 |
Header 和 Footer
组件可以包含 header 和 footer 以显示补充信息。补充信息可以是一行或多行,通过下列方法添加:
方法 |
描述 |
|---|---|
|
在 header 块的底部添加一行。 |
|
在 header 块的顶部添加一行。 |
|
在 footer 块的底部添加一行。 |
|
在 footer 块的顶部添加一行。 |
下面的示例中,dataGrid 的 header 显示合并的单元格,而 footer 显示一些计算值:
显示代码
<dataGrid id="dataGrid"
width="100%"
dataContainer="usersDc"
themeNames="column-borders">
<columns>
<column property="firstName"/>
<column property="lastName"/>
<column property="active"/>
</columns>
</dataGrid>
@ViewComponent
private DataGrid<User> dataGrid;
@ViewComponent
private CollectionContainer<User> usersDc;
@ViewComponent
private CollectionLoader<User> usersDl;
@Autowired
private DataManager dataManager;
@Autowired
private UiComponents uiComponents;
@Autowired
private Notifications notifications;
@ViewComponent
private DataContext dataContext;
@Subscribe
protected void onInit(final InitEvent event) {
usersDl.load();
initHeader();
initFooter();
}
protected void initHeader() {
HeaderRow headerRow = dataGrid.prependHeaderRow();
HeaderRow.HeaderCell headerCell = headerRow.join(
dataGrid.getColumnByKey("firstName"),
dataGrid.getColumnByKey("lastName")); (1)
headerCell.setText("Full Name");
}
protected void initFooter() {
FooterRow footerRow = dataGrid.appendFooterRow();
FooterRow.FooterCell activeCell = footerRow.getCell(dataGrid.getColumnByKey("active"));
activeCell.setText(getActiveCount() + "/" + usersDc.getItems().size());
}
protected int getActiveCount() {
int activeCount = 0;
Collection<User> items = dataGrid.getGenericDataView().getItems().toList();
for (User user : items) {
if (user.getActive()) {
activeCount++;
}
}
return activeCount;
}
| 1 | header 中的单元格可以合并。以提供关于列的通用信息。 |
| header 和 footer 都可以包含丰富的内容和组件。参考 在线示例。 |
表头过滤
| 该功能目前是预览状态。其显示以及实现细节可能会在未来的版本中发生重大变化。 |
dataGrid 中的数据可以使用列标题中嵌入的 属性过滤器 进行过滤。
可以通过 XML 中的 filterable 属性定义哪些列可以进行过滤。可过滤的列表头中带有“漏斗”图标()。用户点击图标时,会显示一个包含属性过滤条件的对话框。如果设置了过滤条件,则该列中的图标将高亮显示。
若要确保过滤器图标始终正常显示,请使用 width 或 autoWidth 属性为列设置适当的宽度。最好不要让用户能调整列宽,否则用户有可能调小列宽导致过滤器图标不可见。
示例:
<dataGrid dataContainer="usersDc"
width="100%">
<columns>
<column property="firstName" filterable="true"
width="15em"/>
<column property="lastName" filterable="true"
autoWidth="true"/>
<column property="username"/>
<column property="active"/>
<column property="joiningDate"/>
<column property="onboardingStatus"/>
</columns>
</dataGrid>
表头中的属性过滤器与独立属性过滤器或 genericFilter 通用过滤器 的工作方式相同 - 都是在 数据加载器 中添加查询条件。在标准流程中,过滤条件将转换为 JPQL 查询语句,并在数据库级别筛选数据。
可过滤的列可以与 propertyFilter 和 genericFilter 组件一起使用。所有过滤器的条件都由以 AND 逻辑运算进行组合。
目前,列过滤条件还没有绑定到页面的 URL 中。也就是说,如果用户使用了表头过滤器,然后导航到详情视图并返回,则表头过滤器将被清除。
操作
|
如需在 Jmix Studio 中添加 |
显示代码
<hbox id="buttonsPanel" classNames="buttons-panel"> (1)
<button id="createBtn" action="usersDataGrid.create"/>
<button id="editBtn" action="usersDataGrid.edit"/>
<button id="removeBtn" action="usersDataGrid.remove"/>
<button id="infoBtn" action="usersDataGrid.getInfo"/>
</hbox>
<dataGrid width="100%" dataContainer="usersDc" id="usersDataGrid">
<columns>
<column property="username"/>
<column property="firstName"/>
<column property="lastName"/>
<column property="active"/>
<column property="onboardingStatus"/>
<column property="joiningDate"/>
</columns>
<actions>
<action id="create" type="list_create"/> (2)
<action id="edit" type="list_edit"/>
<action id="remove" type="list_remove"/>
<action id="getInfo" text="Get Info"/> (3)
</actions>
</dataGrid>
| 1 | 定义一个 hbox 包含操作按钮。 |
| 2 | 定义 list_create 标准操作。 |
| 3 | 定义 getInfo 自定义操作。其 text 属性的值用于在右键菜单中显示该操作,并作为按钮的标题。 |
右键菜单
右键菜单提供了另一种访问 操作 的方式。每个操作显示为一个菜单项。
使用 contextMenu 元素可以优化菜单项列表,使用分隔符或层次结构进行组织。
显示代码
<dataGrid width="100%" dataContainer="usersDc" id="menuDataGrid">
<columns>
<column property="username"/>
<column property="firstName"/>
<column property="lastName"/>
<column property="active"/>
<column property="onboardingStatus"/>
<column property="joiningDate"/>
</columns>
<actions showInContextMenuEnabled="false"> (1)
<action id="create" type="list_create"/>
<action id="edit" type="list_edit"/>
<action id="remove" type="list_remove"/>
<action id="getInfo" text="View"/>
<action id="sendMessage" text="Message"/>
</actions>
<contextMenu id="contextMenu">
<item action="menuDataGrid.getInfo" icon="INFO_CIRCLE"/> (2)
<separator/>
<item text="Contact" icon="USER"> (3)
<item action="menuDataGrid.sendMessage" icon="COMMENT"/>
<item id="emailItem"/> (4)
</item>
<separator/>
<item action="menuDataGrid.remove" icon="TRASH"/>
</contextMenu>
</dataGrid>
| 1 | 隐藏菜单中所有的操作。或者使用 visible 属性隐藏特定操作。 |
| 2 | 为需要的操作显式添加菜单项。 |
| 3 | 菜单项互相包含以创建层级结构。 |
| 4 | 通过 dynamicContentHandler 动态添加的菜单项内容。 |
@ViewComponent
private GridMenuItem<Object> emailItem;
@Install(to = "contextMenu", subject = "dynamicContentHandler")
private boolean contextMenuDynamicContentHandler(final User user) {
if (user == null) {
return false;
}
emailItem.setText("Email: " + user.getEmail());
return true;
}
渲染器
渲染器用于自定义列单元格的显示方式。本节介绍 dataGrid 的多种列渲染器。通用的渲染器 API,如 ComponentRenderer、LitRenderer 和 FragmentRenderer,在 渲染器 中介绍。
渲染器可以通过以下几种方式定义:
-
在 XML 中声明式定义:在
column元素内部使用预定义渲染器,例如numberRenderer、localDateRenderer、localDateTimeRenderer、detailLinkRenderer和detailButtonRenderer。 -
使用
@Supply注解:在视图控制器中提供自定义渲染器。 -
使用
fragmentRenderer:使用基于 fragment 的渲染器机制,详见 Fragment 渲染器。 -
使用
addColumn()和addComponentColumn():在 Java 中创建列并以编程方式配置渲染器。请参见 编程方式添加列。
|
如需在 Jmix Studio 中添加渲染器,请在视图的 XML 或 Jmix UI 的结构面板中选择 |
本地日期渲染器
在列单元格中渲染 LocalDate 值。
<column property="joiningDate">
<localDateRenderer format="MMM dd, yyyy"/>
</column>
使用 format 属性定义输出格式。可选的 nullRepresentation 属性可以指定 null 值的显示方式。
对于 Java 代码配置,使用 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");
}
本地日期时间渲染器
在列单元格中渲染 LocalDateTime 值。
<column property="passwordExpiration">
<localDateTimeRenderer format="dd/MM/YYYY HH:mm:ss"/>
</column>
使用 format 属性定义输出格式。可选的 nullRepresentation 属性可以指定 null 值的显示方式。
数字渲染器
在列单元格中渲染数字值。
<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 代码配置渲染器,并使用 DetailLinkRenderer.withHrefProvider() 提供自定义 URL。
详情按钮渲染器
渲染一个按钮,用于打开当前实体的详情视图。
<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用于配置样式。
仅含 key 的列中的详情渲染器
可以在使用 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。
文本渲染器
渲染 Java 代码提供的纯文本。
<column key="status" header="Status"/>
@Supply(to = "userStepsDataGrid.status", subject = "renderer")
private Renderer<UserStep> userStepsDataGridStatusRenderer() {
return new TextRenderer<>(userStep ->
isOverdue(userStep) ? "Overdue!" : "");
}
该渲染器通常通过 renderer 处理方法或 @Supply 注解在 Java 代码提供。
组件渲染器
在单元格内渲染自定义组件。当单元格需要包含丰富或可交互的内容(复选框、图片、按钮等)时,请使用该渲染器。
| 参阅 组件渲染器 了解可用的构造器、通用行为以及性能。 |
下面的示例根据是否设置了 completedDate ,在 completed 列中呈现一个只读复选框。
<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;
});
}
条目详情
组件中的行可以展开,以显示每行的附加信息。通过 setItemDetailsRenderer() 方法可以实现该功能。方法使用一个 ComponentRenderer 作为参数,提供内容的渲染方式。
detailsVisibleOnClick 属性可以设置当点击特定行时,是否显示详细信息。如果需要通过其他事件触发详情展示,可以设置该属性为 false。
排序
本节演示了针对单个 dataGrid 组件的自定义排序。关于应用程序范围的自定义排序,请参见 排序。
|
任何列都可用于对数据进行排序。单击列标题可激活表格中的特殊控件,该控件显示当前正在排序的列以及排序的顺序(升序或降序)。随后每次单击同一标题都可以切换排序的顺序。
|
枚举 类型的属性按 因此,在 |
多列排序
也可以同时按多列进行排序。按多列排序时,第一个选定的列确定主要排序条件,第二列设置次要排序条件,依此类推。可以使用 multiSort、multiSortOnShiftClickOnly 和 multiSortPriority 属性配置多列排序。
示例:
<dataGrid width="100%"
dataContainer="usersDc"
multiSort="true"
multiSortOnShiftClickOnly="true"
multiSortPriority="APPEND">
列比较器
使用 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)));
}
排序 Builder 代理
如果表格从数据库重新加载数据,需要配置排序 builder 代理。该代理从表格接收排序指令,并返回一个 DataGridSort 对象,支持在同一处代码中覆盖内存排序、数据库排序。
以下示例自定义排序,使 Department.num 值按数值而非字符串进行比较:
<dataGrid id="delegateDepartmentsDataGrid"
width="100%"
dataContainer="delegateDepartmentsDc">
<columns>
<column property="name"/>
<column property="num"/>
</columns>
</dataGrid>
private void initBoundPropertySortBuilder() {
delegateDepartmentsDataGrid.setSortBuilderDelegate(sortContext ->
DataGridSortBuilder.create(sortContext)
.replaceSort("num", "CAST({E}.num BIGINT)", (1)
Comparator.comparing(
department -> department.getNum() == null
? null
: Integer.valueOf(department.getNum()),
Comparator.nullsFirst(Integer::compareTo))) (2)
.build());
}
| 1 | 如果表格从数据库重新加载数据,CAST({E}.num BIGINT) 表达式会被添加到 ORDER BY 中,这样数据库也会按数值排序,而非按字典顺序。 |
| 2 | 如果表格在内存中排序,比较器将 num 转换为 Integer 并比较数值。 |
计算列排序
排序 builder 代理对于未绑定实体属性的列也有效。下面的示例中,fullName 列由 firstName 和 lastName 渲染生成,排序 builder 代理将该合成列映射至 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 可以对行内值进行聚合。当启用聚合时,会显示一个附加行,包含聚合值。示例:
<dataGrid width="100%"
dataContainer="stepsDc"
aggregatable="true"
aggregationPosition="TOP"> (1)
<columns>
<column property="name"/>
<column property="duration">
<aggregation type="AVG"
cellTitle="msg://aggregation.cellTitle"/> (2)
</column>
<column property="sortValue"/>
<column property="factor">
<aggregation type="AVG">
<formatter>
<number format="#,##0.00"/> (3)
</formatter>
</aggregation>
</column>
</columns>
</dataGrid>
| 1 | 设置 aggregatable 属性为 true。 |
| 2 | 为需要进行聚合运算的列设置 aggregation 元素。还可以使用 cellTitle 属性配置用户鼠标悬浮在聚合值上时显示的提示信息。 |
| 3 | 指定 formatter 可以为聚合值设置不同于标准数据类型格式的其他展示格式。 |
aggregation 元素还可以通过 strategyClass 属性设置一个用于自定义聚合计算的类:
<dataGrid width="100%"
dataContainer="usersDc"
aggregatable="true"
aggregationPosition="TOP">
<columns>
<column property="firstName"/>
<column property="lastName"/>
<column property="onboardingStatus">
<aggregation
strategyClass="com.company.onboarding.view.component.datagrid.DataGridUserStatusAggregation"/>
</column>
</columns>
</dataGrid>
该类需要实现 AggregationStrategy 接口。示例:
public class DataGridUserStatusAggregation implements AggregationStrategy<OnboardingStatus, String> {
@Autowired
public Messages messages;
@Override
public String aggregate(Collection<OnboardingStatus> propertyValues) {
OnboardingStatus mostFrequent = null;
long max = 0;
if (CollectionUtils.isNotEmpty(propertyValues)) {
for (OnboardingStatus status : OnboardingStatus.values()) {
long current = propertyValues.stream()
.filter(status :: equals)
.count();
if (current > max) {
mostFrequent = status;
max = current;
}
}
}
if (mostFrequent != null) {
String key = OnboardingStatus.class.getSimpleName() + "." + mostFrequent.name();
return String.format("%s: %d/%d", messages.getMessage(OnboardingStatus.class, key), max, propertyValues.size());
}
return null;
}
@Override
public Class<String> getResultClass() {
return String.class;
}
}
空状态
当数据网格没有数据可供展示时,表格中间的数据显示区默认为空。此时可以利用空状态的功能显示一条消息或 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>该属性的优先级高于在 Java 中通过 setEmptyStateComponent(Component)或 XML 中通过emptyStateComponent设置的空状态元素。即,设置emptyStateText会自动移除所有已经存在的组件。 -
使用
emptyStateComponent元素定义空状态显示的组件。<dataGrid id="dataGridEmptyStateComp" dataContainer="customersDc" width="100%"> <columns> <column property="firstName"/> <column property="lastName"/> <column property="age"/> <column property="martialStatus"/> </columns> <emptyStateComponent> <button id="newCustomerBtn" text="Create customer" width="100%"/> </emptyStateComponent> </dataGrid>一旦设置了
emptyStateComponent后,就可以与这些空状态的元素进行交互。比如自定义组件的样式或创建组件的事件监听器。设置该元素会覆盖在视图控制器中通过 setEmptyStateText(String)方法或在 XML 中通过emptyStateText定义的空状态文字。
样式版本
使用 themeNames 属性设置组件的样式。
| 样式 | 描述 | 支持的主题 |
|---|---|---|
|
在列之间添加垂直边框 |
Aura, Lumo |
|
减小行高和间距 |
Lumo |
|
移除表格的外边框 |
Aura, Lumo |
|
移除行之间的水平边框 |
Aura, Lumo |
|
交替行背景色以提升可读性 |
Aura, Lumo |
|
允许单元格内容换行显示,而不是截断 |
Aura, Lumo |
XML 属性
通用属性 对所有组件都是一样的配置。
下面是 dataGrid 的特殊属性:
名称 |
描述 |
默认值 |
|---|---|---|
如果设置为 如果有大量数据,会有性能问题,此时不推荐使用该功能。 |
|
|
如果设置为 |
|
|
|
||
如果设置为 |
|
|
如果设置为 |
|
|
设置行的拖放模式。支持 |
- |
|
如果设置为 |
|
|
当表格无数据时显示的文本。使用 null 移除当前空状态的内容。参阅 空状态。 |
– |
|
如果设置为 |
|
|
如果设置为 |
|
|
设置点击的列添加到排序列中的位置。支持 |
|
|
设置解析可能包含 |
|
|
设置每页展示的行数,这个数值也是每次从数据提供器中获取的数量。 |
|
|
如果设置为 |
|
|
设置选择模式。支持 |
|
事件和处理器
通用事件和处理器 对所有组件都是一样的配置。
下面是 dataGrid 的特殊事件和处理器:
|
在 Jmix Studio 生成处理器桩代码时,可以使用 Jmix UI 组件面板的 Handlers 标签页或者视图类顶部面板的 Generate Handler 添加,也可以通过 Code → Generate 菜单(Alt+Insert / Cmd+N)生成。 |
名称 |
描述 |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
为数据网格添加数据生成器。如果已经添加,则不做操作。参考 |
|
用于控制特定行是否可拖放,而不是通过 rowsDraggable 设置所有行的拖放。 |
|
用于设置哪些行可以作为拖放的目的地。 |
|
用户按下回车键的处理器。 |
|
设置一个 provider,检查用户是否可以选择或取消表格中的某个特定条目。provider 接收一个条目实例,如果用户可以修改该条目的选择状态,应返回 |
|
按照给定条件为列单元格生成提示窗。参考 在线示例。 |
XML 内部元素
dataGrid 的 XML 内部元素提供很多选项来控制列的外观、行为和功能,既可以集中控制列,也可以单独控制列的功能。
|
如需为选择的组件添加内部元素,可以使用 Jmix UI 组件属性面板中的 Add 按钮添加。 |
columns
columns 元素可以设置一组需要展示的属性,并设置所有列的行为。
XML 元素 |
|
|---|---|
属性 |
exclude - includeAll - resizable - sortable |
XML 内部元素 |
名称 |
描述 |
默认值 |
|---|---|---|
排除特定的属性卡不予展示。多个属性需要使用逗号分隔。示例: |
- |
|
如果为 |
- |
|
如果设置为 |
|
|
如果设置为 |
|
column
column 元素定义一个单独的列。为单独列设置的属性会覆盖为所有列设置的属性。
XML 元素 |
|
|---|---|
属性 |
autowidth - editable - filterable - flexGrow - footer - frozen - header - key - property - resizable - sortable - textAlign - visible - width |
事件和处理器 |
AttachEvent - DataGridColumnVisibilityChangedEvent - DetachEvent - partNameGenerator - renderer - tooltipGenerator |
XML 内部元素 |
Aggregation - FragmentRenderer - LocalDateRenderer - LocalDateTimeRenderer - NumberRenderer - DetailLinkRenderer - DetailButtonRenderer |
名称 |
描述 |
默认值 |
|---|---|---|
如果设置为 |
|
|
如果设置为 |
|
|
如果设置为 |
|
|
设置列的 flex grow 比例。当设置为 |
|
|
为列设置一个 footer 文本。该属性可以是文本本身或者 消息包 中的一个键值。如果是消息包键值,则必须以 |
- |
|
如果设置为 |
|
|
为列设置一个 header 文本。该属性可以是文本本身或者 消息包 中的一个键值。如果是消息包键值,则必须以 |
- |
|
设置用户定义的列映射 id。这个键值可以用来通过 键值必须在同一个数据网格中唯一,设定后不能更改。 |
- |
|
指定列中需要显示的实体属性。可以是根实体的属性 |
- |
|
如果设置为 |
|
|
如果设置为 |
|
|
设置文本的对其方式,支持 |
|
|
如果设置为 |
|
|
用 CSS 字符串设置列宽。 |
- |
名称 |
描述 |
|---|---|
通过 gridColumnVisibility 列可见性 组件修改列可见性时发送。 |
|
按照给定条件为列生成 CSS 类的 part。此功能可以根据展示的数据修改单元格的外观。参考 在线示例。 |
|
按照给定条件为列生成提示窗。参考 在线示例。 |
contextMenu
contextMenu 元素可以使用与其默认排列方式不同的方式组织右键菜单的条目。请参阅 示例。
XML 元素 |
|
|---|---|
属性 |
id - classNames - css - enabled - visible |
事件和处理器 |
AttachEvent - DetachEvent - GridContextMenuOpenedEvent - openedChangeEvent - dynamicContentHandler |
XML 内部元素 |
item - separator |
名称 |
描述 |
|---|---|
当右键菜单的打开状态变化时触发。可以返回目标菜单项本身或 id,能根据点击事件调整菜单项的展示。 |
|
当右键菜单的打开状态变化时触发。 |
|
在菜单打开时处理菜单的动态更新,例如添加菜单项或其内容。请参阅 示例。 |
emptyStateComponent
emptyStateComponent 可以定义表格空状态时显示的组件。使用 null 移除当前空状态的内容。参阅 空状态。