I'm having an issue with lazy loading a Primefaces Datascroller component.
I have a jsf page that should display 10 events on page load. If the user wants to see more he/she can click the more button to load and display the 10 next events. For each of the event rows, there is a link that can be used to display the event's details.
<h:form id="mainForm" >
<p:dataScroller value="#{backing.lazyModel}" var="event" lazy="true" chunkSize="10" rowIndexVar="index">
#{event.name}
<p:commandLink class="view-trigger"
value="View Event Details"
actionListener="#{backing.initViewEventDetails(index, event)}"/>
<f:facet name="loader">
<p:outputPanel
visible="#{backing.lazyModel.rowCount gt 10}"
rendered="#{backing.lazyModel.rowCount gt 10}">
<p:commandLink value="More" />
</p:outputPanel>
</f:facet>
</p:dataScroller>
</h:form>
The initial search works fine, that is, when I click the view event details link, my backing bean is invoked and I see that the index and event received correspond to the row I clicked on.
However, once I load the next chunk, which consists of 1 extra event, the page displays 11 events but clicking a view event details link sends the proper index but does not send the proper event. For example, if I click on event at index 0, I get the event at index 10, if I click on event at index 1 my backing bean is not invoked.
It looks like the datascroller forgets about the last 10 events when I click on the more button but my lazy data model still remembers.
The backing bean:
@ManagedBean(name="backing")
@ViewScoped
public class DataScrollerBacking implements Serializable {
private static final long serialVersionUID = 4012320411042043677L;
private static final Logger LOGGER = Logger.getLogger(DataScrollerBacking.class);
@ManagedProperty("#{settings.dataSource}")
private String dataSource;
private WebEventDAO webEventDAO;
private LazyDataModel<Event> lazyModel;
@PostConstruct
public void init() {
webEventDAO = CommonDAOFactory.getInstance(dataSource).getWebEventDAO();
search();
}
public void search() {
DateTime start = new DateTime(2014, 1, 1, 0, 0 ,0);
final Date startDate = start.toDate();
final Date endDate = start.plus(Years.ONE.toPeriod()).minus(Seconds.ONE.toPeriod()).toDate();
lazyModel = new LazyDataModel<Event>() {
private static final long serialVersionUID = 1231902031619933635L;
private LinkedHashSet<Event> eventCache; // Ordered set of all retrieved events so far.
// I'm using a set because the load method is called twice on page load (any idea why???) and I don't want duplicates in my cache.
@Override
public List<Event> load(int first, int pageSize, String sortField, SortOrder sortOrder, Map<String, Object> filters) {
List<Event> events = new ArrayList<Event>(10);
try {
if(eventCache == null){
int count = webEventDAO.getSearchByPeriodRaceTypeAndRaceStatusForCompanyCount(Collections.singletonList(1), startDate, endDate, null, null);
this.setRowCount(count);
eventCache = new LinkedHashSet<Event>(count);
}
events = webEventDAO.searchByPeriodRaceTypeAndRaceStatusForCompany(Collections.singletonList(1), startDate, endDate, null, null, true, first, pageSize);
eventCache.addAll(events);
} catch (DAOException e) {
LOGGER.error("An error occurred while retrieving events.", e);
}
return events;
}
};
}
public void initViewEventDetails(Integer index, Event event){
LOGGER.info("index=" + index + " eventname=" + event.getName());
}
public String getDataSource() {
return dataSource;
}
public void setDataSource(String dataSource) {
this.dataSource = dataSource;
}
public LazyDataModel<Event> getLazyModel() {
return lazyModel;
}
public void setLazyModel(LazyDataModel<Event> lazyModel) {
this.lazyModel = lazyModel;
}}
Since the page displays the proper information and the index received is always valid, my current workaround is to go fetch the Event in the lazy data model by index.
However, I would like to understand why the received event is not the one I clicked on.
Am I doing something wrong or this is just how the scroller is implemented?
Running on Mojarra 2.2, Tomcat 7, Primefaces 5, Omnifaces 1.8