实时滚动后,PrimeFaces LazyDataModel是否错误地解析了行?

时间:2015-11-22 16:29:55

标签: jsf jsf-2 primefaces datatable el

我正在尝试使用PrimeFaces 5.3(使用JSF 2.2)来实现包含项目的数据表,并且每个项目都有一个命令按钮列表来执行服务器端操作。我使用primefaces LazyDataModel,因为我有庞大的数据集,并且无法在加载视图时加载所有项目。但是在实际进行实时滚动之后,行的行为并不像预期的那样。

我已经设置了一个小项目来说明我的问题。 (帖子末尾的完整资料来源)

Screenshot

问题

按A0时,必须在控制台上打印“已执行A0”(依此类推)。

第一个数据集的一切正常。这是按下前三个按钮时发生的情况:

Fetching starting at 0       //expected result  
Executed A0                  //A0
Executed A1                  //A1
Executed A2                  //A2

但是当我滚动并加载了其他3个项目时,前三行无法正常工作。

After scrolling

按顺序按下所有按钮时会发生以下情况:

Fetching starting at 3    //expected result
Executed A3               //A0
Executed A4               //A1
Executed A5               //A2
Executed A3               //A3
Executed A4               //A4
Executed A5               //A5

前三个按钮执行最后三个按钮的操作:(

服务器端问题

这不是客户端问题,我已经监控了发送的请求。

当我按A0时,请求包含以下POST参数:

j_idt8:resultsTable:0:j_idt12:0:j_idt13=j_idt8:resultsTable:0:j_idt12:0:j_idt13
javax.faces.source=j_idt8:resultsTable:0:j_idt12:0:j_idt13

当我按下A3时:

j_idt8:resultsTable:3:j_idt12:0:j_idt13=j_idt8:resultsTable:3:j_idt12:0:j_idt13
javax.faces.source=j_idt8:resultsTable:3:j_idt12:0:j_idt13

在resultsTable:

之后引用了正确的行

调试模式

我也搞乱了调试模式。当我按A1时,将使用包含字段rowIndex = 1的事件调用UIData #brodacast(FacesEvent)。所以它在这一点上运作良好。

然后,我真的不明白会发生什么。但是在进程结束时,目标方法在错误的实例上执行......

源代码

Item.java

public class Item {
    private int id;
    private String name;
    private List<ItemAction> actions;
    public Item(int id, String name, List<ItemAction> actions) {
        this.id = id;
        this.name = name;
        this.actions = actions;
    }
    public int getId() {
        return id;
    }
    public String getName() {
        return name;
    }
    public List<ItemAction> getActions() {
        return actions;
    }
}

ItemAction.java

public class ItemAction {
    private String label;
    public ItemAction(String label) {
        this.label = label;
    }
    public void execute(){
        System.out.println("Executed " + label);
    }
    public String getLabel() {
        return label;
    }
}

TheService.java

public class TheService {
    private static final int ITEMS_COUNT = 8;
    public int getItemsCount() {
        return ITEMS_COUNT;
    }
    public List<Item> retrieveItems(int startIndex, int pageSize){
        List<Item> result = new ArrayList<>(pageSize);
        int endIndex = startIndex + pageSize;
        if(endIndex > ITEMS_COUNT){
            endIndex = ITEMS_COUNT;
        }
        for(int index=startIndex; index<endIndex; index++){
            result.add(generateItem(index));
        }
        return result;
    }
    private Item generateItem(int id){
        List<ItemAction> actions = new ArrayList<>(2);
        actions.add(new ItemAction("A" + id));
        actions.add(new ItemAction("B" + id));
        return new Item(id, "Item " + id, actions);
    }
}

DatatableLazyModel.java

@ManagedBean
@ViewScoped
public class DatatableLazyModel extends LazyDataModel<Item>{
    private TheService service;
    private List<Item> allResults;
    public DatatableLazyModel() {
        this.service = new TheService();
        this.allResults = new ArrayList<>();
    }
    @Override
    public int getRowCount() {
        return service.getItemsCount();
    }
    @Override
    public List<Item> load(int first, int pageSize, String sortField, SortOrder sortOrder,
            Map<String, Object> filters) {
        System.out.println("Fetching starting at " + first);
        List<Item> pageResults = service.retrieveItems(first, pageSize);
        allResults.addAll(pageResults);
        return pageResults;
    }
}

的index.xhtml

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
    xmlns:ui="http://java.sun.com/jsf/facelets"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:p="http://primefaces.org/ui" template="template/ui.xhtml">
    <ui:define name="body">
        <h3>Datatablelazy load</h3>

        <h:form>
            <p:dataTable id="resultsTable" var="item"
                value="#{datatableLazyModel}" liveScroll="true" scrollRows="3"
                scrollHeight="100" scrollable="true" lazy="true">

                <p:column headerText="Name">
                    <h:outputText value="#{item.name}" />
                </p:column>

                <p:column headerText="Actions">
                    <ui:repeat var="action" value="#{item.actions}">
                        <p:commandButton action="#{action.execute()}" value="#{action.label}">
                        </p:commandButton>
                    </ui:repeat>
                </p:column>
            </p:dataTable>
        </h:form>
    </ui:define>
</ui:composition>

pom.xml依赖项

<dependency>
        <groupId>com.sun.faces</groupId>
        <artifactId>jsf-api</artifactId>
        <version>2.2.4</version>
    </dependency>
    <dependency>
        <groupId>com.sun.faces</groupId>
        <artifactId>jsf-impl</artifactId>
        <version>2.2.4</version>
    </dependency>
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>3.0.1</version>
    </dependency>

        <dependency>  
        <groupId>org.primefaces</groupId>  
        <artifactId>primefaces</artifactId>  
        <version>5.3</version>  
    </dependency>  

问题

我已尝试加载更多项目。每次只执行最后加载的集合上的方法。即使是旧组件。

基本上,问题是:“如何让所有按钮按预期工作?”

为什么方法不能在正确的实例上执行?目标实例是如何解决的?

感谢您的帮助:)

