datatable with lazyloading and rowexpansion gives wrong object when opening expansion

703 Views Asked by At

I'm using a normal datatable with lazyloading and rowexpansion with a nested table and I have problems by opening the rowexpansion after lazyloading. ScrollRows is setted to 150.

<ui:composition template="/ressources/basic.xhtml" xmlns="http://www.w3.org/1999/xhtml" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:p="http://primefaces.org/ui">
<ui:define name="title">#{label['template.tab1']}</ui:define>
<ui:define name="content">
    <h:form id="crateTbl">
        <p:dataTable  rowExpandMode="single" id="cTbl" var="crate" value="#{cratesView.lazyModel}" selectionMode="single" 
                      filteredValue="#{cratesView.filteredCrates}" selection="#{cratesView.selectedCrate}" 
                      lazy="true" liveScroll="true" scrollRows="150" 
                      widgetVar="cratesTbl" rowKey="#{crate.id}" scrollable="true" scrollHeight="550">

            <p:column style="width:16px" exportable="false">
                <p:rowToggler />
            </p:column>
            <p:column headerText="#{label['crate.col.grp']}" filterStyle="width:60px;" filterBy="#{crate.grp}" filterMatchMode="contains" width="60">
                <h:outputText value="#{crate.grp}"/>
            </p:column>

            ...additional columns of crate.
            <p:rowExpansion >
                <p:panelGrid columnClasses="label,value" >
                    <div class="remark-label"><b>#{label['crateview.toggle.remark']}</b>: <h:outputLabel value="#{crate.remark}"/></div>
                        <p:dataTable id='bTbl' var="bottle" value="#{cratesView.getBottles2Crate(crate)}"
                                     widgetVar="bottleTbl" rowKey="#{bottle.id}" scrollable="true" scrollHeight="300">

                            <p:column headerText="#{label['bottle.col.grp']}" width="50">
                                <h:outputText value="#{bottle.grp}"/>
                            </p:column>

                            ...additional columns of bottle
                        </p:dataTable>
                    </p:column>    
                    </p:row>
                </p:panelGrid>  
            </p:rowExpansion>
        </p:dataTable>
    </h:form>
</ui:define>  

and Bean:

@ManagedBean(name = "cratesView")
@ViewScoped
public class CrateView implements Serializable {

    //object definitions

    @PostConstruct
    public void init() {
        //init some objects
        lazyModel = new LazyCrateDatamodel(cratesList);
    }

    public LazyDataModel<Crate> getLazyModel() {
        return lazyModel;
    }

    public void setLazyModel(LazyCrateDatamodel lazyModel){
        this.lazyModel = lazyModel;
    }

    public void setSelectedCrate(final Crate c){
        this.selectedCrate = c;
    }

    public Crate getSelectedCrate() {
       return this.selectedCrate;
    }

    public List<Crate> getFilteredCrates() {
        return filteredCrates;
    }

    public void setFilteredCrates(List<Crate> filteredCrates) {
        this.filteredCrates = filteredCrates;
    }

    public List<Bottle> getBottles2Crate(final Crate c)
    {
        int grp = c.getGrp(); //
        return gProvider.getBottlesByCrate(calculated.getGrp(), 0);
    }
}

Everything runs fine on the firstpage, opening the expansion shows correct data in the table. After loading the next 150 rows and scrolling back to e.g. the first row and opening the expansion the data of a wrong crate is loaded. cratesView.getBottles2Crate(crate) is called with a crate-object shifted by n*150(n - numbers of loadings). The object of the rowtoggling-event is not the same of the datamodel. On the other side setSelectedCrate gets the right object. Do I have something misconfigurated or is there a hint in the docs using rowexpansion and lazyloading...

Thanks for some hints.

Edit: Correct some syntax.

1

There are 1 best solutions below

0
On

Sorry for the missing information:

Mojarra JSF Implementation 2.3.0 (20170310-1214)

Primeface 6.2

I tested it parallel with a minimal version:

<?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 lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:p="http://primefaces.org/ui">
    <h:head>
        <title>Lazy Test</title>
        <link rel="icon" href="imgs/favicon.ico" type="image/x-icon" />
    </h:head>
    <body>
        <h:form id="crateTbl">
            <p:dataTable  rowExpandMode="single" id="cTbl" var="crate" value="#{lazyView.model}" selectionMode="single"  selection="#{lazyView.selectedCrate}" 
                          lazy="true" liveScroll="true" scrollRows="150" 
                          widgetVar="cratesTbl" rowKey="#{crate.id}" scrollable="true" scrollHeight="550">
                <p:column style="width:16px" exportable="false">
                    <p:rowToggler />
                </p:column>
                <p:column headerText="Kistengrp" width="60">
                    <h:outputText value="#{crate.grp}"/>
                </p:column>
                <p:column headerText="Kistennamen" width="250">
                    <h:outputText value="#{crate.name}"/>
                </p:column>
                <p:column headerText="Flaschengrp" width="100">
                    <h:outputText value="#{crate.bgrp}"/>
                </p:column>
                <p:rowExpansion >
                    <p:panelGrid  columnClasses="label,value" >
                        <p:dataTable id='bTbl' var="bottle" value="#{lazyView.getBottlesByCrate(crate)}" widgetVar="bottleTbl" rowKey="#{bottle.id}" scrollable="true" scrollHeight="300">
                            <p:column headerText="Gruppe" width="50">
                                <h:outputText value="#{bottle.grp}"/>
                            </p:column>
                            <p:column headerText="Name" width="250">
                                <h:outputText value="#{bottle.name}"/>
                            </p:column>
                            <p:column headerText="Id" width="100">
                                <h:outputText value="#{bottle.id}"/>
                            </p:column>
                        </p:dataTable>
                    </p:panelGrid>
                </p:rowExpansion>
            </p:dataTable>
        </h:form>
    </body>
