3.5.2.1.43. 表格
Table
组件以表格的方式展示信息,对数据进行排序 、管理表格列和表头,并对选中的行执行操作。
组件对应的 XML 名称: table
在界面 XML 描述中定义组件的示例:
<data readOnly="true">
<collection id="ordersDc" class="com.company.sales.entity.Order" view="order-with-customer">
<loader id="ordersDl">
<query>
<![CDATA[select e from sales_Order e]]>
</query>
</loader>
</collection>
</data>
<layout>
<table id="ordersTable" dataContainer="ordersDc" width="100%">
<columns>
<column id="date"/>
<column id="amount"/>
<column id="customer"/>
</columns>
<rowsCount/>
</table>
</layout>
在上面的示例中,data
元素定义集合数据容器,它使用 JPQL 查询 Order
实体。table
元素定义数据容器,而 columns
元素定义哪些实体属性用作表格列。
table
元素:
rows
– 如果使用 datasource 属性来做数据绑定,则必须设置此元素。
每行可以在左侧的附加列中有一个图标。在界面控制器中创建 ListComponent.IconProvider
接口的实现,并将其设置给表格:
@Inject
private Table<Customer> table;
@Subscribe
protected void onInit(InitEvent event) {
table.setIconProvider(new ListComponent.IconProvider<Customer>() {
@Nullable
@Override
public String getItemIcon(Customer entity) {
CustomerGrade grade = entity.getGrade();
switch (grade) {
case PREMIUM: return "icons/premium_grade.png";
case HIGH: return "icons/high_grade.png";
case MEDIUM: return "icons/medium_grade.png";
default: return null;
}
}
});
}
columns
– 定义表格列的必须元素。
每个列都在嵌套的 column
元素中描述,column
元素具有以下属性:
id
− 必须属性,包含列中要显示的实体属性的名称。可以是来自数据容器的实体的属性,也可以是关联实体的属性(使用 "." 来指定属性在对象关系图中的路径)。例如:
<columns>
<column id="date"/>
<column id="customer"/>
<column id="customer.name"/>
<column id="customer.address.country"/>
</columns>
caption
− 包含列标题的可选属性。如果不指定,将显示本地化属性名称。
collapsed
− 可选属性;当设置为true
时默认隐藏列。当表格的columnControlVisible
属性不是false
时,用户可以通过表格右上角的菜单中的按钮 控制列的可见性。默认情况下,collapsed
是false
。
width
− 可选属性,控制默认列宽。只能是以像素为单位的数值。
align
− 可选属性,用于设置单元格的文本对齐方式。可选值:LEFT
、RIGHT
、CENTER
。默认为LEFT
。
editable
− 可选属性,允许编辑表中的相应列。为了使列可编辑,整个表的 editable 属性也应设置为true
。不支持在运行时更改此属性。
sortable
− 可选属性,用于禁用列的排序。整个表的 sortable 属性为true
此属性有效(默认为true
)。
maxTextLength
– 可选属性,允许限制单元格中的字符数。如果实际值和最大允许字符数之间的差异不超过 10 个字符,则多出来的字符不会被隐藏。用户可以点击可见部分来查看完整的文本。例如一列的字符数限制为 10 个字符:
link
- 如果设置为true
,则允许列中显示指向实体编辑器的链接。对于基本类型的列,link
属性也可以设置为 true; 在这种情况下,将打开主实体编辑器。这个方法可用于简化导航:用户能够通过单击一些关键属性快速地打开实体编辑器。
linkScreen
- 设置单击link
属性为true
的列中的链接时打开的界面的标识符。
linkScreenOpenType
- 设置界面打开模式(THIS_TAB
、NEW_TAB
或者DIALOG
)。
linkInvoke
- 单击链接时调用控制器方法而不是打开界面。
@Inject
private Notifications notifications;
public void linkedMethod(Entity item, String columnId) {
Customer customer = (Customer) item;
notifications.create()
.withCaption(customer.getName())
.show();
}
captionProperty
- 指定一个要显示在列中的实体属性名称,而不是显示 id 指定的实体属性值。例如,如果有一个包含name
和orderNo
属性的实体Priority
,则可以定义以下列:
<column id="priority.orderNo" captionProperty="priority.name" caption="msg://priority" />
此时,列中将会显示 Priority
实体的 name
属性,但是列的排序是根据 Priority
实体的 orderNo
属性。
- 可选的
generator
属性包含指向界面控制器中方法,该方法可创建一个可视化组件显示在表格单元格中:
<columns>
<column id="name"/>
<column id="imageFile"
generator="generateImageFileCell"/>
</columns>
public Component generateImageFileCell(Employee entity){
Image image = uiComponents.create(Image.NAME);
image.setSource(FileDescriptorResource.class).setFileDescriptor(entity.getImageFile());
return image;
}
它可以用来为 addGeneratedColumn() 方法提供一个 Table.ColumnGenerator
的实现
<column id="date">
<formatter class="com.haulmont.cuba.gui.components.formatters.DateFormatter"
format="yyyy-MM-dd HH:mm:ss"
useUserTimezone="true"/>
</column>
rowsCount
− 可选元素,为表格添加RowsCount
组件;此组件能够分页加载表格数据。可以使用数据加载器的setMaxResults()
方法限制数据容器中的记录数来定义分页的大小。这个方法通常是由链接到表格数据加载器的过滤器组件来执行的。如果表格没有通用过滤器,则可以直接从界面控制器调用此方法。
RowsCount
组件还可以显示当前数据容器查询的记录总数,而无需提取这些记录。当用户单击 ? 图标时,它会调用 com.haulmont.cuba.core.global.DataManager#getCount
方法,执行与当前查询条件相同的数据库查询,不过会使用 COUNT()
聚合函数代替查询列。然后显示检索到的数字,代替 *? 图标。
actions
− 可选元素,用于描述和表格相关的操作。除了自定义的操作外,该元素还支持以下在com.haulmont.cuba.gui.actions.list
里定义标准操作:create
、edit
、remove
、refresh
、add
、exclude
、excel
。
- 可选元素,在表格上方添加一个 ButtonsPanel 容器来显示操作按钮。
table
属性:
multiselect
属性可以为表格行设置多选模式。如果multiselect
为true
,用户可以按住 Ctrl 或 Shift 键在表格中选择多行。默认情况下关闭多选模式。
sortable
属性可以对表中的数据进行排序。默认情况下,它设置为true
。如果允许排序,单击列标题在列名称右侧将显示图标 / 。可以使用sortable属性禁用特定列的排序。
根据是否将所有记录放在了一页上来使用不同的方式进行排序。如果所有记录在一页,则在内存中执行排序而不需要数据库查询。如果数据有多页,则通过发送具有相应 ORDER BY
条件的新的查询请求在数据库中执行排序。
一个表格中的列可能包含本地属性或实体链接。例如:
<table id="ordersTable"
dataContainer="ordersDc">
<columns>
<column id="customer.name"/> <!-- the 'name' attribute of the 'Customer' entity -->
<column id="contract"/> <!-- the 'Contract' entity -->
</columns>
</table>
在后一种情况下,数据排序是根据关联实体的 @NamePattern
注解中定义的属性进行的。如果实体中没有这个注解,则仅仅在内存中对当前页的数据进行排序。
如果列引用了非持久化实体属性,则数据排序将根据 @MetaProperty
注解的 related()
参数中定义的属性执行。如果未指定相关属性,则仅仅在内存中对当前页的数据进行排序。
如果表格链接到一个嵌套的属性容器,这个属性容器包含相关实体的集合。这个集合属性必须是有序类型(List
或 LinkedHashSet
)才能使表格支持排序。如果属性的类型为 Set
,则 sortable
属性不起作用,并且用户无法对表格进行排序。
presentations
属性控制展示设置。默认情况下,该值为false
。如果属性值为true
,则会在表格的右上角添加相应的图标 。
- 如果
columnControlVisible
属性设置为false
,则用户无法使用位于表头的右侧的下拉菜单按钮 隐藏列: 按钮位于表头的右侧。当前显示的列在菜单中标记为选中状态。
- 如果
reorderingAllowed
属性设置为false
,则用户不能通过用鼠标拖动来更改列顺序。
- 如果
columnHeaderVisible
属性设置为false
,则该表没有列标题。
- 如果
showSelection
属性设置为false
,则不突出显示当前行。
contextMenuEnabled
属性启用右键菜单。默认情况下,此属性设置为true
。右键菜单中会列出表格操作(如果有的话)和 System Information 菜单项(如果用户具有cuba.gui.showInfo
权限),通过 System Information 菜单项可查看选中实体的详细信息。
- 将
multiLineCells
设置为true
可以让包含多行文本的单元格显示多行文本。在这种模式下,浏览器会一次加载表格中当前页的所有行,而不是延迟加载表格的可见部分。这就要求在 Web 客户端中适当的滚动。默认值为“false”。
aggregatable
属性启用表格行的聚合运算。支持以下操作:
SUM
– 计算总和AVG
– 计算平均值COUNT
– 计算总数MIN
– 找到最小值MAX
– 找到最大值
聚合列的 aggregation
元素应该设置 type
属性,在这个属性中设置聚合函数。默认情况下,聚合列仅支持数值类型,例如 Integer 、 Double 、 Long
和 BigDecimal
。聚合表格值显示在表格顶部的附加行中。这是一个定义聚合表示例:
<table id="itemsTable" aggregatable="true" dataContainer="itemsDc">
<columns>
<column id="product"/>
<column id="quantity"/>
<column id="amount">
<aggregation type="SUM"/>
</column>
</columns>
</table>
aggregation
元素还可以包含 strategyClass
属性,指定一个实现 AggregationStrategy
接口的类(参阅下面以编程方式设置聚合策略的示例)。
可以指定不同于 Datatype 标准格式的格式化器显示聚合值:
<column id="amount">
<aggregation type="SUM">
<formatter class="com.company.sample.MyFormatter"/>
</aggregation>
</column>
aggregationStyle
属性允许指定聚合行的位置:TOP
或 BOTTOM
。默认情况下使用 TOP
。
除了上面列出的操作之外,还可以自定义聚合策略,通过实现 AggregationStrategy
接口并将其传递给 AggregationInfo
实例中 Table.Column
类的 setAggregation()
方法。例如:
public class TimeEntryAggregation implements AggregationStrategy<List<TimeEntry>, String> {
@Override
public String aggregate(Collection<List<TimeEntry>> propertyValues) {
HoursAndMinutes total = new HoursAndMinutes();
for (List<TimeEntry> list : propertyValues) {
for (TimeEntry timeEntry : list) {
total.add(HoursAndMinutes.fromTimeEntry(timeEntry));
}
}
return StringFormatHelper.getTotalDayAggregationString(total);
}
@Override
public Class<String> getResultClass() {
return String.class;
}
}
AggregationInfo info = new AggregationInfo();
info.setPropertyPath(metaPropertyPath);
info.setStrategy(new TimeEntryAggregation());
Table.Column column = weeklyReportsTable.getColumn(columnId);
column.setAggregation(info);
editable
属性可以将表格转换为即时编辑模式。在这种模式下,具有editable = true
属性的列显示用于编辑相应实体属性的组件。
根据相应实体属性的类型自动选择每个可编辑列的组件类型。例如,对于字符串和数字属性,应用程序将使用 TextField;对于 Date
将使用 DateField;对于列表将使用 LookupField;对于指向其它实体的链接将使用 PickerField。
对于 Date
类型的可编辑列,还可以定义 dateFormat
或 resolution
属性,类似于为 DateField 的属性。
可以为显示链接实体的可编辑列定义 optionsContainer 和 captionProperty 属性。如果设置了 optionsContainer
属性,应用程序将使用 LookupField 而不是 PickerField。
可以使用 Table.addGeneratedColumn()
方法实现单元格的自定义配置(包括编辑) - 见下文。
- 在具有基于 Halo-based 主题的 Web 客户端中,
stylename
属性可以在 XML 描述中或者界面控制器中为Table
组件设置预定义样式:
<table id="table"
dataContainer="itemsDc"
stylename="no-stripes">
<columns>
<column id="product"/>
<column id="quantity"/>
</columns>
</table>
当以编程方式设置样式时,需要选择 HaloTheme
类的一个以 TABLE_
为前缀的常量:
table.setStyleName(HaloTheme.TABLE_NO_STRIPES);
表格样式:
borderless
- 不显示表格的外部边线。
compact
- 减少表格单元格内的空白区域。
no-header
- 隐藏表格的列标题。
no-horizontal-lines
- 删除行之间的水平分隔线。
no-stripes
- 删除交替的行颜色。
no-vertical-lines
- 删除列之间的垂直分隔线。
small
- 使用小字体并减少表格单元格内的空白区域。
Table
接口的方法:
- 可以使用
addColumnCollapsedListener
方法和ColumnCollapsedListener
接口的实现跟踪列的可视化状态。
getSelected()
、getSingleSelected()
返回表格中的选定行对应的实体实例。可以通过调用getSelected()
方法来获得集合。如果未选择任何内容,则程序将返回空集。如果禁用了multiselect
,应该使用getSingleSelected()
方法返回一个选定实体,如果没有选择任何内容则返回null
。
addSelectionListener()
可以跟踪表格选中行的变化,示例:
customersTable.addSelectionListener(customerSelectionEvent ->
notifications.create()
.withCaption("You selected " + customerSelectionEvent.getSelected().size() + " customers")
.show());
也可以通过订阅相应的事件来跟踪选中行的变化:
@Subscribe("customersTable")
protected void onCustomersTableSelection(Table.SelectionEvent<Customer> event) {
notifications.create()
.withCaption("You selected " + customerSelectionEvent.getSelected().size() + " customers")
.show();
}
可以使用isUserOriginated() 方法跟踪 SelectionEvent
事件的来源。
addGeneratedColumn()
方法允许在列中自定义数据的表现方式。它需要两个参数:列的标识符和Table.ColumnGenerator
接口的实现。如果标识符可以匹配 XML 描述中为表格列设置的标识符 - 在这种情况下,插入新列代替 XML 中定义的列。如果标识符与任何列都不匹配,则会在右侧添加新列。
对于表的每一行将调用 Table.ColumnGenerator
接口的 generateCell()
方法。该方法接受在相应行中显示的实体实例作为参数。generateCell()
方法应该返回一个可视化组件,该组件将显示在单元格中。
使用组件的示例:
@Inject
private GroupTable<Car> carsTable;
@Inject
private CollectionContainer<Car> carsDc;
@Inject
private CollectionContainer<Color> colorsDc;
@Inject
private UiComponents uiComponents;
@Inject
private Actions actions;
@Subscribe
protected void onInit(InitEvent event) {
carsTable.addGeneratedColumn("color", entity -> {
LookupPickerField<Color> field = uiComponents.create(LookupPickerField.NAME);
field.setValueSource(new ContainerValueSource<>(carsDc, "color"));
field.setOptions(new ContainerOptions<>(colorsDc));
field.addAction(actions.create(LookupAction.class));
field.addAction(actions.create(OpenAction.class));
return field;
});
}
在上面的示例中,表中 color
列中的所有单元格都显示了 LookupPickerField 组件。组件应将它的值保存到相应的行中的实体的 color
属性中。
如果要显示动态文本,请使用特殊类 Table.PlainTextCell
而不是 Label 组件。它将简化渲染过程并使表格运行更快。
如果 addGeneratedColumn()
方法接收到的参数是未在 XML 描述中声明的列的标识符,则新列的标题将设置如下:
carsTable.getColumn("colour").setCaption("Colour");
还可以考虑使用 XML 的 generator 属性做更具声明性的设置方案。
requestFocus()
方法允许将焦点设置在某一行的具体的可编辑字段上。需要两个参数:表示行的实体实例和列的标识符。请求焦点的示例如下:
table.requestFocus(item, "count");
scrollTo()
方法允许将表格滚动到具体行。需要一个参数:表示行的实体实例。
滚动条的示例:
table.scrollTo(item);
- 如果需要在单元格中显示自定义内容并且在用户单击单元格的时候能收到通知,可以使用
setClickListener()
方法实现这些功能。CellClickListener
接口的实现接收选中实体和列标识符作为参数。这些单元格的内容将被包装在一个 span 元素中,这个 span 元素带有cuba-table-clickable-cell
样式,可以利用该样式来定义单元格外观。
使用 CellClickListener
的示例:
@Inject
private Table<Customer> customersTable;
@Inject
private Notifications notifications;
@Subscribe
protected void onInit(InitEvent event) {
customersTable.setCellClickListener("name", customerCellClickEvent ->
notifications.create()
.withCaption(customerCellClickEvent.getItem().getName())
.show());
}
setStyleProvider()
方法可以设置表格单元格显示样式。该方法接受Table.StyleProvider
接口的实现类作为参数。表格的每一行和每个单元分别调用这个接口的getStyleName()
方法。如果某一行调用该方法,则第一个参数包含该行显示的实体实例,第二个参数为null
。如果单元格调用该方法,则第二个参数包含单元格显示的属性的名称。
设置样式的示例:
@Inject
protected Table customersTable;
@Subscribe
protected void onInit(InitEvent event) {
customersTable.setStyleProvider((customer, property) -> {
if (property == null) {
// style for row
if (hasComplaints(customer)) {
return "unsatisfied-customer";
}
} else if (property.equals("grade")) {
// style for column "grade"
switch (customer.getGrade()) {
case PREMIUM: return "premium-grade";
case HIGH: return "high-grade";
case MEDIUM: return "medium-grade";
default: return null;
}
}
return null;
});
}
然后应该在应用程序主题中设置的单元格和行样式。有关创建主题的详细信息,请参阅 主题。对于 Web 客户端,新样式在 styles.scss
文件中。在控制器中定义的样式名称,以及表格行和列的前缀标识符构成 CSS 选择器。例如:
.v-table-row.unsatisfied-customer {
font-weight: bold;
}
.v-table-cell-content.premium-grade {
background-color: red;
}
.v-table-cell-content.high-grade {
background-color: green;
}
.v-table-cell-content.medium-grade {
background-color: blue;
}
addPrintable()
当通过excel
标准操作或直接使用ExcelExporter
类导出数据到 XLS 文件时,此方法可以给列中数据设置自定义展现。该方法接收的两个参数为列标识符和为列提供的Table.Printable
接口实现。例如:
ordersTable.addPrintable("customer", new Table.Printable<Customer, String>() {
@Override
public String getValue(Customer customer) {
return "Name: " + customer.getName;
}
});
Table.Printable
接口的 getValue()
方法应该返回在表格单元格中显示的数据。返回的数据不一定是字符串类型,该方法可以返回其它类型的值,比如数字或日期,它们将在 XLS 文件中以相应的类型展示。
如果生成的列需要在输出到 XLS 时带有格式,则应该使用 addGeneratedColumn()
方法,传递一个 Table.PrintableColumnGenerator
接口的实现作为参数。XLS 文档中单元格的值在这个接口的 getValue()
方法中定义:
ordersTable.addGeneratedColumn("product", new Table.PrintableColumnGenerator<Order, String>() {
@Override
public Component generateCell(Order entity) {
Label label = uiComponents.create(Label.NAME);
Product product = order.getProduct();
label.setValue(product.getName() + ", " + product.getCost());
return label;
}
@Override
public String getValue(Order entity) {
Product product = order.getProduct();
return product.getName() + ", " + product.getCost();
}
});
如果没有以某种方式为生成的列定义 Printable
描述,那么该列将显示相应实体属性的值,如果没有关联的实体属性,则不显示任何内容。
setItemClickAction()
方法能够定义一个双击表格行时将执行的操作。如果未定义此操作,表格将尝试按以下顺序在其操作列表中查找适当的操作:
由
shortcut
属性指定给 Enter 键的操作edit
操作view
操作
如果找到此操作,并且操作具有 enabled=true
属性,则执行该操作。
setEnterPressAction()
方法可以定义按下 Enter 键时执行的操作。如果未定义此操作,则表将尝试按以下顺序在其操作列表中查找适当的操作:
由
setItemClickAction()
方法定义的动作由
shortcut
属性指定给 Enter 键的操作edit
操作view
操作
如果找到此操作,并且操作具有 enabled=true
属性,则执行该操作。
- table 的属性
align - aggregatable - aggregationStyle - caption - captionAsHtml - columnControlVisible - columnHeaderVisible - contextHelpText - contextHelpTextHtmlEnabled - contextMenuEnabled - css - dataContainer - description - descriptionAsHtml - editable - enable - box.expandRatio - height - id - multiLineCells - multiselect - presentations - reorderingAllowed - settingsEnabled - showSelection - sortable - stylename - tabIndex - textSelectionEnabled - visible - width- table 的元素
actions - buttonsPanel - columns - rows - rowsCount- column 元素的属性
align - caption - captionProperty - collapsed - dateFormat - editable - generator - id - link - linkInvoke - linkScreen - linkScreenOpenType - maxTextLength - optionsDatasource - resolution - sortable - visible - width- column的元素
aggregation - formatter- aggregation的属性
type - strategyClass- rows的属性
datasource- table 的预定义样式
borderless - compact - no-header - no-horizontal-lines - no-stripes - no-vertical-lines - small- API
addGeneratedColumn - addPrintable - addColumnCollapseListener - addSelectionListener - applySettings - generateCell - getSelected - requestFocus - saveSettings - scrollTo - setClickListener - setEnterPressAction - setItemClickAction - setStyleProvider