1 个答案:

答案 0 :(得分:2)

问题在于LazyDataModel实现是否适用于页面而非实时滚动,尤其是此片段:

if(rowIndex == -1 || pageSize == 0)
        this.rowIndex = -1;
    else
        this.rowIndex = (rowIndex % pageSize);

我使用自己的实现将LazyDataModel子类化,以解决问题。

<强> LiveScrollLazyDataModel.java

public abstract class LiveScrollLazyModel<T> extends LazyDataModel<T> {

    private int rowIndex;

    @Override
    public void setRowIndex(int rowIndex) {
        int oldIndex = getRowIndex();

        if (rowIndex == -1 || getPageSize() == 0){
            super.setRowIndex(-1);
            this.rowIndex = -1;
        }else{
            super.setRowIndex(rowIndex);
            this.rowIndex = rowIndex;
        }

        if (getResults() == null) {
            return;
        }

        DataModelListener[] listeners = getDataModelListeners();
        if (listeners != null && oldIndex != this.rowIndex) {
            Object rowData = null;
            if (isRowAvailable()) {
                rowData = getRowData();
            }

            DataModelEvent dataModelEvent = new DataModelEvent(this, rowIndex, rowData);
            for (int i = 0; i < listeners.length; i++) {
                listeners[i].rowSelected(dataModelEvent);
            }
        }
    }

    @Override
    public T getRowData() {
        return getResults().get(this.rowIndex);
    }

    @Override
    public boolean isRowAvailable() {
        if(getResults() == null) {
            return false;
        }

        return rowIndex >= 0 && rowIndex < getResults().size();
    }

    /**
     * The complete list of results
     * @return
     */
    public abstract List<T> getResults();

}

实现者需要实现getResults()方法,该方法提供完整的结果列表。