My problem is that after I\'ve selected a few items on the 1st page, if I paginate to another page and come back, my initial selections are not shown. I\'ve tried to impleme
While Bruno's solution works for keeping selections across paginations, it doesn't account for retaining selections on an individual page (i.e. when never changing pages).
This problem can be resolved more simply by using the rowSelectCheckbox and rowUnselectCheckbox ajax events, in addition to having a separate "saved" row list.
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>
Backing 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;
}
This way the list of saved rows is always kept up to date without needing a "page" ajax event.
In webPage just add a event for when page switch:
<p:ajax event="page" listener="#{listingBean.updateSelected()}" />
In the listingBean, just save the selected:
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);
}
}
}
}
I had the same problem with my data table. Although my case is a bit different because I am using selectBooleanCheckbox instead. I found a simple solution that works for me. It hit me when you said "old selection are not rendered in the table".
code:
<h:selectBooleanCheckbox value="#{batch.toPortfolio}">
<a4j:support event="onchange" />
</h:selectBooleanCheckbox>
This is because when SelectionFeature
is decoded a new list is created.
And if table.getRowData(rowKeys[i])
(related to your LazyDataModel
implementation) returns null your old selectıons in the previous page are gone.may try to solve it by changing your LazyDataModel implementation I didn't try these but take a look at this and this
Had the same problem and I think this solution is easier if you have a lot of different tables implementing LazyDataModel.
This is what I did: check if it is lazy first then add currently selected rows to the selectionList.
For primefaces 4.0
1)Override DataTableRenderer
In 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>
And
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)Override SelectionFeature's decode
Updated: edited to allow deselecting
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);
}
}
}
}
Might not be the best solution but should work, let me know if there is a better way. Hope this helps someone.
Just implement the property bound to selection property of DataTable (selection="#{pageBackingForm.selectedEntityList}"
) like this and it will work :
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();
}