I had a similar problem. Don't know why this is so complex to achieve in richfaces (and/or JSF).
In my app I wish I would could do something similar to gmail's interface, in the sense that the user can select one or many items (checkboxes) in different pages or select all the items in all pages.
I ended up coding a "MultiRowTableDataModel" that extends an "ExtendedDataModel" that internally manages a SelectionRow. Without this model I had similar problems like the one you mentioned.
Maybe someone knows a better and simpler solution.
Regards,
Rafael.
test.xhtml
...
<rich:dataTable id="dataTable"
var="item"
value="#{myHandler.dataModel}"
binding="#{myHandler.dataTable}"
>
<rich:column>
<f:facet name="header">
<h:selectBooleanCheckbox id="selectAllItemsChbk" value="#{myHandler.allRowsSelected}"
valueChangeListener="#{myHandler.selectAllRowsChanged}"
title="#{gui.label_click_select_all_items}"
>
<a4j:support event="onclick" action="#{myHandler.updateSelectedItems}"
onsubmit="toggleAll();"/>
</h:selectBooleanCheckbox>
</f:facet>
<h:selectBooleanCheckbox id="selectItemChbk" value="#{item.selected}">
<a4j:support event="onclick" action="#{myHandler.updateSelectedItems}"/>
</h:selectBooleanCheckbox>
</rich:column>
<rich:column>
...
</rich:column>
</rich:dataTable>
...
MyHandler.java
public class MyHandler extends MultiRowDataTableHandler {
private MyDataModel dataModel = new MyDataModel(); // MyDataModel extends MultiRowTableDataModel
public void selectAllRowsChanged(ValueChangeEvent e) {
boolean newVal = (Boolean) e.getNewValue();
selectAllRows(newVal);
}
....
}
MultiRowDataTableHandler.java
public class MultiRowDataTableHandler extends DataTableHandler {
private boolean allRowsSelected;
public MultiRowDataTableHandler() {
}
public MultiRowDataTableHandler(String binding) {
super(binding);
}
public MultiRowDataTableHandler(String binding, UIData dataTable) {
super(binding);
this.dataTable = dataTable;
}
public MultiRowTableDataModel getTableDataModel() {
if (dataTable != null) {
return (MultiRowTableDataModel) dataTable.getValue();
}
return null;
}
public void updateSelectedData() {
getTableDataModel().saveItemsSelection();
}
public List<?> getSelectedData() {
return getTableDataModel().getSelectedData();
}
protected List<Key> getSelectedKeys() {
return getTableDataModel().getSelectedKeys();
}
private List<SelectionRow> getRows() {
List<SelectionRow> rows = (List<SelectionRow>) getTableDataModel().getWrappedData();
return rows;
}
public void selectAllRows(boolean value) {
List<SelectionRow> rows = getRows();
Iterator<SelectionRow> it = rows != null ? rows.iterator() : null;
SelectionRow row;
if (it != null) { //optimizacion
while (it.hasNext()) {
row = it.next();
row.setSelected(value);
}
}
if (!value) {
getTableDataModel().resetItemsSelection();
}
}
public boolean isAllRowsSelected() {
return allRowsSelected;
}
public void setAllRowsSelected(boolean allRowsSelected) {
this.allRowsSelected = allRowsSelected;
}
}
DataTableHandler
public class DataTableHandler extends BaseHandler {
protected UIData dataTable;
public DataTableHandler() {
}
public DataTableHandler(String binding) {
super(binding);
}
public DataTableHandler(String binding, UIData dataTable) {
super(binding);
this.dataTable = dataTable;
}
public UIData getDataTable() {
return dataTable;
}
public void setDataTable(UIData dataTable) {
this.dataTable = dataTable;
}
public int getDataTableSelectedIndex() {
if (dataTable != null) {
return dataTable.getRowIndex();
} else {
return -1;
}
}
public Object getDataTableSelectedRow() {
return dataTable.getRowData();
}
}
MultiRowTableDataModel
public abstract class MultiRowTableDataModel<T extends Keyable<ID>, ID extends Key>
extends ExtendedDataModel {
//~ Instance fields --------------------------------------------------------
private ID currentId;
private List<ID> wrappedKeys;
private Map<ID, SelectionRow<T>> wrappedData = new HashMap<ID, SelectionRow<T>>();
/**
* lista of selected items
*/
private Set<ID> selectedKeys = new HashSet<ID>();
private Map<ID, SelectionRow<T>> selectedData = new HashMap<ID, SelectionRow<T>>();
private int rowIndex;
//~ Methods ----------------------------------------------------------------
public abstract int getDatasetSize();
public abstract List<T> getList(
final Integer firstRow,
final Integer maxResults
);
protected List<SelectionRow<T>> loadList(final Integer firstRow, final Integer maxResults) {
// load rows form datasource
LineContainerWrapper<T> lineContainerWrapper = new LineContainerWrapper<T>(getList(firstRow, maxResults));
return lineContainerWrapper.getRows();
}
public abstract T findById(final ID id);
protected SelectionRow findId(final ID id) {
T item = findById(id);
return new SelectionRow<T>(item);
}
public ID getId(final T row) {
return row.getKey();
}
public void wrap(
final FacesContext context,
final DataVisitor visitor,
final Range range,
final Object argument,
final List<SelectionRow<T>> list
) throws IOException {
wrappedKeys = new ArrayList<ID>();
wrappedData = new HashMap<ID, SelectionRow<T>>();
for (final SelectionRow<T> row : list) {
ID id = getId(row.getData());
restoreItemsSelection(id, row);
wrappedKeys.add(id);
wrappedData.put(id, row);
visitor.process(context, id, argument);
}
}
@Override
public void walk(
final FacesContext context,
final DataVisitor visitor,
final Range range,
final Object argument
)
throws IOException {
int firstRow = ((SequenceRange) range).getFirstRow();
int maxResults = ((SequenceRange) range).getRows();
wrap(context, visitor, range, argument, loadList(firstRow, maxResults));
}
/**
* updates the selected items
*/
public void saveItemsSelection() {
if (wrappedKeys != null) {
for (ID key : wrappedKeys) {
SelectionRow<T> row = wrappedData.get(key);
if (row.isSelected()) {
selectedKeys.add(key);
selectedData.put(key, row);
} else {
selectedKeys.remove(key);
selectedData.remove(key);
}
}
}
}
private void restoreItemsSelection(ID id, SelectionRow<T> row) {
if (selectedKeys.contains(id)) {
row.setSelected(true);
}
}
public void resetItemsSelection() {
selectedKeys = null;
selectedData = null;
selectedKeys = new HashSet<ID>();
selectedData = new HashMap<ID, SelectionRow<T>>();
}
public List<T> getSelectedData() {
List<SelectionRow<T>> rows = new ArrayList<SelectionRow<T>>(selectedData.values());
Iterator<SelectionRow<T>> it;
SelectionRow<T> selectionRow;
List<T> result = new ArrayList<T>();
it = rows.iterator();
while (it.hasNext()) {
selectionRow = it.next();
if (selectionRow.isSelected()) {
result.add(selectionRow.getData());
}
}
return result;
}
public List<ID> getSelectedKeys() {
return new ArrayList<ID>(selectedKeys);
}
/*
* This method normally called by Visitor before request Data Row.
*/
@Override
@SuppressWarnings("unchecked")
public void setRowKey(final Object key) {
this.currentId = (ID) key;
}
@Override
public Object getRowKey() {
return currentId;
}
@Override
public int getRowCount() {
return this.getDatasetSize();
}
@Override
public boolean isRowAvailable() {
return getRowData() != null;
}
/**
* This is main way to obtain data row. It is intensively used by framework.
* We strongly recommend use of local cache in that method.
*/
@Override
public Object getRowData() {
if (currentId == null) {
return null;
} else {
SelectionRow<T> ret = wrappedData.get(currentId);
if (ret == null) {
ret = this.findId(currentId);
wrappedData.put(currentId, ret);
return ret;
} else {
return ret;
}
}
}
// Unused rudiment from old JSF staff.
@Override
public void setRowIndex(int index) {
//throw new UnsupportedOperationException();
this.rowIndex = index;
}
@Override
public int getRowIndex() {
//throw new UnsupportedOperationException();
return this.rowIndex;
}
@Override
public Object getWrappedData() {
return new ArrayList(wrappedData.values());
}
@Override
public void setWrappedData(final Object data) {
throw new UnsupportedOperationException();
}
SelectionRow
public class SelectionRow<T> implements Serializable {
private T data;
private boolean selected = false;
public SelectionRow(T data) {
this.data = data;
}
public SelectionRow(T data, boolean selected) {
this.data = data;
this.selected = selected;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public boolean isSelected() {
return selected;
}
public void setSelected(boolean selected) {
this.selected = selected;
}
public boolean isRowSelected() {
return isSelected();
}
public void rowSelectedChanged(javax.faces.event.ValueChangeEvent valueChangeEvent) throws javax.faces.event.AbortProcessingException {
setSelected((Boolean) valueChangeEvent.getNewValue());
}
}