p: выборки dataTable теряются после разбиения на страницы LazyDataModel
Моя проблема в том, что после того, как я выбрал несколько элементов на 1-й странице, если я разбил страницу на другую страницу и вернулся, мои начальные выборы не отображаются. Я пытался реализовать SelectableDataModel
а также с использованием rowKey
атрибут, но проблема сохраняется.
Это мой тестовый боб:
@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
нет ли способа сделать работу правильно? - После того, как я выбрал первые 2 пункта на первой странице, на консоли я увидел
TEST 3: chosenItems's size: 2
, Если я перейду на 2-ю страницу, а затем вернусь на 1-ю страницу, выборы будут потеряны, как уже упоминалось. Однако, если я продолжил выбирать другой элемент, на консоли я увиделTEST 3: chosenItems's size: 3
, Очевидно, чтоchosenItems
В списке все еще хранятся мои старые выборки, но они не отображаются на столе.
5 ответов
Это потому что когда SelectionFeature
расшифрован новый список создан.
И если table.getRowData(rowKeys[i])
(связано с вашим LazyDataModel
реализация) возвращает null, ваши старые варианты выбора на предыдущей странице пропали. можете попытаться решить эту проблему, изменив вашу реализацию LazyDataModel. Я не пробовал этого, но взглянул на это и это
У меня была та же проблема, и я думаю, что это решение проще, если у вас есть много разных таблиц, реализующих LazyDataModel.
Это то, что я сделал: сначала проверьте, не ленивый ли он, затем добавьте выбранные строки в selectionList.
Для простых лиц 4.0
1) Переопределить DataTableRenderer
В лицах-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);
}
}
}
}
Может быть не лучшим решением, но должно работать, дайте мне знать, если есть лучший способ. Надеюсь, это кому-нибудь поможет.
В webPage просто добавьте событие, когда страница переключается:
<p:ajax event="page" listener="#{listingBean.updateSelected()}" />
В листинге Bean просто сохраните выбранное:
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);
}
}
}
}
Хотя решение Bruno работает для сохранения выбора между нумерацией страниц, оно не учитывает сохранение выбора на отдельной странице (то есть, когда никогда не меняются страницы).
Эту проблему можно решить проще, используя события ajax rowSelectCheckbox и rowUnselectCheckbox, в дополнение к наличию отдельного "сохраненного" списка строк.
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>
Бэк бин:
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-события "page".
Просто реализуйте свойство, привязанное к свойству выбора объекта DataTable (selection="#{pageBackingForm.selectedEntityList}"
) вот так и будет работать
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();
}
У меня была такая же проблема с моей таблицей данных. Хотя мой случай немного отличается, потому что я использую вместо этого selectBooleanCheckbox. Я нашел простое решение, которое работает для меня. Меня поразило, когда вы сказали, что "старые выборки не отображаются в таблице".
- привязать флажок к событию поддержки a4j:
код:
<h:selectBooleanCheckbox value="#{batch.toPortfolio}">
<a4j:support event="onchange" />
</h:selectBooleanCheckbox>