</html>

Classes: Testdata provider:

package de.test;

import java.util.ArrayList;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.faces.bean.ApplicationScoped;
import javax.faces.bean.ManagedBean;

@ManagedBean(name = "testservice")
@ApplicationScoped
public class AssortmentProvider {

private final static String[] NAMES = {"Uerige","Koelsch","Wasser","Saft","Limo","Cola","Kupfer","Messing"};
private final static int[] BGRPS = {1,12,15,20,24,25,26,30,100,112,155,200,201,242,250};
private final static int[] CGRPS = {1000,1020,1050,2000,2400,2500,2600,3000,1006,1120,1550,2050,2010,2420,2500};
private List<Crate> crates;
private List<Bottle> bottles;

@PostConstruct
public void init() {
    this.createBottles();
    this.createCrates();
}

public List<Crate> getCrates() { return crates; }
public List<Bottle> getBottles() { return bottles; }

public List createCrates() {
    crates = new ArrayList<>();
    for(int i=0;i<500;i++) {
        int n = (int) (Math.random() * 8);
        int g = (int) (Math.random() * 15);
        int b = (int) (Math.random() * 15);
        Crate c = new Crate(NAMES[n], i, CGRPS[g]);
        c.setBgrp(BGRPS[b]);
        crates.add(c);
    }
    return crates;
}

public List createBottles() {
    bottles = new ArrayList<>();
    for(int i=0;i<500;i++) {
        int n = (int) (Math.random() * 8);
        int b = (int) (Math.random() * 15);
        Bottle btl = new Bottle("B_" + NAMES[n], i, BGRPS[b]);
        bottles.add(btl);
    }
    return bottles;
  }
}

Entities Crate:

package de.test;

public class Crate extends Entity {

    private int bgrp;
    public Crate(String name, int id, int cgrp) { super(name,id,cgrp); }
    public int getBgrp() { return bgrp; }
    public void setBgrp(int bgrp) { this.bgrp = bgrp; }
    @Override
    public String toString() { return "Crate{" + "name=" + name + ", id=" + id + ", grp=" + grp + ", bgrp=" + bgrp +'}'; }

}

next Bottle:

package de.test;

public class Bottle extends Entity {

    public Bottle(String name, int id, int grp) { super(name,id,grp); }
    @Override
    public String toString() { return "Bottle{" + "name=" + name + ", id=" + id + ", grp=" + grp + '}'; }

}

next Entity:

package de.test;

public class Entity {
    protected String name;
    protected int id;
    protected int grp;

    public Entity(String name, int id, int grp) {
        this.name = name;
        this.id = id;
        this.grp = grp;
    }

    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public int getId() { return id; }
    public void setId(int id) { this.id = id; }
    public int getGrp() { return grp; }
    public void setGrp(int grp) { this.grp = grp; }

}

next LazyView:

package de.test;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;
import javax.faces.bean.ViewScoped;

@ManagedBean(name = "lazyView")
@ViewScoped
public class LazyView implements Serializable {

    @ManagedProperty("#{testservice}")
    AssortmentProvider service;
    List<Crate> crateList;
    Crate selectedCrate;
    LazyCrateModel model;

    @PostConstruct
    public void init() {
        crateList = this.service.createCrates();
        model = new LazyCrateModel(crateList);
    }

    public void setService(AssortmentProvider service) { this.service = service; }
    public Crate getSelectedCrate() { return selectedCrate; }
    public void setSelectedCrate(Crate selectedCrate) { this.selectedCrate = selectedCrate; }
    public List<Crate> getCrateList() { return crateList; }
    public void setCrateList(List<Crate> crateList) { this.crateList = crateList; }
    public LazyCrateModel getModel() { return model; }

    public List<Bottle> getBottlesByCrate(final Crate c) {
        System.out.println(c);
        List<Bottle> l = new ArrayList<>();
        for(Bottle b : service.getBottles())
        {
            if( b.getGrp() == c.getBgrp())
                l.add(b);
        }
        return l;
    }
}

next LazyDatamodel:

package de.test;

import java.util.List;
import java.util.Map;
import org.primefaces.model.LazyDataModel;
import org.primefaces.model.SortOrder;

public class LazyCrateModel extends LazyDataModel<Crate> {

    private final List<Crate> list;

    public LazyCrateModel(List<Crate> list) { this.list = list; }

    @Override
    public Integer getRowKey(Crate object) { return object.getId(); }

    @Override
    public List<Crate> load(int first, int pageSize, String sortField, SortOrder sortOrder, Map<String, Object> filters) {

        this.setRowCount(list.size());
        if(first + pageSize > list.size()) {
            pageSize = list.size() - first;
        }
        List<Crate> sub = list.subList(first, first + pageSize);
        return sub;
    }

    @Override
    public Crate getRowData(String str) {
        int idx = Integer.parseInt(str);
        for(Crate c : list) {
            if(c.getId() == idx)
                return c;
        }
        return null;
    }
}

It's the same behavior: After one loading scrolling to the beginning, the given crate is the 150 shifted crate, not crate[0]. Hope someone can verify this behavior.