我的问题是,在我在第一页上选择了几个项目后,如果我分页到另一个页面并返回,我的初始选择不会显示。我已尝试实施SelectableDataModel
以及使用rowKey
属性,但问题仍然存在。
这是我的测试bean:
@ManagedBean
@ViewScoped
public class MrBean {
private List<Item> chosenItems;
private LazyDataModel lazyModel;
@PostConstruct
public void prepareTest() {
this.lazyModel = new LazyItemDataModel();
}
public void countItems() {
System.out.println("TEST 3: chosenItems's size: " + chosenItems.size());
}
private class LazyItemDataModel extends LazyDataModel<Item> implements SelectableDataModel<Item> {
@Override
public Item getRowData(String rowKey) {
System.out.println("TEST 1: getRowData");
Iterator<Item> iter = ((List<Item>) this.getWrappedData()).iterator();
while (iter.hasNext()) {
Item item = iter.next();
if (item.getId().equals(rowKey)) {
return item;
}
}
return null;
}
@Override
public Object getRowKey(Item item) {
return item.getId();
}
@Override
public List<Item> load(int first, int pageSize, String sortField, SortOrder sortOrder, Map filters) {
System.out.println("TEST 2: load");
// Code to retrieve items from database
}
}
// Getters and Setters
}
这是我的测试页:
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:p="http://primefaces.org/ui">
<h:head>
<title>Test page</title>
</h:head>
<h:body>
<h:form>
<p:dataTable id="itemTable" var="item" value="#{mrBean.items}" rows="5"
paginator="true" selection="#{mrBean.chosenItems}" lazy="true" >
<p:ajax event="rowSelectCheckbox" listener="mrBean.countItems" />
<p:column selectionMode="multiple" />
<p:column headerText="ID">
<h:outputText value="#{item.id}" />
</p:column>
<p:column headerText="Name">
<h:outputText value="#{item.name}" />
</p:column>
</p:dataTable>
</h:form>
</h:body>
</html>
如果你能告诉我这里我做错了什么,我将非常感激。
更新:我在上面的代码中添加了更多System.out.println("TEST")
后,我发现了以下内容:
TEST 1: getRowData
始终会在TEST 2: load
之前打印。因此,我认为方法#LazyDataModel.getWrappedData()
可能会返回旧页面中的数据。起初,我认为这个方法的目标是检索选定的行以突出显示在表上。但是,如果在load
之前调用此方法,那么它无法正常工作吗?TEST 3: chosenItems's size: 2
。如果我分页到第二页然后回到第一页,则选择将丢失,如上所述。但是,如果我继续在控制台上选择另一个项目,我会看到TEST 3: chosenItems's size: 3
。显然,chosenItems
列表仍然保留了我的旧选项,但它们没有呈现在桌面上。答案 0 :(得分:2)
在webPage中只需为页面切换添加一个事件:
<p:ajax event="page" listener="#{listingBean.updateSelected()}" />
在listingBean中,只需保存选中的内容:
private List<Entity> selectedInstances;
private List<Entity> selectedInstancesSaved;
public List<Entity> getSelectedInstances()
{
return selectedInstancesSaved;
}
public void setSelectedInstances(List<Entity> selectedInstances)
{
this.selectedInstances = selectedInstances;
}
public void updateSelected()
{
if (selectedInstances != null && !selectedInstances.isEmpty()) {
for (Entity inst : lazyModel.getDatasource()) {
if (selectedInstances.contains(inst)) {
selectedInstancesSaved.add( inst);
} else {
selectedInstancesSaved.remove( inst);
}
}
}
}
答案 1 :(得分:2)
虽然Bruno的解决方案适用于保留跨分页的选择,但它并不考虑保留单个页面上的选择(即从不更改页面时)。
除了拥有单独的“已保存”行列表外,还可以使用 rowSelectCheckbox 和 rowUnselectCheckbox ajax事件更简单地解决此问题。
<强> JSF:强>
<p:dataTable selection="#{myBean.selectedRows}" ... >
<p:ajax event="rowSelectCheckbox" process="@this" listener="#{myBean.onSelectRow}" />
<p:ajax event="rowUnselectCheckbox" process="@this" listener="#{myBean.onUnselectRow}" />
<p:column selectionMode="multiple" ... />
...
</p:dataTable>
支持Bean:
private List<MyRowClass> selectedRows;
private List<MyRowClass> selectedRowsSaved;
...
public void onSelectRow(SelectEvent event){
MyRowClass row = (MyRowClass) event.getObject();
selectedRowsSaved.add(row);
}
public void onUnselectRow(UnselectEvent event){
MyRowClass row = (MyRowClass) event.getObject();
selectedRowsSaved.remove(row);
}
public List<MyRowClass> getSelectedRows(){
return selectedRowsSaved;
}
public void setSelectedRows(List<MyRowClass> selectedRows){
this.selectedRows = selectedRows;
}
这样,保存的行列表始终保持最新,而不需要“页面”ajax事件。
答案 2 :(得分:1)
这是因为当解码SelectionFeature
时会创建一个新列表。
如果table.getRowData(rowKeys[i])
(与您的LazyDataModel
实现相关)返回null,则您在上一页中的旧选择已经消失。可能会尝试通过更改您的LazyDataModel实现来解决它我没有尝试这些但是请查看this和this
遇到同样的问题,如果您有很多不同的表实现LazyDataModel,我认为这个解决方案会更容易。
这就是我所做的:首先检查它是否是懒惰的,然后将当前选定的行添加到selectionList。
对于primefaces 4.0
1)覆盖DataTableRenderer
在faces-config.xml
中<render-kit>
<renderer>
<component-family>org.primefaces.component</component-family>
<renderer-type>org.primefaces.component.DataTableRenderer</renderer-type>
<renderer-class>com.package.LazyDataTableRenderer</renderer-class>
</renderer>
</render-kit>
并且
public class LazyDataTableRenderer extends DataTableRenderer {
static Map<DataTableFeatureKey,DataTableFeature> FEATURES;
static {
FEATURES = new HashMap<DataTableFeatureKey,DataTableFeature>();
FEATURES.put(DataTableFeatureKey.DRAGGABLE_COLUMNS, new DraggableColumnsFeature());
FEATURES.put(DataTableFeatureKey.FILTER, new FilterFeature());
FEATURES.put(DataTableFeatureKey.PAGE, new PageFeature());
FEATURES.put(DataTableFeatureKey.SORT, new SortFeature());
FEATURES.put(DataTableFeatureKey.RESIZABLE_COLUMNS, new ResizableColumnsFeature());
FEATURES.put(DataTableFeatureKey.SELECT, new LazySelectionFeature());
FEATURES.put(DataTableFeatureKey.ROW_EDIT, new RowEditFeature());
FEATURES.put(DataTableFeatureKey.CELL_EDIT, new CellEditFeature());
FEATURES.put(DataTableFeatureKey.ROW_EXPAND, new RowExpandFeature());
FEATURES.put(DataTableFeatureKey.SCROLL, new ScrollFeature());
}
@Override
public void decode(FacesContext context, UIComponent component) {
DataTable table = (DataTable) component;
for(Iterator<DataTableFeature> it = FEATURES.values().iterator(); it.hasNext();) {
DataTableFeature feature = it.next();
if(feature.shouldDecode(context, table)) {
feature.decode(context, table);
}
}
decodeBehaviors(context, component);
}
}
2)覆盖SelectionFeature的解码
已更新:已修改以允许取消选择
public class LazySelectionFeature extends org.primefaces.component.datatable.feature.SelectionFeature{
@Override
public void decode(FacesContext context, DataTable table) {
String clientId = table.getClientId(context);
Map<String,String> params = context.getExternalContext().getRequestParameterMap();
String selection = params.get(clientId + "_selection");
if(table.isSingleSelectionMode())
decodeSingleSelection(table, selection);
else
decodeMultipleSelection(context, table, selection);
}
void decodeSingleSelection(DataTable table, String selection) {
if(ComponentUtils.isValueBlank(selection))
table.setSelection(null);
else
table.setSelection(table.getRowData(selection));
}
void decodeMultipleSelection(FacesContext context, DataTable table, String selection) {
Class<?> clazz = table.getValueExpression("selection").getType(context.getELContext());
boolean isArray = clazz.isArray();
if(!isArray && !List.class.isAssignableFrom(clazz)) {
throw new FacesException("Multiple selection reference must be an Array or a List for datatable " + table.getClientId());
}
if(ComponentUtils.isValueBlank(selection)) {
if(isArray) {
table.setSelection(Array.newInstance(clazz.getComponentType(), 0));
}
else {
table.setSelection(new ArrayList<Object>());
}
}
else {
String[] rowKeys = selection.split(",");
List<Object> selectionList = new ArrayList<Object>();
boolean lazy=table.isLazy();
if (lazy) {
List<String> currentRowKeys = new ArrayList<String>(Arrays.asList(rowKeys));
if (table.getSelection() != null) {
List<Object> alreadySelected = (List<Object>) table.getSelection();
for (Object object : alreadySelected) {//For deselecting
Object rowKeyFromModel = table.getRowKeyFromModel(object);
if (currentRowKeys.contains(rowKeyFromModel)) {
selectionList.add(object);
currentRowKeys.remove(rowKeyFromModel);
}
}
}
for (String key : currentRowKeys) {//For selecting
Object rowData = table.getRowData(key);
if (rowData != null && !selectionList.contains(rowData)) {
selectionList.add(rowData);
}
}
}else{
for(int i = 0; i < rowKeys.length; i++) {
Object rowData = table.getRowData(rowKeys[i]);
if(rowData != null)
selectionList.add(rowData);
}
}
if(isArray) {
Object selectionArray = Array.newInstance(clazz.getComponentType(), selectionList.size());
table.setSelection(selectionList.toArray((Object[]) selectionArray));
}
else {
table.setSelection(selectionList);
}
}
}
}
可能不是最好的解决方案,但应该有效,请告诉我是否有更好的方法。希望这有助于某人。
答案 3 :(得分:1)
只需像这样实现绑定到DataTable(selection="#{pageBackingForm.selectedEntityList}"
)的selection属性的属性,它就可以工作:
private Map<Integer, List<Entity>> selectedEntityListMap = new Hashtable<>();
public List<Entity> getSelectedEntityList() {
return selectedEntityListMap.get(getCurrentEntitySelectionPage());
}
public void setSelectedEntityList(List<Entity> selectedEntityList) {
if (selectedEntityList == null) {
selectedEntityListMap.remove(getCurrentEntitySelectionPage());
return;
}
selectedEntityListMap.put(getCurrentEntitySelectionPage(), selectedEntityList);
}
public Integer getCurrentEntitySelectionPage() {
DataTable dataTable = (DataTable) FacesContext.getCurrentInstance().getViewRoot().findComponent("formId:dataTableId");
return dataTable.getPage();
}
答案 4 :(得分:-1)
我的数据表遇到了同样的问题。虽然我的情况有点不同,因为我使用的是 selectBooleanCheckbox 。我找到了一个适合我的简单解决方案。当你说“旧的选择未在表格中呈现”时,它会打击我。
代码:
<h:selectBooleanCheckbox value="#{batch.toPortfolio}">
<a4j:support event="onchange" />
</h:selectBooleanCheckbox>